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