kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

modtime.c (4481B)


      1 /*
      2  * Modification times of files and archive members. Ported from pdpmake
      3  * modtime.c. File mtimes come from the injected host->mtime callback; archive
      4  * member timestamps are parsed from the archive bytes read through
      5  * KitContext.file_io (pdpmake used <ar.h> + fopen/fread/fseek). Part of the
      6  * src/api/make.c amalgamation.
      7  */
      8 #include "make.h"
      9 
     10 /* ar(5) constants (System V / GNU format). */
     11 #define MK_SARMAG 8
     12 enum { AR_NAME = 0, AR_DATE = 16, AR_SIZE = 48, AR_FMAG = 58, AR_HDRLEN = 60 };
     13 
     14 /*
     15  * Read a right-blank-padded decimal number from an archive header field.
     16  */
     17 static size_t mk_argetnum(MakeCtx* mc, const char* str, int len) {
     18   const char* s;
     19   size_t val = 0;
     20   for (s = str; s < str + len && isdigit((unsigned char)*s); s++) {
     21     if (val > (size_t)(INT_MAX - 1) / 10) break;
     22     val = val * 10 + (size_t)(*s - '0');
     23   }
     24   if (s != str + len && *s != ' ') mk_error(mc, "invalid archive");
     25   return val;
     26 }
     27 
     28 /*
     29  * Search a System V/GNU archive image for the given member and return its
     30  * timestamp (seconds), or 0 if not found.
     31  */
     32 static int64_t mk_arsearch(MakeCtx* mc, const uint8_t* data, size_t len,
     33                            const char* member) {
     34   size_t pos = MK_SARMAG;
     35   const char* names = NULL;
     36   size_t names_len = 0;
     37   size_t memlen = strlen(member);
     38 
     39   while (pos + AR_HDRLEN <= len) {
     40     const char* hdr = (const char*)data + pos;
     41     size_t msize;
     42     const char* t;
     43     size_t namelim;
     44     size_t nl;
     45 
     46     if (memcmp(hdr + AR_FMAG, "`\n", 2) != 0) mk_error(mc, "invalid archive");
     47     msize = mk_argetnum(mc, hdr + AR_SIZE, 10);
     48     pos += AR_HDRLEN;
     49     if (msize > len - pos) msize = len - pos; /* clamp to buffer */
     50 
     51     t = hdr + AR_NAME;
     52     namelim = 16;
     53     if (hdr[AR_NAME] == '/') {
     54       if (hdr[AR_NAME + 1] == ' ') {
     55         /* Symbol table: skip. */
     56         pos += msize + (msize & 1);
     57         continue;
     58       } else if (hdr[AR_NAME + 1] == '/' && names == NULL) {
     59         /* Extended filename table: remember it for later. */
     60         names = (const char*)data + pos;
     61         names_len = msize;
     62         pos += msize + (msize & 1);
     63         continue;
     64       } else if (isdigit((unsigned char)hdr[AR_NAME + 1]) && names) {
     65         size_t off = mk_argetnum(mc, hdr + AR_NAME + 1, 15);
     66         if (off > names_len) mk_error(mc, "invalid archive");
     67         t = names + off;
     68         namelim = names_len - off;
     69       } else {
     70         mk_error(mc, "invalid archive");
     71       }
     72     }
     73 
     74     /* The member name runs up to the terminating '/'. */
     75     nl = 0;
     76     while (nl < namelim && t[nl] != '/') nl++;
     77     if (nl == namelim) mk_error(mc, "invalid archive");
     78 
     79     if (nl == memlen && memcmp(t, member, memlen) == 0)
     80       return (int64_t)mk_argetnum(mc, hdr + AR_DATE, 12);
     81 
     82     pos += msize + (msize & 1);
     83   }
     84   return 0;
     85 }
     86 
     87 static int64_t mk_artime(MakeCtx* mc, const char* archive, const char* member) {
     88   KitFileData fd;
     89   const KitFileIO* io = mc->ctx->file_io;
     90   int64_t mtime;
     91 
     92   fd.data = NULL;
     93   fd.size = 0;
     94   fd.token = NULL;
     95   if (!io || io->read_all(io->user, archive, &fd) != KIT_OK) return 0;
     96   if (fd.size < MK_SARMAG ||
     97       memcmp(fd.data, "!<arch>\n", MK_SARMAG) != 0) {
     98     if (io->release) io->release(io->user, &fd);
     99     mk_error(mc, "%s: not an archive", archive);
    100   }
    101   mtime = mk_arsearch(mc, fd.data, fd.size, member);
    102   if (io->release) io->release(io->user, &fd);
    103   return mtime;
    104 }
    105 
    106 /*
    107  * If name is 'libname(member.o)' split it into name and member parts; else copy
    108  * name and leave *member alone. The return value is an arena string.
    109  */
    110 static char* mk_splitlib(MakeCtx* mc, const char* name, char** member) {
    111   char* s;
    112   char* t;
    113   size_t len;
    114 
    115   t = mk_strdup(mc, name);
    116   s = strchr(t, '(');
    117   if (s) {
    118     *s++ = '\0';
    119     len = strlen(s);
    120     if (len <= 1 || s[len - 1] != ')' || *t == '\0')
    121       mk_error(mc, "invalid name '%s'", name);
    122     s[len - 1] = '\0';
    123     *member = s;
    124   }
    125   return t;
    126 }
    127 
    128 /*
    129  * Get the modification time of a file (or archive member). Set it to 0 if the
    130  * file doesn't exist.
    131  */
    132 static void mk_modtime(MakeCtx* mc, struct name* np) {
    133   char* name;
    134   char* member = NULL;
    135   int64_t ns;
    136 
    137   name = mk_splitlib(mc, np->n_name, &member);
    138   if (member) {
    139     np->n_tim.tv_sec = mk_artime(mc, mk_path(mc, name), member);
    140     np->n_tim.tv_nsec = 0;
    141   } else if (mc->host->mtime(mc->host->user, mk_path(mc, name), &ns) == 0) {
    142     np->n_tim.tv_sec = ns / 1000000000;
    143     np->n_tim.tv_nsec = ns % 1000000000;
    144   } else {
    145     np->n_tim.tv_sec = 0;
    146     np->n_tim.tv_nsec = 0;
    147   }
    148 }