[9] | 1 | /* archive.c - archive support Author: Kees J. Bot
|
---|
| 2 | * 13 Nov 1993
|
---|
| 3 | */
|
---|
| 4 | #include "h.h"
|
---|
| 5 |
|
---|
| 6 | #ifdef unix
|
---|
| 7 |
|
---|
| 8 | #include <unistd.h>
|
---|
| 9 | #include <fcntl.h>
|
---|
| 10 |
|
---|
| 11 | #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
|
---|
| 12 | #define arraylimit(a) ((a) + arraysize(a))
|
---|
| 13 |
|
---|
| 14 | /* ASCII ar header. */
|
---|
| 15 |
|
---|
| 16 | #define ASCII_ARMAG "!<arch>\n"
|
---|
| 17 | #define ASCII_SARMAG 8
|
---|
| 18 | #define ASCII_ARFMAG "`\n"
|
---|
| 19 |
|
---|
| 20 | struct ascii_ar_hdr {
|
---|
| 21 | char ar_name[16];
|
---|
| 22 | char ar_date[12];
|
---|
| 23 | char ar_uid[6];
|
---|
| 24 | char ar_gid[6];
|
---|
| 25 | char ar_mode[8];
|
---|
| 26 | char ar_size[10];
|
---|
| 27 | char ar_fmag[2];
|
---|
| 28 | };
|
---|
| 29 |
|
---|
| 30 | /* ACK ar header. */
|
---|
| 31 |
|
---|
| 32 | #define ACK_ARMAG 0177545
|
---|
| 33 | #define ACK_AALMAG 0177454
|
---|
| 34 |
|
---|
| 35 | struct ack_ar_hdr {
|
---|
| 36 | char ar_name[14];
|
---|
| 37 | unsigned long ar_date;
|
---|
| 38 | unsigned char ar_uid;
|
---|
| 39 | unsigned char ar_gid;
|
---|
| 40 | unsigned short ar_mode;
|
---|
| 41 | unsigned long ar_size;
|
---|
| 42 | };
|
---|
| 43 |
|
---|
| 44 | typedef struct archname {
|
---|
| 45 | struct archname *next; /* Next on the hash chain. */
|
---|
| 46 | char name[16]; /* One archive entry. */
|
---|
| 47 | time_t date; /* The timestamp. */
|
---|
| 48 | /* (no need for other attibutes) */
|
---|
| 49 | } archname_t;
|
---|
| 50 |
|
---|
| 51 | static size_t namelen; /* Max name length, 14 or 16. */
|
---|
| 52 |
|
---|
| 53 | #define HASHSIZE (64 << sizeof(int))
|
---|
| 54 |
|
---|
| 55 | static archname_t *nametab[HASHSIZE];
|
---|
| 56 |
|
---|
| 57 | _PROTOTYPE( static int hash, (char *name) );
|
---|
| 58 | _PROTOTYPE( static int searchtab, (char *name, time_t *date, int scan) );
|
---|
| 59 | _PROTOTYPE( static void deltab, (void) );
|
---|
| 60 | _PROTOTYPE( static long ar_atol, (char *s, size_t n) );
|
---|
| 61 | _PROTOTYPE( static int read_ascii_archive, (int afd) );
|
---|
| 62 | _PROTOTYPE( static int read_ack_archive, (int afd) );
|
---|
| 63 |
|
---|
| 64 | static char *lpar, *rpar; /* Leave these at '(' and ')'. */
|
---|
| 65 |
|
---|
| 66 | int is_archive_ref(name) char *name;
|
---|
| 67 | /* True if name is of the form "archive(file)". */
|
---|
| 68 | {
|
---|
| 69 | char *p = name;
|
---|
| 70 |
|
---|
| 71 | while (*p != 0 && *p != '(' && *p != ')') p++;
|
---|
| 72 | lpar = p;
|
---|
| 73 | if (*p++ != '(') return 0;
|
---|
| 74 |
|
---|
| 75 | while (*p != 0 && *p != '(' && *p != ')') p++;
|
---|
| 76 | rpar = p;
|
---|
| 77 | if (*p++ != ')') return 0;
|
---|
| 78 |
|
---|
| 79 | return *p == 0;
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | static int hash(name) char *name;
|
---|
| 83 | /* Compute a hash value out of a name. */
|
---|
| 84 | {
|
---|
| 85 | unsigned h = 0;
|
---|
| 86 | unsigned char *p = (unsigned char *) name;
|
---|
| 87 | int n = namelen;
|
---|
| 88 |
|
---|
| 89 | while (*p != 0) {
|
---|
| 90 | h = h * 0x1111 + *p++;
|
---|
| 91 | if (--n == 0) break;
|
---|
| 92 | }
|
---|
| 93 |
|
---|
| 94 | return h % arraysize(nametab);
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | static int searchtab(name, date, scan) char *name; time_t *date; int scan;
|
---|
| 98 | /* Enter a name to the table, or return the date of one already there. */
|
---|
| 99 | {
|
---|
| 100 | archname_t **pnp, *np;
|
---|
| 101 | int cmp = 1;
|
---|
| 102 |
|
---|
| 103 | pnp = &nametab[hash(name)];
|
---|
| 104 |
|
---|
| 105 | while ((np = *pnp) != NULL
|
---|
| 106 | && (cmp = strncmp(name, np->name, namelen)) > 0) {
|
---|
| 107 | pnp= &np->next;
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | if (cmp != 0) {
|
---|
| 111 | if (scan) {
|
---|
| 112 | errno = ENOENT;
|
---|
| 113 | return -1;
|
---|
| 114 | }
|
---|
| 115 | if ((np = (archname_t *) malloc(sizeof(*np))) == NULL)
|
---|
| 116 | fatal("No memory for archive name cache",(char *)0,0);
|
---|
| 117 | strncpy(np->name, name, namelen);
|
---|
| 118 | np->date = *date;
|
---|
| 119 | np->next = *pnp;
|
---|
| 120 | *pnp = np;
|
---|
| 121 | }
|
---|
| 122 | if (scan) *date = np->date;
|
---|
| 123 | return 0;
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | static void deltab()
|
---|
| 127 | /* Delete the name cache, a different library is to be read. */
|
---|
| 128 | {
|
---|
| 129 | archname_t **pnp, *np, *junk;
|
---|
| 130 |
|
---|
| 131 | for (pnp = nametab; pnp < arraylimit(nametab); pnp++) {
|
---|
| 132 | for (np = *pnp; np != NULL; ) {
|
---|
| 133 | junk = np;
|
---|
| 134 | np = np->next;
|
---|
| 135 | free(junk);
|
---|
| 136 | }
|
---|
| 137 | *pnp = NULL;
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | static long ar_atol(s, n) char *s; size_t n;
|
---|
| 142 | /* Transform a string into a number. Ignore the space padding. */
|
---|
| 143 | {
|
---|
| 144 | long l= 0;
|
---|
| 145 |
|
---|
| 146 | while (n > 0) {
|
---|
| 147 | if (*s != ' ') l= l * 10 + (*s - '0');
|
---|
| 148 | s++;
|
---|
| 149 | n--;
|
---|
| 150 | }
|
---|
| 151 | return l;
|
---|
| 152 | }
|
---|
| 153 |
|
---|
| 154 | static int read_ascii_archive(afd)
|
---|
| 155 | int afd;
|
---|
| 156 | /* Read a modern ASCII type archive. */
|
---|
| 157 | {
|
---|
| 158 | struct ascii_ar_hdr hdr;
|
---|
| 159 | off_t pos= 8;
|
---|
| 160 | char *p;
|
---|
| 161 | time_t date;
|
---|
| 162 |
|
---|
| 163 | namelen = 16;
|
---|
| 164 |
|
---|
| 165 | for (;;) {
|
---|
| 166 | if (lseek(afd, pos, SEEK_SET) == -1) return -1;
|
---|
| 167 |
|
---|
| 168 | switch (read(afd, &hdr, sizeof(hdr))) {
|
---|
| 169 | case sizeof(hdr):
|
---|
| 170 | break;
|
---|
| 171 | case -1:
|
---|
| 172 | return -1;
|
---|
| 173 | default:
|
---|
| 174 | return 0;
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | if (strncmp(hdr.ar_fmag, ASCII_ARFMAG, sizeof(hdr.ar_fmag)) != 0) {
|
---|
| 178 | errno= EINVAL;
|
---|
| 179 | return -1;
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | /* Strings are space padded! */
|
---|
| 183 | for (p= hdr.ar_name; p < hdr.ar_name + sizeof(hdr.ar_name); p++) {
|
---|
| 184 | if (*p == ' ') {
|
---|
| 185 | *p= 0;
|
---|
| 186 | break;
|
---|
| 187 | }
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | /* Add a file to the cache. */
|
---|
| 191 | date = ar_atol(hdr.ar_date, sizeof(hdr.ar_date));
|
---|
| 192 | searchtab(hdr.ar_name, &date, 0);
|
---|
| 193 |
|
---|
| 194 | pos+= sizeof(hdr) + ar_atol(hdr.ar_size, sizeof(hdr.ar_size));
|
---|
| 195 | pos= (pos + 1) & (~ (off_t) 1);
|
---|
| 196 | }
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 | static int read_ack_archive(afd)
|
---|
| 200 | int afd;
|
---|
| 201 | /* Read an ACK type archive. */
|
---|
| 202 | {
|
---|
| 203 | unsigned char raw_hdr[14 + 4 + 1 + 1 + 2 + 4];
|
---|
| 204 | struct ack_ar_hdr hdr;
|
---|
| 205 | off_t pos= 2;
|
---|
| 206 | time_t date;
|
---|
| 207 |
|
---|
| 208 | namelen = 14;
|
---|
| 209 |
|
---|
| 210 | for (;;) {
|
---|
| 211 | if (lseek(afd, pos, SEEK_SET) == -1) return -1;
|
---|
| 212 |
|
---|
| 213 | switch (read(afd, raw_hdr, sizeof(raw_hdr))) {
|
---|
| 214 | case sizeof(raw_hdr):
|
---|
| 215 | break;
|
---|
| 216 | case -1:
|
---|
| 217 | return -1;
|
---|
| 218 | default:
|
---|
| 219 | return 0;
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | /* Copy the useful fields from the raw bytes transforming PDP-11
|
---|
| 223 | * style numbers to native format.
|
---|
| 224 | */
|
---|
| 225 | memcpy(hdr.ar_name, raw_hdr + 0, 14);
|
---|
| 226 | hdr.ar_date= (long) raw_hdr[14 + 1] << 24
|
---|
| 227 | | (long) raw_hdr[14 + 0] << 16
|
---|
| 228 | | (long) raw_hdr[14 + 3] << 8
|
---|
| 229 | | (long) raw_hdr[14 + 2] << 0;
|
---|
| 230 | hdr.ar_size= (long) raw_hdr[22 + 1] << 24
|
---|
| 231 | | (long) raw_hdr[22 + 0] << 16
|
---|
| 232 | | (long) raw_hdr[22 + 3] << 8
|
---|
| 233 | | (long) raw_hdr[22 + 2] << 0;
|
---|
| 234 |
|
---|
| 235 | /* Add a file to the cache. */
|
---|
| 236 | date = hdr.ar_date;
|
---|
| 237 | searchtab(hdr.ar_name, &date, 0);
|
---|
| 238 |
|
---|
| 239 | pos= (pos + 26 + hdr.ar_size + 1) & (~ (off_t) 1);
|
---|
| 240 | }
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | int archive_stat(name, stp) char *name; struct stat *stp;
|
---|
| 244 | /* Search an archive for a file and return that file's stat info. */
|
---|
| 245 | {
|
---|
| 246 | int afd;
|
---|
| 247 | int r= -1;
|
---|
| 248 | char magic[8];
|
---|
| 249 | char *file;
|
---|
| 250 | static dev_t ardev;
|
---|
| 251 | static ino_t arino = 0;
|
---|
| 252 | static time_t armtime;
|
---|
| 253 |
|
---|
| 254 | if (!is_archive_ref(name)) { errno = EINVAL; return -1; }
|
---|
| 255 | *lpar= 0;
|
---|
| 256 | *rpar= 0;
|
---|
| 257 | file= lpar + 1;
|
---|
| 258 |
|
---|
| 259 | if (stat(name, stp) < 0) goto bail_out;
|
---|
| 260 |
|
---|
| 261 | if (stp->st_ino != arino || stp->st_dev != ardev) {
|
---|
| 262 | /* Either the first (and probably only) library, or a different
|
---|
| 263 | * library.
|
---|
| 264 | */
|
---|
| 265 | arino = stp->st_ino;
|
---|
| 266 | ardev = stp->st_dev;
|
---|
| 267 | armtime = stp->st_mtime;
|
---|
| 268 | deltab();
|
---|
| 269 |
|
---|
| 270 | if ((afd= open(name, O_RDONLY)) < 0) goto bail_out;
|
---|
| 271 |
|
---|
| 272 | switch (read(afd, magic, sizeof(magic))) {
|
---|
| 273 | case 8:
|
---|
| 274 | if (strncmp(magic, ASCII_ARMAG, 8) == 0) {
|
---|
| 275 | r= read_ascii_archive(afd);
|
---|
| 276 | break;
|
---|
| 277 | }
|
---|
| 278 | if ((magic[0] & 0xFF) == ((ACK_AALMAG >> 0) & 0xFF)
|
---|
| 279 | && (magic[1] & 0xFF) == ((ACK_AALMAG >> 8) & 0xFF)
|
---|
| 280 | ) {
|
---|
| 281 | r= read_ack_archive(afd);
|
---|
| 282 | break;
|
---|
| 283 | }
|
---|
| 284 | /*FALL THROUGH*/
|
---|
| 285 | default:
|
---|
| 286 | errno = EINVAL;
|
---|
| 287 | /*FALL THROUGH*/
|
---|
| 288 | case -1:
|
---|
| 289 | /* r= -1 */;
|
---|
| 290 | }
|
---|
| 291 | { int e= errno; close(afd); errno= e; }
|
---|
| 292 | } else {
|
---|
| 293 | /* Library is cached. */
|
---|
| 294 | r = 0;
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | if (r == 0) {
|
---|
| 298 | /* Search the cache. */
|
---|
| 299 | r = searchtab(file, &stp->st_mtime, 1);
|
---|
| 300 | if (stp->st_mtime > armtime) stp->st_mtime = armtime;
|
---|
| 301 | }
|
---|
| 302 |
|
---|
| 303 | bail_out:
|
---|
| 304 | /* Repair the name(file) thing. */
|
---|
| 305 | *lpar= '(';
|
---|
| 306 | *rpar= ')';
|
---|
| 307 | return r;
|
---|
| 308 | }
|
---|
| 309 | #endif
|
---|