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
|
---|