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 }