1/* $NetBSD: strptime.c,v 1.4 2006/10/26 07:24:14 lukem Exp $ */ 2/* from NetBSD: strptime.c,v 1.18 1999/04/29 02:58:30 tv Exp */ 3 4/*- 5 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code was contributed to The NetBSD Foundation by Klaus Klein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include "tnftp.h" 40 41/* 42 * We do not implement alternate representations. However, we always 43 * check whether a given modifier is allowed for a certain conversion. 44 */ 45#define ALT_E 0x01 46#define ALT_O 0x02 47#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } 48 49 50static int conv_num(const char **, int *, int, int); 51 52static const char *day[7] = { 53 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", 54 "Friday", "Saturday" 55}; 56static const char *abday[7] = { 57 "Sun","Mon","Tue","Wed","Thu","Fri","Sat" 58}; 59static const char *mon[12] = { 60 "January", "February", "March", "April", "May", "June", "July", 61 "August", "September", "October", "November", "December" 62}; 63static const char *abmon[12] = { 64 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 65 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 66}; 67static const char *am_pm[2] = { 68 "AM", "PM" 69}; 70 71 72char * 73strptime(const char *buf, const char *fmt, struct tm *tm) 74{ 75 char c; 76 const char *bp; 77 size_t len = 0; 78 int alt_format, i, split_year = 0; 79 80 bp = buf; 81 82 while ((c = *fmt) != '\0') { 83 /* Clear `alternate' modifier prior to new conversion. */ 84 alt_format = 0; 85 86 /* Eat up white-space. */ 87 if (isspace(c)) { 88 while (isspace(*bp)) 89 bp++; 90 91 fmt++; 92 continue; 93 } 94 95 if ((c = *fmt++) != '%') 96 goto literal; 97 98 99again: switch (c = *fmt++) { 100 case '%': /* "%%" is converted to "%". */ 101literal: 102 if (c != *bp++) 103 return (0); 104 break; 105 106 /* 107 * "Alternative" modifiers. Just set the appropriate flag 108 * and start over again. 109 */ 110 case 'E': /* "%E?" alternative conversion modifier. */ 111 LEGAL_ALT(0); 112 alt_format |= ALT_E; 113 goto again; 114 115 case 'O': /* "%O?" alternative conversion modifier. */ 116 LEGAL_ALT(0); 117 alt_format |= ALT_O; 118 goto again; 119 120 /* 121 * "Complex" conversion rules, implemented through recursion. 122 */ 123 case 'c': /* Date and time, using the locale's format. */ 124 LEGAL_ALT(ALT_E); 125 if (!(bp = strptime(bp, "%x %X", tm))) 126 return (0); 127 break; 128 129 case 'D': /* The date as "%m/%d/%y". */ 130 LEGAL_ALT(0); 131 if (!(bp = strptime(bp, "%m/%d/%y", tm))) 132 return (0); 133 break; 134 135 case 'R': /* The time as "%H:%M". */ 136 LEGAL_ALT(0); 137 if (!(bp = strptime(bp, "%H:%M", tm))) 138 return (0); 139 break; 140 141 case 'r': /* The time in 12-hour clock representation. */ 142 LEGAL_ALT(0); 143 if (!(bp = strptime(bp, "%I:%M:%S %p", tm))) 144 return (0); 145 break; 146 147 case 'T': /* The time as "%H:%M:%S". */ 148 LEGAL_ALT(0); 149 if (!(bp = strptime(bp, "%H:%M:%S", tm))) 150 return (0); 151 break; 152 153 case 'X': /* The time, using the locale's format. */ 154 LEGAL_ALT(ALT_E); 155 if (!(bp = strptime(bp, "%H:%M:%S", tm))) 156 return (0); 157 break; 158 159 case 'x': /* The date, using the locale's format. */ 160 LEGAL_ALT(ALT_E); 161 if (!(bp = strptime(bp, "%m/%d/%y", tm))) 162 return (0); 163 break; 164 165 /* 166 * "Elementary" conversion rules. 167 */ 168 case 'A': /* The day of week, using the locale's form. */ 169 case 'a': 170 LEGAL_ALT(0); 171 for (i = 0; i < 7; i++) { 172 /* Full name. */ 173 len = strlen(day[i]); 174 if (strncasecmp(day[i], bp, len) == 0) 175 break; 176 177 /* Abbreviated name. */ 178 len = strlen(abday[i]); 179 if (strncasecmp(abday[i], bp, len) == 0) 180 break; 181 } 182 183 /* Nothing matched. */ 184 if (i == 7) 185 return (0); 186 187 tm->tm_wday = i; 188 bp += len; 189 break; 190 191 case 'B': /* The month, using the locale's form. */ 192 case 'b': 193 case 'h': 194 LEGAL_ALT(0); 195 for (i = 0; i < 12; i++) { 196 /* Full name. */ 197 len = strlen(mon[i]); 198 if (strncasecmp(mon[i], bp, len) == 0) 199 break; 200 201 /* Abbreviated name. */ 202 len = strlen(abmon[i]); 203 if (strncasecmp(abmon[i], bp, len) == 0) 204 break; 205 } 206 207 /* Nothing matched. */ 208 if (i == 12) 209 return (0); 210 211 tm->tm_mon = i; 212 bp += len; 213 break; 214 215 case 'C': /* The century number. */ 216 LEGAL_ALT(ALT_E); 217 if (!(conv_num(&bp, &i, 0, 99))) 218 return (0); 219 220 if (split_year) { 221 tm->tm_year = (tm->tm_year % 100) + (i * 100); 222 } else { 223 tm->tm_year = i * 100; 224 split_year = 1; 225 } 226 break; 227 228 case 'd': /* The day of month. */ 229 case 'e': 230 LEGAL_ALT(ALT_O); 231 if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) 232 return (0); 233 break; 234 235 case 'k': /* The hour (24-hour clock representation). */ 236 LEGAL_ALT(0); 237 /* FALLTHROUGH */ 238 case 'H': 239 LEGAL_ALT(ALT_O); 240 if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) 241 return (0); 242 break; 243 244 case 'l': /* The hour (12-hour clock representation). */ 245 LEGAL_ALT(0); 246 /* FALLTHROUGH */ 247 case 'I': 248 LEGAL_ALT(ALT_O); 249 if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) 250 return (0); 251 if (tm->tm_hour == 12) 252 tm->tm_hour = 0; 253 break; 254 255 case 'j': /* The day of year. */ 256 LEGAL_ALT(0); 257 if (!(conv_num(&bp, &i, 1, 366))) 258 return (0); 259 tm->tm_yday = i - 1; 260 break; 261 262 case 'M': /* The minute. */ 263 LEGAL_ALT(ALT_O); 264 if (!(conv_num(&bp, &tm->tm_min, 0, 59))) 265 return (0); 266 break; 267 268 case 'm': /* The month. */ 269 LEGAL_ALT(ALT_O); 270 if (!(conv_num(&bp, &i, 1, 12))) 271 return (0); 272 tm->tm_mon = i - 1; 273 break; 274 275 case 'p': /* The locale's equivalent of AM/PM. */ 276 LEGAL_ALT(0); 277 /* AM? */ 278 len = strlen(am_pm[0]); 279 if (strncasecmp(am_pm[0], bp, len) == 0) { 280 if (tm->tm_hour > 11) 281 return (0); 282 283 bp += len; 284 break; 285 } 286 287 /* PM? */ 288 len = strlen(am_pm[1]); 289 if (strncasecmp(am_pm[1], bp, len) == 0) { 290 if (tm->tm_hour > 11) 291 return (0); 292 293 tm->tm_hour += 12; 294 bp += len; 295 break; 296 } 297 298 /* Nothing matched. */ 299 return (0); 300 301 case 'S': /* The seconds. */ 302 LEGAL_ALT(ALT_O); 303 if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) 304 return (0); 305 break; 306 307 case 'U': /* The week of year, beginning on sunday. */ 308 case 'W': /* The week of year, beginning on monday. */ 309 LEGAL_ALT(ALT_O); 310 /* 311 * XXX This is bogus, as we can not assume any valid 312 * information present in the tm structure at this 313 * point to calculate a real value, so just check the 314 * range for now. 315 */ 316 if (!(conv_num(&bp, &i, 0, 53))) 317 return (0); 318 break; 319 320 case 'w': /* The day of week, beginning on sunday. */ 321 LEGAL_ALT(ALT_O); 322 if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) 323 return (0); 324 break; 325 326 case 'Y': /* The year. */ 327 LEGAL_ALT(ALT_E); 328 if (!(conv_num(&bp, &i, 0, 9999))) 329 return (0); 330 331 tm->tm_year = i - TM_YEAR_BASE; 332 break; 333 334 case 'y': /* The year within 100 years of the epoch. */ 335 LEGAL_ALT(ALT_E | ALT_O); 336 if (!(conv_num(&bp, &i, 0, 99))) 337 return (0); 338 339 if (split_year) { 340 tm->tm_year = ((tm->tm_year / 100) * 100) + i; 341 break; 342 } 343 split_year = 1; 344 if (i <= 68) 345 tm->tm_year = i + 2000 - TM_YEAR_BASE; 346 else 347 tm->tm_year = i + 1900 - TM_YEAR_BASE; 348 break; 349 350 /* 351 * Miscellaneous conversions. 352 */ 353 case 'n': /* Any kind of white-space. */ 354 case 't': 355 LEGAL_ALT(0); 356 while (isspace(*bp)) 357 bp++; 358 break; 359 360 361 default: /* Unknown/unsupported conversion. */ 362 return (0); 363 } 364 365 366 } 367 368 /* LINTED functional specification */ 369 return ((char *)bp); 370} 371 372 373static int 374conv_num(const char **buf, int *dest, int llim, int ulim) 375{ 376 int result = 0; 377 378 /* The limit also determines the number of valid digits. */ 379 int rulim = ulim; 380 381 if (**buf < '0' || **buf > '9') 382 return (0); 383 384 do { 385 result *= 10; 386 result += *(*buf)++ - '0'; 387 rulim /= 10; 388 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); 389 390 if (result < llim || result > ulim) 391 return (0); 392 393 *dest = result; 394 return (1); 395} 396