diff --git a/bedstead.c b/bedstead.c index fbeee78..9bdc44d 100644 --- a/bedstead.c +++ b/bedstead.c @@ -3144,79 +3144,64 @@ fullname_to_fontname(char const *fullname) static char * time_for_ttx(void) { - time_t now = 0; + struct tm *timeptr, tm; uintmax_t epochumax; - struct tm *timeptr; char *epochstr, *endptr, *timestr; /* Work out what timestamp to use. */ if ((epochstr = getenv("SOURCE_DATE_EPOCH")) != NULL) { - /* - * Correctly handling SOURCE_DATE_EPOCH is - * surprisingly fiddly. To make life slightly easy, - * we assume that we're on a POSIX system, where - * time_t is an integer type, and that we can reject - * negative values out of hand. - * - * Even given that, time_t might be any integer type. - * It might be signed or unsigned, and it might be a - * standard or an extended type. - * - * If time_t is unsigned, it's always safe to convert - * something to it. If the value is within the range - * of time_t it will remain unchanged, and if it - * isn't, it'll be reduced to be within the range (C11 - * 6.3.1.3). - * - * While it's well-known that signed integer overflow - * in C causes undefined behaviour, this doesn't apply - * to conversion. According to C11 6.3.1.3, if a - * conversion overflows "either the result is - * implementation-defined or an implementation-defined - * signal is raised." I can't find any clear - * definition of what might happen when such a signal - * is raised, but I think it must either cause the - * process to exit with an error or do nothing (and - * presumably leave the destination untouched). - * - * So having initialised the destination to 0, we can - * assume that after the assignment it will either - * have the correct value or have a different value - * within the range of time_t. Then we just have to - * check it. - */ errno = 0; epochumax = strtoumax(epochstr, &endptr, 10); if (!isdigit((unsigned char)epochstr[0]) || endptr == epochstr || *endptr != '\0' || - errno == ERANGE) { + errno == ERANGE || + /* Limit ourselves to 2000 to 10000. */ + epochumax < 946684800 || epochumax >= 253402300800) { fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n"); return NULL; } - now = epochumax; - /* - * Check if the value fitted into time_t. If "now" is - * negative then it obviously didn't. If it's - * non-negative then converting back into a uintmax_t - * will not change the value, since all non-negative - * numbers that can be represented in any integer type - * can be represented in a uintmax_t. - */ - if (now < 0 || (uintmax_t)now != epochumax) { - fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n"); - return NULL; + epochumax -= 946684800; /* Rebase to 2000. */ + tm.tm_isdst = -1; + tm.tm_sec = epochumax % 60; + tm.tm_min = epochumax / 60 % 60; + tm.tm_hour = epochumax / 3600 % 24; + long day = epochumax / 86400; + tm.tm_wday = (day - 1) % 7; + int y = 2000 + (day / 146097) * 400; + day = day % 146097; + bool ly; + for (;;) { + ly = y % 400 ? y % 100 ? y % 4 ? false:true:false:true; + if (day < 365 + ly) break; + day -= 365 + ly; y++; } + tm.tm_year = y - 1900; + tm.tm_yday = day; + static int const mlc[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + static int const mll[] = {31,29,31,30,31,30,31,31,30,31,30,31}; + tm.tm_mon = 0; + for (;;) { + int md = (ly ? mll : mlc)[tm.tm_mon]; + if (day < md) break; + day -= md; tm.tm_mon++; + } + tm.tm_mday = day + 1; + timeptr = &tm; } else { - now = time(NULL); + time_t now = time(NULL); if (now == (time_t)-1) { fprintf(stderr, "Can't get current time\n"); return NULL; } - } - timeptr = gmtime(&now); - if (timeptr == NULL) { - fprintf(stderr, "Can't convert time to UTC\n"); - return NULL; + timeptr = gmtime(&now); + if (timeptr == NULL) { + fprintf(stderr, "Can't convert time to UTC\n"); + return NULL; + } + if (timeptr->tm_year >= 8100) { + fprintf(stderr, "Can't handle years past 9999\n"); + return NULL; + } } timestr = asctime(timeptr); assert(strlen(timestr) == 25);