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;
}