static int tzload()

in libs/libc/time/lib_localtime.c [571:1113]


static int tzload(FAR const char *name,
                  FAR struct state_s *sp, int doextend)
{
  int i;
  int fid;
  int stored;
  ssize_t nread;

  typedef union
  {
    struct tzhead_s tzhead;
    char buf[2 * sizeof(struct tzhead_s) +
             2 * sizeof(struct state_s) +
             4 * TZ_MAX_TIMES];
  } u_t;

  /* Section 4.9.1 of the C standard says that
   * "FILENAME_MAX expands to an integral constant expression
   * that is the size needed for an array of char large enough
   * to hold the longest file name string that the implementation
   * guarantees can be opened."
   */

  union local_storage
  {
    char fullname[FILENAME_MAX + 1];

    /* The main part of the storage for this function.  */

    struct
      {
        u_t u;
        struct state_s st;
      } u;
  };

  FAR char *fullname;
  FAR u_t *up;
  int doaccess;
  FAR union local_storage *lsp;
  int tzheadsize = sizeof(struct tzhead_s);

  lsp = lib_malloc(sizeof(*lsp));
  if (lsp == NULL)
    {
      return -1;
    }

  fullname = lsp->fullname;
  up = &lsp->u.u;

  sp->goback = sp->goahead = FALSE;

  if (name == NULL)
    {
      name = TZDEFAULT;
      if (name == NULL)
        {
          goto oops;
        }
    }

  if (name[0] == ':')
    {
      ++name;
    }

  doaccess = name[0] == '/';
  if (!doaccess)
    {
      FAR const char *dot;
      size_t namelen = strlen(name);
      const char tzdirslash[sizeof(TZDIR)] = TZDIR "/";

      if (sizeof(lsp->fullname) - sizeof(tzdirslash) <= namelen)
        {
          goto oops;
        }

      /* Create a string "TZDIR/NAME".  Using sprintf here
       * would pull in stdio (and would fail if the
       * resulting string length exceeded INT_MAX!).
       */

      memcpy(fullname, tzdirslash, sizeof(tzdirslash));
      strlcpy(fullname + sizeof(tzdirslash), name,
              sizeof(lsp->fullname) - sizeof(tzdirslash));

      /* Set doaccess if NAME contains a ".." file name
       * component, as such a name could read a file outside
       * the TZDIR virtual subtree.
       */

      for (dot = name; (dot = strchr(dot, '.')); dot++)
        {
          if ((dot == name || dot[0 - 1] == '/') && dot[1] == '.' &&
              (dot[2] == '/' || !dot[2]))
            {
              doaccess = TRUE;
              break;
            }
        }

      name = fullname;
    }

  if (doaccess && access(name, R_OK) != 0)
    {
      goto oops;
    }

  fid = _NX_OPEN(name, O_RDONLY | O_CLOEXEC);
  if (fid < 0)
    {
      goto oops;
    }

  nread = _NX_READ(fid, up->buf, sizeof(up->buf));
  if (_NX_CLOSE(fid) < 0 || nread < tzheadsize)
    {
      goto oops;
    }

  for (stored = 4; stored <= 8; stored *= 2)
    {
      char version = up->tzhead.tzh_version[0];
      int skip_datablock = stored == 4 && version;
      int_fast32_t datablock_size;
      int_fast32_t ttisstdcnt;
      int_fast32_t ttisutcnt;
      int_fast32_t leapcnt;
      int_fast32_t timecnt;
      int_fast32_t typecnt;
      int_fast32_t charcnt;
      int_fast64_t prevtr = -1;
      int_fast32_t prevcorr;
      FAR const char *p;

      ttisstdcnt  = detzcode(up->tzhead.tzh_ttisstdcnt);
      ttisutcnt  = detzcode(up->tzhead.tzh_ttisutcnt);
      leapcnt = detzcode(up->tzhead.tzh_leapcnt);
      timecnt = detzcode(up->tzhead.tzh_timecnt);
      typecnt = detzcode(up->tzhead.tzh_typecnt);
      charcnt = detzcode(up->tzhead.tzh_charcnt);
      p = up->buf + tzheadsize;
      if (leapcnt < 0 || leapcnt > TZ_MAX_LEAPS ||
          typecnt < 0 || typecnt > TZ_MAX_TYPES ||
          timecnt < 0 || timecnt > TZ_MAX_TIMES ||
          charcnt < 0 || charcnt > TZ_MAX_CHARS ||
          ttisstdcnt < 0 || ttisstdcnt > TZ_MAX_TYPES ||
          ttisutcnt < 0 || ttisutcnt > TZ_MAX_TYPES)
        {
          goto oops;
        }

      datablock_size = (timecnt * stored          /* ats */
                     + timecnt                    /* types */
                     + typecnt * 6                /* ttinfos */
                     + charcnt                    /* chars */
                     + leapcnt * (stored + 4)     /* lsinfos */
                     + ttisstdcnt                 /* ttisstds */
                     + ttisutcnt);                /* ttisuts */
      if (nread - tzheadsize < datablock_size)
        {
          goto oops;
        }

      if (skip_datablock)
        {
          p += datablock_size;
        }
      else
        {
          if (!((ttisstdcnt == typecnt || ttisstdcnt == 0) &&
              (ttisutcnt == typecnt || ttisutcnt == 0)))
            {
              goto oops;
            }

          sp->leapcnt = leapcnt;
          sp->timecnt = timecnt;
          sp->typecnt = typecnt;
          sp->charcnt = charcnt;

          timecnt = 0;
          for (i = 0; i < sp->timecnt; ++i)
            {
              int_fast64_t at = stored == 4 ? detzcode(p) : detzcode64(p);
              sp->types[i] = at <= TIME_T_MAX;
              if (sp->types[i])
                {
                  time_t attime = ((TYPE_SIGNED(time_t) ?
                                  at < TIME_T_MIN : at < 0) ?
                                  TIME_T_MIN : at);
                  if (timecnt && attime <= sp->ats[timecnt - 1])
                    {
                      if (attime < sp->ats[timecnt - 1])
                        {
                          goto oops;
                        }

                      sp->types[i - 1] = 0;
                      timecnt--;
                    }

                  sp->ats[timecnt++] = attime;
                }

              p += stored;
            }

          timecnt = 0;
          for (i = 0; i < sp->timecnt; ++i)
            {
              unsigned char typ = *p++;
              if (sp->typecnt <= typ)
                {
                  goto oops;
                }

              if (sp->types[i])
                {
                  sp->types[timecnt++] = typ;
                }
            }

          sp->timecnt = timecnt;
          for (i = 0; i < sp->typecnt; ++i)
            {
              FAR struct ttinfo_s *ttisp;
              unsigned char isdst;
              unsigned char desigidx;

              ttisp = &sp->ttis[i];
              ttisp->tt_utoff = detzcode(p);
              p += 4;
              isdst = *p++;
              if (isdst >= 2)
                {
                  goto oops;
                }

              ttisp->tt_isdst = isdst;
              desigidx = *p++;
              if (desigidx >= sp->charcnt)
                {
                  goto oops;
                }

              ttisp->tt_desigidx = desigidx;
            }

          for (i = 0; i < sp->charcnt; ++i)
            {
              sp->chars[i] = *p++;
            }

          /* Ensure '\0'-terminated, and make it safe to call
           * ttunspecified later.
           */

          memset(&sp->chars[i], 0, CHARS_EXTRA);

          /* Read leap seconds, discarding those out of time_t range. */

          leapcnt = 0;
          for (i = 0; i < sp->leapcnt; ++i)
            {
              int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
              int_fast32_t corr = detzcode(p + stored);
              p += stored + 4;

              /* Leap seconds cannot occur before the Epoch,
               * or out of order.
               */

              if (tr <= prevtr)
                {
                  goto oops;
                }

              /* To avoid other botches in this code, each leap second's
               * correction must differ from the previous one's by 1
               * second or less, except that the first correction can be
               * any value; these requirements are more generous than
               * RFC 8536, to allow future RFC extensions.
               */

              if (!(i == 0 || (prevcorr < corr ? corr == prevcorr + 1 :
                                                 (corr == prevcorr ||
                                                 corr == prevcorr - 1))))
                {
                  goto oops;
                }

              prevtr = tr;
              prevcorr = corr;
              if (tr <= TIME_T_MAX)
                {
                  sp->lsis[leapcnt].ls_trans = tr;
                  sp->lsis[leapcnt].ls_corr = corr;
                  leapcnt++;
                }
            }

          sp->leapcnt = leapcnt;
          for (i = 0; i < sp->typecnt; ++i)
            {
              FAR struct ttinfo_s *ttisp;

              ttisp = &sp->ttis[i];
              if (ttisstdcnt == 0)
                {
                  ttisp->tt_ttisstd = FALSE;
                }
              else
                {
                  if (*p != TRUE && *p != FALSE)
                    {
                      goto oops;
                    }

                  ttisp->tt_ttisstd = *p++;
                }
            }

          for (i = 0; i < sp->typecnt; ++i)
            {
              FAR struct ttinfo_s *ttisp;

              ttisp = &sp->ttis[i];
              if (ttisutcnt == 0)
                {
                  ttisp->tt_ttisut = FALSE;
                }
              else
                {
                  if (*p != TRUE && *p != FALSE)
                    {
                      goto oops;
                    }

                  ttisp->tt_ttisut = *p++;
                }
            }
        }

      nread -= p - up->buf;
      for (i = 0; i < nread; ++i)
        {
          up->buf[i] = p[i];
        }

      /* If this is an old file, we're done. */

      if (version == '\0')
        {
          break;
        }
    }

  if (doextend && nread > 2 &&
      up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
      sp->typecnt + 2 <= TZ_MAX_TYPES)
    {
      FAR struct state_s *ts = &lsp->u.st;

      up->buf[nread - 1] = '\0';
      if (tzparse(&up->buf[1], ts, sp) == 0)
        {
          /* Attempt to reuse existing abbreviations.
           * Without this, America/Anchorage would be right on
           * the edge after 2037 when TZ_MAX_CHARS is 50, as
           * sp->charcnt equals 40 (for LMT AST AWT APT AHST
           * AHDT YST AKDT AKST) and ts->charcnt equals 10
           * (for AKST AKDT).  Reusing means sp->charcnt can
           * stay 40 in this example.
           */

          int gotabbr = 0;
          int charcnt = sp->charcnt;
          for (i = 0; i < ts->typecnt; i++)
            {
              FAR char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx;
              int j;

              for (j = 0; j < charcnt; j++)
                {
                  if (strcmp(sp->chars + j, tsabbr) == 0)
                    {
                      ts->ttis[i].tt_desigidx = j;
                      gotabbr++;
                      break;
                    }
                }

              if (j >= charcnt)
                {
                  int tsabbrlen = strlen(tsabbr);

                  if (j + tsabbrlen < TZ_MAX_CHARS)
                    {
                      strlcpy(sp->chars + j, tsabbr, sizeof(sp->chars) - j);
                      charcnt = j + tsabbrlen + 1;
                      ts->ttis[i].tt_desigidx = j;
                      gotabbr++;
                    }
                }
            }

          if (gotabbr == ts->typecnt)
            {
              sp->charcnt = charcnt;

              /* Ignore any trailing, no-op transitions generated
               * by zic as they don't help here and can run afoul
               * of bugs in zic 2016j or earlier.
               */

              while (sp->timecnt > 1 && (sp->types[sp->timecnt - 1] ==
                                         sp->types[sp->timecnt - 2]))
                {
                  sp->timecnt--;
                }

              for (i = 0; i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; i++)
                {
                  time_t t = ts->ats[i];
                  if (increment_overflow_time(&t, leapcorr(sp, t))
                      || (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1]))
                    {
                      continue;
                    }

                  sp->ats[sp->timecnt] = t;
                  sp->types[sp->timecnt] = (sp->typecnt + ts->types[i]);
                  sp->timecnt++;
                }

              for (i = 0; i < ts->typecnt; i++)
                {
                  sp->ttis[sp->typecnt++] = ts->ttis[i];
                }
            }
        }
    }

  if (sp->typecnt == 0)
    {
      goto oops;
    }

  if (sp->timecnt > 1)
    {
      if (TIME_T_MAX > SECSPERREPEAT &&
          sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT)
        {
          time_t repeatat = sp->ats[0] + SECSPERREPEAT;
          int repeattype = sp->types[0];
          for (i = 1; i < sp->timecnt; ++i)
            {
              if (sp->ats[i] == repeatat &&
                  typesequiv(sp, sp->types[i], repeattype))
                {
                  sp->goback = TRUE;
                  break;
                }
            }
        }

      if (TIME_T_MAX > SECSPERREPEAT &&
          TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1])
        {
          time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT;
          int repeattype = sp->types[sp->timecnt - 1];
          for (i = sp->timecnt - 2; i >= 0; --i)
            {
              if (sp->ats[i] == repeatat &&
                  typesequiv(sp, sp->types[i], repeattype))
                {
                  sp->goahead = TRUE;
                  break;
                }
            }
        }
    }

  /* If type 0 is is unused in transitions, it's the type to use for early
   * times.
   */

  for (i = 0; i < sp->timecnt; ++i)
    {
      if (sp->types[i] == 0)
        {
          break;
        }
    }

  i = i < sp->timecnt  && ! ttunspecified(sp, 0) ? -1 : 0;

  /* Absent the above, if there are transition times and the first
   * transition is to a daylight time find the standard type less than and
   * closest to the type of the first transition.
   */

  if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst)
    {
      i = sp->types[0];
      while (--i >= 0)
        {
          if (!sp->ttis[i].tt_isdst)
            {
              break;
            }
        }
    }

  /* If no result yet, find the first standard type. If there is none, punt
   * to type zero.
   */

  if (i < 0)
    {
      i = 0;
      while (sp->ttis[i].tt_isdst)
        {
          if (++i >= sp->typecnt)
            {
              i = 0;
              break;
            }
        }
    }

  sp->defaulttype = i;
  lib_free(lsp);
  return 0;

oops:
  lib_free(lsp);
  return -1;
}