309 lines
6.3 KiB
C
Executable file
309 lines
6.3 KiB
C
Executable file
/* archive.c - archive support Author: Kees J. Bot
|
|
* 13 Nov 1993
|
|
*/
|
|
#include "h.h"
|
|
|
|
#ifdef unix
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#define arraysize(a) (sizeof(a) / sizeof((a)[0]))
|
|
#define arraylimit(a) ((a) + arraysize(a))
|
|
|
|
/* ASCII ar header. */
|
|
|
|
#define ASCII_ARMAG "!<arch>\n"
|
|
#define ASCII_SARMAG 8
|
|
#define ASCII_ARFMAG "`\n"
|
|
|
|
struct ascii_ar_hdr {
|
|
char ar_name[16];
|
|
char ar_date[12];
|
|
char ar_uid[6];
|
|
char ar_gid[6];
|
|
char ar_mode[8];
|
|
char ar_size[10];
|
|
char ar_fmag[2];
|
|
};
|
|
|
|
/* ACK ar header. */
|
|
|
|
#define ACK_ARMAG 0177545
|
|
#define ACK_AALMAG 0177454
|
|
|
|
struct ack_ar_hdr {
|
|
char ar_name[14];
|
|
unsigned long ar_date;
|
|
unsigned char ar_uid;
|
|
unsigned char ar_gid;
|
|
unsigned short ar_mode;
|
|
unsigned long ar_size;
|
|
};
|
|
|
|
typedef struct archname {
|
|
struct archname *next; /* Next on the hash chain. */
|
|
char name[16]; /* One archive entry. */
|
|
time_t date; /* The timestamp. */
|
|
/* (no need for other attibutes) */
|
|
} archname_t;
|
|
|
|
static size_t namelen; /* Max name length, 14 or 16. */
|
|
|
|
#define HASHSIZE (64 << sizeof(int))
|
|
|
|
static archname_t *nametab[HASHSIZE];
|
|
|
|
_PROTOTYPE( static int hash, (char *name) );
|
|
_PROTOTYPE( static int searchtab, (char *name, time_t *date, int scan) );
|
|
_PROTOTYPE( static void deltab, (void) );
|
|
_PROTOTYPE( static long ar_atol, (char *s, size_t n) );
|
|
_PROTOTYPE( static int read_ascii_archive, (int afd) );
|
|
_PROTOTYPE( static int read_ack_archive, (int afd) );
|
|
|
|
static char *lpar, *rpar; /* Leave these at '(' and ')'. */
|
|
|
|
int is_archive_ref(name) char *name;
|
|
/* True if name is of the form "archive(file)". */
|
|
{
|
|
char *p = name;
|
|
|
|
while (*p != 0 && *p != '(' && *p != ')') p++;
|
|
lpar = p;
|
|
if (*p++ != '(') return 0;
|
|
|
|
while (*p != 0 && *p != '(' && *p != ')') p++;
|
|
rpar = p;
|
|
if (*p++ != ')') return 0;
|
|
|
|
return *p == 0;
|
|
}
|
|
|
|
static int hash(name) char *name;
|
|
/* Compute a hash value out of a name. */
|
|
{
|
|
unsigned h = 0;
|
|
unsigned char *p = (unsigned char *) name;
|
|
int n = namelen;
|
|
|
|
while (*p != 0) {
|
|
h = h * 0x1111 + *p++;
|
|
if (--n == 0) break;
|
|
}
|
|
|
|
return h % arraysize(nametab);
|
|
}
|
|
|
|
static int searchtab(name, date, scan) char *name; time_t *date; int scan;
|
|
/* Enter a name to the table, or return the date of one already there. */
|
|
{
|
|
archname_t **pnp, *np;
|
|
int cmp = 1;
|
|
|
|
pnp = &nametab[hash(name)];
|
|
|
|
while ((np = *pnp) != NULL
|
|
&& (cmp = strncmp(name, np->name, namelen)) > 0) {
|
|
pnp= &np->next;
|
|
}
|
|
|
|
if (cmp != 0) {
|
|
if (scan) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
if ((np = (archname_t *) malloc(sizeof(*np))) == NULL)
|
|
fatal("No memory for archive name cache",(char *)0,0);
|
|
strncpy(np->name, name, namelen);
|
|
np->date = *date;
|
|
np->next = *pnp;
|
|
*pnp = np;
|
|
}
|
|
if (scan) *date = np->date;
|
|
return 0;
|
|
}
|
|
|
|
static void deltab()
|
|
/* Delete the name cache, a different library is to be read. */
|
|
{
|
|
archname_t **pnp, *np, *junk;
|
|
|
|
for (pnp = nametab; pnp < arraylimit(nametab); pnp++) {
|
|
for (np = *pnp; np != NULL; ) {
|
|
junk = np;
|
|
np = np->next;
|
|
free(junk);
|
|
}
|
|
*pnp = NULL;
|
|
}
|
|
}
|
|
|
|
static long ar_atol(s, n) char *s; size_t n;
|
|
/* Transform a string into a number. Ignore the space padding. */
|
|
{
|
|
long l= 0;
|
|
|
|
while (n > 0) {
|
|
if (*s != ' ') l= l * 10 + (*s - '0');
|
|
s++;
|
|
n--;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
static int read_ascii_archive(afd)
|
|
int afd;
|
|
/* Read a modern ASCII type archive. */
|
|
{
|
|
struct ascii_ar_hdr hdr;
|
|
off_t pos= 8;
|
|
char *p;
|
|
time_t date;
|
|
|
|
namelen = 16;
|
|
|
|
for (;;) {
|
|
if (lseek(afd, pos, SEEK_SET) == -1) return -1;
|
|
|
|
switch (read(afd, &hdr, sizeof(hdr))) {
|
|
case sizeof(hdr):
|
|
break;
|
|
case -1:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (strncmp(hdr.ar_fmag, ASCII_ARFMAG, sizeof(hdr.ar_fmag)) != 0) {
|
|
errno= EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* Strings are space padded! */
|
|
for (p= hdr.ar_name; p < hdr.ar_name + sizeof(hdr.ar_name); p++) {
|
|
if (*p == ' ') {
|
|
*p= 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add a file to the cache. */
|
|
date = ar_atol(hdr.ar_date, sizeof(hdr.ar_date));
|
|
searchtab(hdr.ar_name, &date, 0);
|
|
|
|
pos+= sizeof(hdr) + ar_atol(hdr.ar_size, sizeof(hdr.ar_size));
|
|
pos= (pos + 1) & (~ (off_t) 1);
|
|
}
|
|
}
|
|
|
|
static int read_ack_archive(afd)
|
|
int afd;
|
|
/* Read an ACK type archive. */
|
|
{
|
|
unsigned char raw_hdr[14 + 4 + 1 + 1 + 2 + 4];
|
|
struct ack_ar_hdr hdr;
|
|
off_t pos= 2;
|
|
time_t date;
|
|
|
|
namelen = 14;
|
|
|
|
for (;;) {
|
|
if (lseek(afd, pos, SEEK_SET) == -1) return -1;
|
|
|
|
switch (read(afd, raw_hdr, sizeof(raw_hdr))) {
|
|
case sizeof(raw_hdr):
|
|
break;
|
|
case -1:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Copy the useful fields from the raw bytes transforming PDP-11
|
|
* style numbers to native format.
|
|
*/
|
|
memcpy(hdr.ar_name, raw_hdr + 0, 14);
|
|
hdr.ar_date= (long) raw_hdr[14 + 1] << 24
|
|
| (long) raw_hdr[14 + 0] << 16
|
|
| (long) raw_hdr[14 + 3] << 8
|
|
| (long) raw_hdr[14 + 2] << 0;
|
|
hdr.ar_size= (long) raw_hdr[22 + 1] << 24
|
|
| (long) raw_hdr[22 + 0] << 16
|
|
| (long) raw_hdr[22 + 3] << 8
|
|
| (long) raw_hdr[22 + 2] << 0;
|
|
|
|
/* Add a file to the cache. */
|
|
date = hdr.ar_date;
|
|
searchtab(hdr.ar_name, &date, 0);
|
|
|
|
pos= (pos + 26 + hdr.ar_size + 1) & (~ (off_t) 1);
|
|
}
|
|
}
|
|
|
|
int archive_stat(name, stp) char *name; struct stat *stp;
|
|
/* Search an archive for a file and return that file's stat info. */
|
|
{
|
|
int afd;
|
|
int r= -1;
|
|
char magic[8];
|
|
char *file;
|
|
static dev_t ardev;
|
|
static ino_t arino = 0;
|
|
static time_t armtime;
|
|
|
|
if (!is_archive_ref(name)) { errno = EINVAL; return -1; }
|
|
*lpar= 0;
|
|
*rpar= 0;
|
|
file= lpar + 1;
|
|
|
|
if (stat(name, stp) < 0) goto bail_out;
|
|
|
|
if (stp->st_ino != arino || stp->st_dev != ardev) {
|
|
/* Either the first (and probably only) library, or a different
|
|
* library.
|
|
*/
|
|
arino = stp->st_ino;
|
|
ardev = stp->st_dev;
|
|
armtime = stp->st_mtime;
|
|
deltab();
|
|
|
|
if ((afd= open(name, O_RDONLY)) < 0) goto bail_out;
|
|
|
|
switch (read(afd, magic, sizeof(magic))) {
|
|
case 8:
|
|
if (strncmp(magic, ASCII_ARMAG, 8) == 0) {
|
|
r= read_ascii_archive(afd);
|
|
break;
|
|
}
|
|
if ((magic[0] & 0xFF) == ((ACK_AALMAG >> 0) & 0xFF)
|
|
&& (magic[1] & 0xFF) == ((ACK_AALMAG >> 8) & 0xFF)
|
|
) {
|
|
r= read_ack_archive(afd);
|
|
break;
|
|
}
|
|
/*FALL THROUGH*/
|
|
default:
|
|
errno = EINVAL;
|
|
/*FALL THROUGH*/
|
|
case -1:
|
|
/* r= -1 */;
|
|
}
|
|
{ int e= errno; close(afd); errno= e; }
|
|
} else {
|
|
/* Library is cached. */
|
|
r = 0;
|
|
}
|
|
|
|
if (r == 0) {
|
|
/* Search the cache. */
|
|
r = searchtab(file, &stp->st_mtime, 1);
|
|
if (stp->st_mtime > armtime) stp->st_mtime = armtime;
|
|
}
|
|
|
|
bail_out:
|
|
/* Repair the name(file) thing. */
|
|
*lpar= '(';
|
|
*rpar= ')';
|
|
return r;
|
|
}
|
|
#endif
|