chiark / gitweb /
Support SOURCE_DATE_EPOCH
authorBen Harris <bjh21@bjh21.me.uk>
Sun, 10 Nov 2024 10:49:28 +0000 (10:49 +0000)
committerBen Harris <bjh21@bjh21.me.uk>
Thu, 14 Nov 2024 22:27:18 +0000 (22:27 +0000)
If set, it is used to set the "created" and "modified" fields in the
OpenType 'head' table.  This means that builds of Bedstead can be
reproducible.

The current code just casts the "long long" interpretation of the
environment variable into a time_t.  This is potentially undefined
behaviour, because time_t might be a signed integer type smaller than
"long long".  But I can't find a way to properly range-check it.  Even
in POSIX, where time_t is required to be an integer type, there
doesn't seem to be a constant that specifies its range.

bedstead.c

index 7522b6e3602cbf5c552209c20e2cfccc9adc5c91..104983b5e950f19ee2c49840ea948c5618ca2ddb 100644 (file)
@@ -99,6 +99,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -2730,14 +2731,34 @@ static char *
 time_for_ttx(void)
 {
        time_t now;
+       long long epochll;
        struct tm *timeptr;
-       char *timestr;
+       char *epochstr, *endptr, *timestr;
 
        /* Work out what timestamp to use. */
-       now = time(NULL);
-       if (now == (time_t)-1) {
-               fprintf(stderr, "Can't get current time\n");
-               return NULL;
+       if ((epochstr = getenv("SOURCE_DATE_EPOCH")) != NULL) {
+               /*
+                * Assume that SOURCE_DATE_EPOCH is set only on
+                * systems where time_t is also in seconds since the
+                * epoch.
+                */
+               epochll = strtoll(epochstr, &endptr, 10);
+               if (endptr == epochstr || *endptr != '\0' ||
+                   epochll == LLONG_MAX) {
+                       fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n");
+                       return NULL;
+               }
+               /*
+                * I can't find a way to range-check this assignment
+                * in standard C or even in POSIX.
+                */
+               now = epochll;
+       } else {
+               now = time(NULL);
+               if (now == (time_t)-1) {
+                       fprintf(stderr, "Can't get current time\n");
+                       return NULL;
+               }
        }
        timeptr = gmtime(&now);
        if (timeptr == NULL) {