This avoids a call to malloc in some failure cases (e.g., bad TZ setting) and should simplify future improvements. * localtime.c (ALL_STATE): Default to 0. All uses changed. (tzloadbody): Last arg is now pointer-to-pointer, instead of just pointer, so that in the ALL_STATE case we malloc the storage instead of the caller doing it. Caller changed. --- localtime.c | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/localtime.c b/localtime.c index ef094560..c7b9f663 100644 --- a/localtime.c +++ b/localtime.c @@ -378,12 +378,14 @@ static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, struct tm *); static bool tzparse(char const *, struct state *, struct state const *); -#ifdef ALL_STATE +#ifndef ALL_STATE +# define ALL_STATE 0 +#endif + +#if ALL_STATE static struct state * lclptr; static struct state * gmtptr; -#endif /* defined ALL_STATE */ - -#ifndef ALL_STATE +#else static struct state lclmem; static struct state gmtmem; static struct state *const lclptr = &lclmem; @@ -702,18 +704,19 @@ enum { TZLOAD_TZSTRING = 2 }; /* Read any newline-surrounded TZ string. */ enum { TZLOAD_TZDIR_SUB = 4 }; /* TZ should be a file under TZDIR. */ /* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS. - Use *LSP for temporary storage. Return 0 on + Use **LSPP for temporary storage. Return 0 on success, an errno value on failure. */ static int tzloadbody(char const *name, struct state *sp, char tzloadflags, - union local_storage *lsp) + union local_storage **lspp) { register int i; register int fid; register int stored; register ssize_t nread; char const *relname; - register union input_buffer *up = &lsp->u.u; + union local_storage *lsp = *lspp; + union input_buffer *up; register int tzheadsize = sizeof(struct tzhead); int dd = AT_FDCWD; int oflags = (O_RDONLY | O_BINARY | O_CLOEXEC | O_CLOFORK @@ -793,6 +796,12 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, /* Create a string "TZDIR/NAME". Using sprintf here would pull in stdio (and would fail if the resulting string length exceeded INT_MAX!). */ + if (ALL_STATE) { + lsp = malloc(sizeof *lsp); + if (!lsp) + return HAVE_MALLOC_ERRNO ? errno : ENOMEM; + *lspp = lsp; + } cp = lsp->fullname; cp = mempcpy(cp, tzdirslash, tzdirslashlen); cp = mempcpy(cp, name, namelen); @@ -814,6 +823,13 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, && !tzfile_changed(fid, &st)) err = -1; else { + if (ALL_STATE && !lsp) { + lsp = malloc(sizeof *lsp); + if (!lsp) + return HAVE_MALLOC_ERRNO ? errno : ENOMEM; + *lspp = lsp; + } + up = &lsp->u.u; nread = read(fid, up->buf, sizeof up->buf); err = tzheadsize <= nread ? 0 : nread < 0 ? errno : EINVAL; } @@ -1069,19 +1085,18 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, static int tzload(char const *name, struct state *sp, char tzloadflags) { -#ifdef ALL_STATE - union local_storage *lsp = malloc(sizeof *lsp); - if (!lsp) { - return HAVE_MALLOC_ERRNO ? errno : ENOMEM; - } else { - int err = tzloadbody(name, sp, tzloadflags, lsp); - free(lsp); - return err; - } + int r; + union local_storage *lsp; +#if ALL_STATE + lsp = NULL; #else union local_storage ls; - return tzloadbody(name, sp, tzloadflags, &ls); + lsp = &ls; #endif + r = tzloadbody(name, sp, tzloadflags, &lsp); + if (ALL_STATE) + free(lsp); + return r; } static const int mon_lengths[2][MONSPERYEAR] = { @@ -1741,7 +1756,7 @@ tzset_unlocked(monotime_t now) ? 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0 : lcl_is_set < 0)) return; -# ifdef ALL_STATE +# if ALL_STATE if (! sp) lclptr = sp = malloc(sizeof *lclptr); # endif @@ -1805,7 +1820,7 @@ gmtcheck(void) if (lock() != 0) return; if (! gmt_is_set) { -#ifdef ALL_STATE +#if ALL_STATE gmtptr = malloc(sizeof *gmtptr); #endif if (gmtptr) -- 2.51.0