1 /*
   2  * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.
   3  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
   4  *
   5  * Copyright (c) 2011 The FreeBSD Foundation
   6  * All rights reserved.
   7  * Portions of this software were developed by David Chisnall
   8  * under sponsorship from the FreeBSD Foundation.
   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  *
  14  * 1. Redistributions of source code must retain the above copyright
  15  *    notice, this list of conditions and the following disclaimer.
  16  *
  17  * 2. Redistributions in binary form must reproduce the above copyright
  18  *    notice, this list of conditions and the following disclaimer
  19  *    in the documentation and/or other materials provided with the
  20  *    distribution.
  21  *
  22  * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
  23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
  26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  29  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  31  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  32  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33  *
  34  * The views and conclusions contained in the software and documentation
  35  * are those of the authors and should not be interpreted as representing
  36  * official policies, either expressed or implied, of Powerdog Industries.
  37  */
  38 
  39 #include "lint.h"
  40 #include <time.h>
  41 #include <ctype.h>
  42 #include <errno.h>
  43 #include <stdlib.h>
  44 #include <string.h>
  45 #include <pthread.h>
  46 #include <alloca.h>
  47 #include "timelocal.h"
  48 
  49 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
  50 
  51 #define F_GMT           (1 << 0)
  52 #define F_ZERO          (1 << 1)
  53 #define F_RECURSE       (1 << 2)
  54 
  55 static char *
  56 __strptime(const char *buf, const char *fmt, struct tm *tm, int *flagsp,
  57     int *GMTp, locale_t locale)
  58 {
  59         char    c;
  60         const char *ptr;
  61         int     i, len, recurse = 0;
  62         int Ealternative, Oalternative;
  63         struct lc_time_T *tptr = __get_current_time_locale(locale);
  64 
  65         if (*flagsp & F_RECURSE)
  66                 recurse = 1;
  67         *flagsp |= F_RECURSE;
  68 
  69         if (*flagsp & F_ZERO)
  70                 (void) memset(tm, 0, sizeof (*tm));
  71         *flagsp &= ~F_ZERO;
  72 
  73         ptr = fmt;
  74         while (*ptr != 0) {
  75                 if (*buf == 0)
  76                         break;
  77 
  78                 c = *ptr++;
  79 
  80                 if (c != '%') {
  81                         if (isspace(c))
  82                                 while (isspace(*buf))
  83                                         buf++;
  84                         else if (c != *buf++)
  85                                 return (NULL);
  86                         continue;
  87                 }
  88 
  89                 Ealternative = 0;
  90                 Oalternative = 0;
  91 label:
  92                 c = *ptr++;
  93                 switch (c) {
  94                 case 0:
  95                 case '%':
  96                         if (*buf++ != '%')
  97                                 return (NULL);
  98                         break;
  99 
 100                 case '+':
 101                         buf = __strptime(buf, tptr->date_fmt, tm, flagsp,
 102                             GMTp, locale);
 103                         if (buf == NULL)
 104                                 return (NULL);
 105                         break;
 106 
 107                 case 'C':
 108                         if (!isdigit(*buf))
 109                                 return (NULL);
 110 
 111                         /* XXX This will break for 3-digit centuries. */
 112                         len = 2;
 113                         for (i = 0; len && isdigit(*buf); buf++) {
 114                                 i *= 10;
 115                                 i += *buf - '0';
 116                                 len--;
 117                         }
 118                         if (i < 19)
 119                                 return (NULL);
 120 
 121                         tm->tm_year = i * 100 - 1900;
 122                         break;
 123 
 124                 case 'c':
 125                         buf = __strptime(buf, tptr->c_fmt, tm, flagsp,
 126                             GMTp, locale);
 127                         if (buf == NULL)
 128                                 return (NULL);
 129                         break;
 130 
 131                 case 'D':
 132                         buf = __strptime(buf, "%m/%d/%y", tm, flagsp,
 133                             GMTp, locale);
 134                         if (buf == NULL)
 135                                 return (NULL);
 136                         break;
 137 
 138                 case 'E':
 139                         if (Ealternative || Oalternative)
 140                                 break;
 141                         Ealternative++;
 142                         goto label;
 143 
 144                 case 'O':
 145                         if (Ealternative || Oalternative)
 146                                 break;
 147                         Oalternative++;
 148                         goto label;
 149 
 150                 case 'F':
 151                         buf = __strptime(buf, "%Y-%m-%d", tm, flagsp,
 152                             GMTp, locale);
 153                         if (buf == NULL)
 154                                 return (NULL);
 155                         break;
 156 
 157                 case 'R':
 158                         buf = __strptime(buf, "%H:%M", tm, flagsp,
 159                             GMTp, locale);
 160                         if (buf == NULL)
 161                                 return (NULL);
 162                         break;
 163 
 164                 case 'r':
 165                         buf = __strptime(buf, tptr->ampm_fmt, tm, flagsp,
 166                             GMTp, locale);
 167                         if (buf == NULL)
 168                                 return (NULL);
 169                         break;
 170 
 171                 case 'T':
 172                         buf = __strptime(buf, "%H:%M:%S", tm, flagsp,
 173                             GMTp, locale);
 174                         if (buf == NULL)
 175                                 return (NULL);
 176                         break;
 177 
 178                 case 'X':
 179                         buf = __strptime(buf, tptr->X_fmt, tm, flagsp,
 180                             GMTp, locale);
 181                         if (buf == NULL)
 182                                 return (NULL);
 183                         break;
 184 
 185                 case 'x':
 186                         buf = __strptime(buf, tptr->x_fmt, tm, flagsp,
 187                             GMTp, locale);
 188                         if (buf == NULL)
 189                                 return (NULL);
 190                         break;
 191 
 192                 case 'j':
 193                         if (!isdigit(*buf))
 194                                 return (NULL);
 195 
 196                         len = 3;
 197                         for (i = 0; len && isdigit(*buf); buf++) {
 198                                 i *= 10;
 199                                 i += *buf - '0';
 200                                 len--;
 201                         }
 202                         if (i < 1 || i > 366)
 203                                 return (NULL);
 204 
 205                         tm->tm_yday = i - 1;
 206                         break;
 207 
 208                 case 'M':
 209                 case 'S':
 210                         if (*buf == 0 || isspace(*buf))
 211                                 break;
 212 
 213                         if (!isdigit(*buf))
 214                                 return (NULL);
 215 
 216                         len = 2;
 217                         for (i = 0; len && isdigit(*buf); buf++) {
 218                                 i *= 10;
 219                                 i += *buf - '0';
 220                                 len--;
 221                         }
 222 
 223                         if (c == 'M') {
 224                                 if (i > 59)
 225                                         return (NULL);
 226                                 tm->tm_min = i;
 227                         } else {
 228                                 if (i > 60)
 229                                         return (NULL);
 230                                 tm->tm_sec = i;
 231                         }
 232 
 233                         if (isspace(*buf))
 234                                 while (*ptr != 0 && !isspace(*ptr))
 235                                         ptr++;
 236                         break;
 237 
 238                 case 'H':
 239                 case 'I':
 240                 case 'k':
 241                 case 'l':
 242                         /*
 243                          * Of these, %l is the only specifier explicitly
 244                          * documented as not being zero-padded.  However,
 245                          * there is no harm in allowing zero-padding.
 246                          *
 247                          * XXX The %l specifier may gobble one too many
 248                          * digits if used incorrectly.
 249                          */
 250                         if (!isdigit(*buf))
 251                                 return (NULL);
 252 
 253                         len = 2;
 254                         for (i = 0; len && isdigit(*buf); buf++) {
 255                                 i *= 10;
 256                                 i += *buf - '0';
 257                                 len--;
 258                         }
 259                         if (c == 'H' || c == 'k') {
 260                                 if (i > 23)
 261                                         return (NULL);
 262                         } else if (i > 12)
 263                                 return (NULL);
 264 
 265                         tm->tm_hour = i;
 266 
 267                         if (isspace(*buf))
 268                                 while (*ptr != 0 && !isspace(*ptr))
 269                                         ptr++;
 270                         break;
 271 
 272                 case 'p':
 273                         /*
 274                          * XXX This is bogus if parsed before hour-related
 275                          * specifiers.
 276                          */
 277                         len = strlen(tptr->am);
 278                         if (strncasecmp(buf, tptr->am, len) == 0) {
 279                                 if (tm->tm_hour > 12)
 280                                         return (NULL);
 281                                 if (tm->tm_hour == 12)
 282                                         tm->tm_hour = 0;
 283                                 buf += len;
 284                                 break;
 285                         }
 286 
 287                         len = strlen(tptr->pm);
 288                         if (strncasecmp(buf, tptr->pm, len) == 0) {
 289                                 if (tm->tm_hour > 12)
 290                                         return (NULL);
 291                                 if (tm->tm_hour != 12)
 292                                         tm->tm_hour += 12;
 293                                 buf += len;
 294                                 break;
 295                         }
 296 
 297                         return (NULL);
 298 
 299                 case 'A':
 300                 case 'a':
 301                         for (i = 0; i < asizeof(tptr->weekday); i++) {
 302                                 len = strlen(tptr->weekday[i]);
 303                                 if (strncasecmp(buf, tptr->weekday[i], len) ==
 304                                     0)
 305                                         break;
 306                                 len = strlen(tptr->wday[i]);
 307                                 if (strncasecmp(buf, tptr->wday[i], len) == 0)
 308                                         break;
 309                         }
 310                         if (i == asizeof(tptr->weekday))
 311                                 return (NULL);
 312 
 313                         tm->tm_wday = i;
 314                         buf += len;
 315                         break;
 316 
 317                 case 'U':
 318                 case 'W':
 319                         /*
 320                          * XXX This is bogus, as we can not assume any valid
 321                          * information present in the tm structure at this
 322                          * point to calculate a real value, so just check the
 323                          * range for now.
 324                          */
 325                         if (!isdigit(*buf))
 326                                 return (NULL);
 327 
 328                         len = 2;
 329                         for (i = 0; len && isdigit(*buf); buf++) {
 330                                 i *= 10;
 331                                 i += *buf - '0';
 332                                 len--;
 333                         }
 334                         if (i > 53)
 335                                 return (NULL);
 336 
 337                         if (isspace(*buf))
 338                                 while (*ptr != 0 && !isspace(*ptr))
 339                                         ptr++;
 340                         break;
 341 
 342                 case 'w':
 343                         if (!isdigit(*buf))
 344                                 return (NULL);
 345 
 346                         i = *buf - '0';
 347                         if (i > 6)
 348                                 return (NULL);
 349 
 350                         tm->tm_wday = i;
 351 
 352                         if (isspace(*buf))
 353                                 while (*ptr != 0 && !isspace(*ptr))
 354                                         ptr++;
 355                         break;
 356 
 357                 case 'e':
 358                         /*
 359                          * The %e format has a space before single digits
 360                          * which we need to skip.
 361                          */
 362                         if (isspace(*buf))
 363                                 buf++;
 364                         /* FALLTHROUGH */
 365                 case 'd':
 366                         /*
 367                          * The %e specifier is explicitly documented as not
 368                          * being zero-padded but there is no harm in allowing
 369                          * such padding.
 370                          *
 371                          * XXX The %e specifier may gobble one too many
 372                          * digits if used incorrectly.
 373                          */
 374                         if (!isdigit(*buf))
 375                                 return (NULL);
 376 
 377                         len = 2;
 378                         for (i = 0; len && isdigit(*buf); buf++) {
 379                                 i *= 10;
 380                                 i += *buf - '0';
 381                                 len--;
 382                         }
 383                         if (i > 31)
 384                                 return (NULL);
 385 
 386                         tm->tm_mday = i;
 387 
 388                         if (isspace(*buf))
 389                                 while (*ptr != 0 && !isspace(*ptr))
 390                                         ptr++;
 391                         break;
 392 
 393                 case 'B':
 394                 case 'b':
 395                 case 'h':
 396                         for (i = 0; i < asizeof(tptr->month); i++) {
 397                                 len = strlen(tptr->month[i]);
 398                                 if (strncasecmp(buf, tptr->month[i], len) == 0)
 399                                         break;
 400                         }
 401                         /*
 402                          * Try the abbreviated month name if the full name
 403                          * wasn't found.
 404                          */
 405                         if (i == asizeof(tptr->month)) {
 406                                 for (i = 0; i < asizeof(tptr->month); i++) {
 407                                         len = strlen(tptr->mon[i]);
 408                                         if (strncasecmp(buf, tptr->mon[i],
 409                                             len) == 0)
 410                                                 break;
 411                                 }
 412                         }
 413                         if (i == asizeof(tptr->month))
 414                                 return (NULL);
 415 
 416                         tm->tm_mon = i;
 417                         buf += len;
 418                         break;
 419 
 420                 case 'm':
 421                         if (!isdigit(*buf))
 422                                 return (NULL);
 423 
 424                         len = 2;
 425                         for (i = 0; len && isdigit(*buf); buf++) {
 426                                 i *= 10;
 427                                 i += *buf - '0';
 428                                 len--;
 429                         }
 430                         if (i < 1 || i > 12)
 431                                 return (NULL);
 432 
 433                         tm->tm_mon = i - 1;
 434 
 435                         if (isspace(*buf))
 436                                 while (*ptr != NULL && !isspace(*ptr))
 437                                         ptr++;
 438                         break;
 439 
 440                 case 's':
 441                         {
 442                         char *cp;
 443                         int sverrno;
 444                         time_t t;
 445 
 446                         sverrno = errno;
 447                         errno = 0;
 448                         t = strtol(buf, &cp, 10);
 449                         if (errno == ERANGE) {
 450                                 errno = sverrno;
 451                                 return (NULL);
 452                         }
 453                         errno = sverrno;
 454                         buf = cp;
 455                         (void) gmtime_r(&t, tm);
 456                         *flagsp |= F_GMT;
 457                         }
 458                         break;
 459 
 460                 case 'Y':
 461                 case 'y':
 462                         if (*buf == NULL || isspace(*buf))
 463                                 break;
 464 
 465                         if (!isdigit(*buf))
 466                                 return (NULL);
 467 
 468                         len = (c == 'Y') ? 4 : 2;
 469                         for (i = 0; len && isdigit(*buf); buf++) {
 470                                 i *= 10;
 471                                 i += *buf - '0';
 472                                 len--;
 473                         }
 474                         if (c == 'Y')
 475                                 i -= 1900;
 476                         if (c == 'y' && i < 69)
 477                                 i += 100;
 478                         if (i < 0)
 479                                 return (NULL);
 480 
 481                         tm->tm_year = i;
 482 
 483                         if (isspace(*buf))
 484                                 while (*ptr != 0 && !isspace(*ptr))
 485                                         ptr++;
 486                         break;
 487 
 488                 case 'Z':
 489                         {
 490                         const char *cp = buf;
 491                         char *zonestr;
 492 
 493                         while (isupper(*cp))
 494                                 ++cp;
 495                         if (cp - buf) {
 496                                 zonestr = alloca(cp - buf + 1);
 497                                 (void) strncpy(zonestr, buf, cp - buf);
 498                                 zonestr[cp - buf] = '\0';
 499                                 tzset();
 500                                 if (strcmp(zonestr, "GMT") == 0) {
 501                                         *flagsp |= F_GMT;
 502                                 } else if (0 == strcmp(zonestr, tzname[0])) {
 503                                         tm->tm_isdst = 0;
 504                                 } else if (0 == strcmp(zonestr, tzname[1])) {
 505                                         tm->tm_isdst = 1;
 506                                 } else {
 507                                         return (NULL);
 508                                 }
 509                                 buf += cp - buf;
 510                         }
 511                         }
 512                         break;
 513 
 514                 case 'z':
 515                         {
 516                         int sign = 1;
 517 
 518                         if (*buf != '+') {
 519                                 if (*buf == '-')
 520                                         sign = -1;
 521                                 else
 522                                         return (NULL);
 523                         }
 524                         buf++;
 525                         i = 0;
 526                         for (len = 4; len > 0; len--) {
 527                                 if (!isdigit(*buf))
 528                                         return (NULL);
 529                                 i *= 10;
 530                                 i += *buf - '0';
 531                                 buf++;
 532                         }
 533 
 534                         tm->tm_hour -= sign * (i / 100);
 535                         tm->tm_min -= sign * (i % 100);
 536                         *flagsp |= F_GMT;
 537                         }
 538                         break;
 539                 }
 540         }
 541 
 542         if (!recurse) {
 543                 if (buf && (*flagsp & F_GMT)) {
 544                         time_t t = timegm(tm);
 545                         (void) localtime_r(&t, tm);
 546                 }
 547         }
 548 
 549         return ((char *)buf);
 550 }
 551 
 552 char *
 553 strptime_l(const char * __restrict buf, const char * __restrict fmt,
 554     struct tm * __restrict tm, locale_t loc)
 555 {
 556         char *ret;
 557         int gmt;
 558         int flags = F_ZERO;
 559         FIX_LOCALE(loc);
 560 
 561         gmt = 0;
 562         ret = __strptime(buf, fmt, tm, &flags, &gmt, loc);
 563         if (ret && gmt) {
 564                 time_t t = timegm(tm);
 565                 localtime_r(&t, tm);
 566         }
 567 
 568         return (ret);
 569 }
 570 
 571 char *
 572 strptime(const char *buf, const char *fmt, struct tm *tm)
 573 {
 574         int     flags = F_ZERO;
 575 
 576         return (strptime_l(buf, fmt, tm, __get_locale()));
 577         // XXX return (__strptime(buf, fmt, tm, &flags));
 578 }
 579 
 580 /*
 581  * This is used by Solaris, and is a variant that does not clear the
 582  * incoming tm.  It is triggered by -D_STRPTIME_DONTZERO.
 583  */
 584 char *
 585 __strptime_dontzero(const char *buf, const char *fmt, struct tm *tm)
 586 {
 587         int     flags = 0;
 588         int     gmt = 0;
 589 
 590         return (__strptime(buf, fmt, tm, &flags, &gmt, __get_locale()));
 591         /* XXX */
 592 }