1 /** strptime workaround (for oa macos leopard) 2 * This strptime follows the man strptime (2001-11-12) 3 * conforming to SUSv2, POSIX.1-2001 4 * 5 * This very simple version of strptime has no: 6 * - E alternatives 7 * - O alternatives 8 * - Glibc additions 9 * - Does not process week numbers 10 * - Does not properly processes year day 11 * 12 * LICENSE 13 * Copyright (c) 2008, NLnet Labs, Matthijs Mekking 14 * All rights reserved. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions are met: 18 * * Redistributions of source code must retain the above copyright notice, 19 * this list of conditions and the following disclaimer. 20 * * Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * * Neither the name of NLnetLabs nor the names of its 24 * contributors may be used to endorse or promote products derived from this 25 * software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 **/ 39 40 #include "config.h" 41 42 #ifndef HAVE_CONFIG_H 43 #include <time.h> 44 #endif 45 46 #ifndef STRPTIME_WORKS 47 48 #define TM_YEAR_BASE 1900 49 50 #include <ctype.h> 51 #include <string.h> 52 53 static const char *abb_weekdays[] = { 54 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 55 }; 56 static const char *full_weekdays[] = { 57 "Sunday", "Monday", "Tuesday", "Wednesday", 58 "Thursday", "Friday", "Saturday", NULL 59 }; 60 static const char *abb_months[] = { 61 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 62 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL 63 }; 64 static const char *full_months[] = { 65 "January", "February", "March", "April", "May", "June", 66 "July", "August", "September", "October", "November", "December", NULL 67 }; 68 static const char *ampm[] = { 69 "am", "pm", NULL 70 }; 71 72 static int 73 match_string(const char **buf, const char **strs) 74 { 75 int i = 0; 76 77 for (i = 0; strs[i] != NULL; i++) { 78 int len = strlen(strs[i]); 79 if (strncasecmp (*buf, strs[i], len) == 0) { 80 *buf += len; 81 return i; 82 } 83 } 84 return -1; 85 } 86 87 static int 88 str2int(const char **buf, int max) 89 { 90 int ret=0, count=0; 91 92 while (*buf[0] != '\0' && isdigit((unsigned char)*buf[0]) && count<max) { 93 ret = ret*10 + (*buf[0] - '0'); 94 (*buf)++; 95 count++; 96 } 97 98 if (!count) 99 return -1; 100 return ret; 101 } 102 103 /** Converts the character string s to values which are stored in tm 104 * using the format specified by format 105 **/ 106 char * 107 unbound_strptime(const char *s, const char *format, struct tm *tm) 108 { 109 int c, ret; 110 int split_year = 0; 111 112 while ((c = *format) != '\0') { 113 /* whitespace, literal or format */ 114 if (isspace((unsigned char)c)) { /* whitespace */ 115 /** whitespace matches zero or more whitespace characters in the 116 * input string. 117 **/ 118 while (isspace((unsigned char)*s)) 119 s++; 120 } 121 else if (c == '%') { /* format */ 122 format++; 123 c = *format; 124 switch (c) { 125 case '%': /* %% is converted to % */ 126 if (*s != c) { 127 return NULL; 128 } 129 s++; 130 break; 131 case 'a': /* weekday name, abbreviated or full */ 132 case 'A': 133 ret = match_string(&s, full_weekdays); 134 if (ret < 0) 135 ret = match_string(&s, abb_weekdays); 136 if (ret < 0) { 137 return NULL; 138 } 139 tm->tm_wday = ret; 140 break; 141 case 'b': /* month name, abbreviated or full */ 142 case 'B': 143 case 'h': 144 ret = match_string(&s, full_months); 145 if (ret < 0) 146 ret = match_string(&s, abb_months); 147 if (ret < 0) { 148 return NULL; 149 } 150 tm->tm_mon = ret; 151 break; 152 case 'c': /* date and time representation */ 153 if (!(s = unbound_strptime(s, "%x %X", tm))) { 154 return NULL; 155 } 156 break; 157 case 'C': /* century number */ 158 ret = str2int(&s, 2); 159 if (ret < 0 || ret > 99) { /* must be in [00,99] */ 160 return NULL; 161 } 162 163 if (split_year) { 164 tm->tm_year = ret*100 + (tm->tm_year%100); 165 } 166 else { 167 tm->tm_year = ret*100 - TM_YEAR_BASE; 168 split_year = 1; 169 } 170 break; 171 case 'd': /* day of month */ 172 case 'e': 173 ret = str2int(&s, 2); 174 if (ret < 1 || ret > 31) { /* must be in [01,31] */ 175 return NULL; 176 } 177 tm->tm_mday = ret; 178 break; 179 case 'D': /* equivalent to %m/%d/%y */ 180 if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) { 181 return NULL; 182 } 183 break; 184 case 'H': /* hour */ 185 ret = str2int(&s, 2); 186 if (ret < 0 || ret > 23) { /* must be in [00,23] */ 187 return NULL; 188 } 189 tm->tm_hour = ret; 190 break; 191 case 'I': /* 12hr clock hour */ 192 ret = str2int(&s, 2); 193 if (ret < 1 || ret > 12) { /* must be in [01,12] */ 194 return NULL; 195 } 196 if (ret == 12) /* actually [0,11] */ 197 ret = 0; 198 tm->tm_hour = ret; 199 break; 200 case 'j': /* day of year */ 201 ret = str2int(&s, 2); 202 if (ret < 1 || ret > 366) { /* must be in [001,366] */ 203 return NULL; 204 } 205 tm->tm_yday = ret; 206 break; 207 case 'm': /* month */ 208 ret = str2int(&s, 2); 209 if (ret < 1 || ret > 12) { /* must be in [01,12] */ 210 return NULL; 211 } 212 /* months go from 0-11 */ 213 tm->tm_mon = (ret-1); 214 break; 215 case 'M': /* minute */ 216 ret = str2int(&s, 2); 217 if (ret < 0 || ret > 59) { /* must be in [00,59] */ 218 return NULL; 219 } 220 tm->tm_min = ret; 221 break; 222 case 'n': /* arbitrary whitespace */ 223 case 't': 224 while (isspace((unsigned char)*s)) 225 s++; 226 break; 227 case 'p': /* am pm */ 228 ret = match_string(&s, ampm); 229 if (ret < 0) { 230 return NULL; 231 } 232 if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */ 233 return NULL; 234 } 235 236 if (ret == 1) /* pm */ 237 tm->tm_hour += 12; 238 break; 239 case 'r': /* equivalent of %I:%M:%S %p */ 240 if (!(s = unbound_strptime(s, "%I:%M:%S %p", tm))) { 241 return NULL; 242 } 243 break; 244 case 'R': /* equivalent of %H:%M */ 245 if (!(s = unbound_strptime(s, "%H:%M", tm))) { 246 return NULL; 247 } 248 break; 249 case 'S': /* seconds */ 250 ret = str2int(&s, 2); 251 /* 60 may occur for leap seconds */ 252 /* earlier 61 was also allowed */ 253 if (ret < 0 || ret > 60) { /* must be in [00,60] */ 254 return NULL; 255 } 256 tm->tm_sec = ret; 257 break; 258 case 'T': /* equivalent of %H:%M:%S */ 259 if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) { 260 return NULL; 261 } 262 break; 263 case 'U': /* week number, with the first Sun of Jan being w1 */ 264 ret = str2int(&s, 2); 265 if (ret < 0 || ret > 53) { /* must be in [00,53] */ 266 return NULL; 267 } 268 /** it is hard (and not necessary for nsd) to determine time 269 * data from week number. 270 **/ 271 break; 272 case 'w': /* day of week */ 273 ret = str2int(&s, 1); 274 if (ret < 0 || ret > 6) { /* must be in [0,6] */ 275 return NULL; 276 } 277 tm->tm_wday = ret; 278 break; 279 case 'W': /* week number, with the first Mon of Jan being w1 */ 280 ret = str2int(&s, 2); 281 if (ret < 0 || ret > 53) { /* must be in [00,53] */ 282 return NULL; 283 } 284 /** it is hard (and not necessary for nsd) to determine time 285 * data from week number. 286 **/ 287 break; 288 case 'x': /* date format */ 289 if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) { 290 return NULL; 291 } 292 break; 293 case 'X': /* time format */ 294 if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) { 295 return NULL; 296 } 297 break; 298 case 'y': /* last two digits of a year */ 299 ret = str2int(&s, 2); 300 if (ret < 0 || ret > 99) { /* must be in [00,99] */ 301 return NULL; 302 } 303 if (split_year) { 304 tm->tm_year = ((tm->tm_year/100) * 100) + ret; 305 } 306 else { 307 split_year = 1; 308 309 /** currently: 310 * if in [0,68] we are in 21th century, 311 * if in [69,99] we are in 20th century. 312 **/ 313 if (ret < 69) /* 2000 */ 314 ret += 100; 315 tm->tm_year = ret; 316 } 317 break; 318 case 'Y': /* year */ 319 ret = str2int(&s, 4); 320 if (ret < 0 || ret > 9999) { 321 return NULL; 322 } 323 tm->tm_year = ret - TM_YEAR_BASE; 324 break; 325 case '\0': 326 default: /* unsupported, cannot match format */ 327 return NULL; 328 break; 329 } 330 } 331 else { /* literal */ 332 /* if input cannot match format, return NULL */ 333 if (*s != c) 334 return NULL; 335 s++; 336 } 337 338 format++; 339 } 340 341 /* return pointer to remainder of s */ 342 return (char*) s; 343 } 344 345 #endif /* STRPTIME_WORKS */ 346