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 #ifdef HAVE_CONFIG_H 41 #include "config.h" 42 #endif 43 44 #ifndef HAVE_CONFIG_H 45 #include <time.h> 46 #endif 47 48 #ifndef STRPTIME_WORKS 49 50 #define TM_YEAR_BASE 1900 51 52 #include <ctype.h> 53 #include <string.h> 54 55 static const char *abb_weekdays[] = { 56 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 57 }; 58 static const char *full_weekdays[] = { 59 "Sunday", "Monday", "Tuesday", "Wednesday", 60 "Thursday", "Friday", "Saturday", NULL 61 }; 62 static const char *abb_months[] = { 63 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 64 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL 65 }; 66 static const char *full_months[] = { 67 "January", "February", "March", "April", "May", "June", 68 "July", "August", "September", "October", "November", "December", NULL 69 }; 70 static const char *ampm[] = { 71 "am", "pm", NULL 72 }; 73 74 static int 75 match_string(const char **buf, const char **strs) 76 { 77 int i = 0; 78 79 for (i = 0; strs[i] != NULL; i++) { 80 int len = strlen(strs[i]); 81 if (strncasecmp (*buf, strs[i], len) == 0) { 82 *buf += len; 83 return i; 84 } 85 } 86 return -1; 87 } 88 89 static int 90 str2int(const char **buf, int max) 91 { 92 int ret=0, count=0; 93 94 while (*buf[0] != '\0' && isdigit((unsigned char)*buf[0]) && count<max) { 95 ret = ret*10 + (*buf[0] - '0'); 96 (*buf)++; 97 count++; 98 } 99 100 if (!count) 101 return -1; 102 return ret; 103 } 104 105 /** Converts the character string s to values which are stored in tm 106 * using the format specified by format 107 **/ 108 char * 109 nsd_strptime(const char *s, const char *format, struct tm *tm) 110 { 111 int c, alt_format, ret; 112 int split_year = 0; 113 114 while ((c = *format) != '\0') { 115 alt_format = 0; 116 117 /* whitespace, literal or format */ 118 if (isspace((unsigned char)c)) { /* whitespace */ 119 /** whitespace matches zero or more whitespace characters in the 120 * input string. 121 **/ 122 while (isspace((unsigned char)*s)) 123 s++; 124 } 125 else if (c == '%') { /* format */ 126 format++; 127 c = *format; 128 switch (c) { 129 case '%': /* %% is converted to % */ 130 if (*s != c) { 131 return NULL; 132 } 133 s++; 134 break; 135 case 'a': /* weekday name, abbreviated or full */ 136 case 'A': 137 ret = match_string(&s, full_weekdays); 138 if (ret < 0) 139 ret = match_string(&s, abb_weekdays); 140 if (ret < 0) { 141 return NULL; 142 } 143 tm->tm_wday = ret; 144 break; 145 case 'b': /* month name, abbreviated or full */ 146 case 'B': 147 case 'h': 148 ret = match_string(&s, full_months); 149 if (ret < 0) 150 ret = match_string(&s, abb_months); 151 if (ret < 0) { 152 return NULL; 153 } 154 tm->tm_mon = ret; 155 break; 156 case 'c': /* date and time representation */ 157 if (!(s = nsd_strptime(s, "%x %X", tm))) { 158 return NULL; 159 } 160 break; 161 case 'C': /* century number */ 162 ret = str2int(&s, 2); 163 if (ret < 0 || ret > 99) { /* must be in [00,99] */ 164 return NULL; 165 } 166 167 if (split_year) { 168 tm->tm_year = ret*100 + (tm->tm_year%100); 169 } 170 else { 171 tm->tm_year = ret*100 - TM_YEAR_BASE; 172 split_year = 1; 173 } 174 break; 175 case 'd': /* day of month */ 176 case 'e': 177 ret = str2int(&s, 2); 178 if (ret < 1 || ret > 31) { /* must be in [01,31] */ 179 return NULL; 180 } 181 tm->tm_mday = ret; 182 break; 183 case 'D': /* equivalent to %m/%d/%y */ 184 if (!(s = nsd_strptime(s, "%m/%d/%y", tm))) { 185 return NULL; 186 } 187 break; 188 case 'H': /* hour */ 189 ret = str2int(&s, 2); 190 if (ret < 0 || ret > 23) { /* must be in [00,23] */ 191 return NULL; 192 } 193 tm->tm_hour = ret; 194 break; 195 case 'I': /* 12hr clock hour */ 196 ret = str2int(&s, 2); 197 if (ret < 1 || ret > 12) { /* must be in [01,12] */ 198 return NULL; 199 } 200 if (ret == 12) /* actually [0,11] */ 201 ret = 0; 202 tm->tm_hour = ret; 203 break; 204 case 'j': /* day of year */ 205 ret = str2int(&s, 2); 206 if (ret < 1 || ret > 366) { /* must be in [001,366] */ 207 return NULL; 208 } 209 tm->tm_yday = ret; 210 break; 211 case 'm': /* month */ 212 ret = str2int(&s, 2); 213 if (ret < 1 || ret > 12) { /* must be in [01,12] */ 214 return NULL; 215 } 216 /* months go from 0-11 */ 217 tm->tm_mon = (ret-1); 218 break; 219 case 'M': /* minute */ 220 ret = str2int(&s, 2); 221 if (ret < 0 || ret > 59) { /* must be in [00,59] */ 222 return NULL; 223 } 224 tm->tm_min = ret; 225 break; 226 case 'n': /* arbitrary whitespace */ 227 case 't': 228 while (isspace((unsigned char)*s)) 229 s++; 230 break; 231 case 'p': /* am pm */ 232 ret = match_string(&s, ampm); 233 if (ret < 0) { 234 return NULL; 235 } 236 if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */ 237 return NULL; 238 } 239 240 if (ret == 1) /* pm */ 241 tm->tm_hour += 12; 242 break; 243 case 'r': /* equivalent of %I:%M:%S %p */ 244 if (!(s = nsd_strptime(s, "%I:%M:%S %p", tm))) { 245 return NULL; 246 } 247 break; 248 case 'R': /* equivalent of %H:%M */ 249 if (!(s = nsd_strptime(s, "%H:%M", tm))) { 250 return NULL; 251 } 252 break; 253 case 'S': /* seconds */ 254 ret = str2int(&s, 2); 255 /* 60 may occur for leap seconds */ 256 /* earlier 61 was also allowed */ 257 if (ret < 0 || ret > 60) { /* must be in [00,60] */ 258 return NULL; 259 } 260 tm->tm_sec = ret; 261 break; 262 case 'T': /* equivalent of %H:%M:%S */ 263 if (!(s = nsd_strptime(s, "%H:%M:%S", tm))) { 264 return NULL; 265 } 266 break; 267 case 'U': /* week number, with the first Sun of Jan being w1 */ 268 ret = str2int(&s, 2); 269 if (ret < 0 || ret > 53) { /* must be in [00,53] */ 270 return NULL; 271 } 272 /** it is hard (and not necessary for nsd) to determine time 273 * data from week number. 274 **/ 275 break; 276 case 'w': /* day of week */ 277 ret = str2int(&s, 1); 278 if (ret < 0 || ret > 6) { /* must be in [0,6] */ 279 return NULL; 280 } 281 tm->tm_wday = ret; 282 break; 283 case 'W': /* week number, with the first Mon of Jan being w1 */ 284 ret = str2int(&s, 2); 285 if (ret < 0 || ret > 53) { /* must be in [00,53] */ 286 return NULL; 287 } 288 /** it is hard (and not necessary for nsd) to determine time 289 * data from week number. 290 **/ 291 break; 292 case 'x': /* date format */ 293 if (!(s = nsd_strptime(s, "%m/%d/%y", tm))) { 294 return NULL; 295 } 296 break; 297 case 'X': /* time format */ 298 if (!(s = nsd_strptime(s, "%H:%M:%S", tm))) { 299 return NULL; 300 } 301 break; 302 case 'y': /* last two digits of a year */ 303 ret = str2int(&s, 2); 304 if (ret < 0 || ret > 99) { /* must be in [00,99] */ 305 return NULL; 306 } 307 if (split_year) { 308 tm->tm_year = ((tm->tm_year/100) * 100) + ret; 309 } 310 else { 311 split_year = 1; 312 313 /** currently: 314 * if in [0,68] we are in 21th century, 315 * if in [69,99] we are in 20th century. 316 **/ 317 if (ret < 69) /* 2000 */ 318 ret += 100; 319 tm->tm_year = ret; 320 } 321 break; 322 case 'Y': /* year */ 323 ret = str2int(&s, 4); 324 if (ret < 0 || ret > 9999) { 325 return NULL; 326 } 327 tm->tm_year = ret - TM_YEAR_BASE; 328 break; 329 case '\0': 330 default: /* unsupported, cannot match format */ 331 return NULL; 332 break; 333 } 334 } 335 else { /* literal */ 336 /* if input cannot match format, return NULL */ 337 if (*s != c) 338 return NULL; 339 s++; 340 } 341 342 format++; 343 } 344 345 /* return pointer to remainder of s */ 346 return (char*) s; 347 } 348 349 #endif /* STRPTIME_WORKS */ 350