/*
* ar - portable (ascii) format version
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include <ar.h>
/*
* The algorithm uses up to 3 temp files. The "pivot member" is the
* archive member specified by and a, b, or i option. The temp files are
* astart - contains existing members up to and including the pivot member.
* amiddle - contains new files moved or inserted behind the pivot.
* aend - contains the existing members that follow the pivot member.
* When all members have been processed, function 'install' streams the
* temp files, in order, back into the archive.
*/
typedef struct Arsymref
{
char *name;
int type;
int len;
vlong offset;
struct Arsymref *next;
} Arsymref;
typedef struct Armember /* Temp file entry - one per archive member */
{
struct Armember *next;
struct ar_hdr hdr;
long size;
long date;
void *member;
} Armember;
typedef struct Arfile /* Temp file control block - one per tempfile */
{
int paged; /* set when some data paged to disk */
char *fname; /* paging file name */
int fd; /* paging file descriptor */
vlong size;
Armember *head; /* head of member chain */
Armember *tail; /* tail of member chain */
Arsymref *sym; /* head of defined symbol chain */
} Arfile;
int aflag; /* command line flags */
int bflag;
int cflag;
int oflag;
int uflag;
int vflag;
Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */
int allobj = 1; /* set when all members are object files of the same type */
int symdefsize; /* size of symdef file */
int dupfound; /* flag for duplicate symbol */
Hashchain *hash[NHASH]; /* hash table of text symbols */
#define ARNAMESIZE sizeof(astart->tail->hdr.name)
char poname[ARNAMESIZE+1]; /* name of pivot member */
char *file; /* current file or member being worked on */
Biobuf bout;
Biobuf bar;
if (count == 0)
return;
fd = openar(arname, ORDWR, 0);
Binit(&bar, fd, OREAD);
Bseek(&bar,seek(fd,0,1), 1);
astart = newtempfile(artemp);
amiddle = newtempfile(movtemp);
aend = 0;
ap = astart;
for (i = 0; bp = getdir(&bar); i++) {
if (bamatch(file, poname)) {
aend = newtempfile(tailtemp);
ap = aend;
}
if(match(count, files)) {
mesg('m', file);
scanobj(&bar, amiddle, bp->size);
arcopy(&bar, amiddle, bp);
} else
/*
* pitch the symdef file if it is at the beginning
* of the archive and we aren't inserting in front
* of it (ap == astart).
*/
if (ap == astart && i == 0 && strcmp(file, symdef) == 0)
skip(&bar, bp->size);
else {
scanobj(&bar, ap, bp->size);
arcopy(&bar, ap, bp);
}
}
close(fd);
if (poname[0] && aend == 0)
fprint(2, "ar: %s not found - files moved to end.\n", poname);
install(arname, astart, amiddle, aend, 0);
}
void
tcmd(char *arname, int count, char **files)
{
int fd;
Armember *bp;
char name[ARNAMESIZE+1];
/*
* extract the symbol references from an object file
*/
void
scanobj(Biobuf *b, Arfile *ap, long size)
{
int obj;
vlong offset;
Dir *d;
static int lastobj = -1;
if (!allobj) /* non-object file encountered */
return;
offset = Boffset(b);
obj = objtype(b, 0);
if (obj < 0) { /* not an object file */
allobj = 0;
d = dirfstat(Bfildes(b));
if (d != nil && d->length == 0)
fprint(2, "ar: zero length file %s\n", file);
free(d);
Bseek(b, offset, 0);
return;
}
if (lastobj >= 0 && obj != lastobj) {
fprint(2, "ar: inconsistent object file %s\n", file);
allobj = 0;
Bseek(b, offset, 0);
return;
}
lastobj = obj;
if (!readar(b, obj, offset+size, 0)) {
fprint(2, "ar: invalid symbol reference in file %s\n", file);
allobj = 0;
Bseek(b, offset, 0);
return;
}
Bseek(b, offset, 0);
objtraverse(objsym, ap);
}
/*
* add text and data symbols to the symbol list
*/
void
objsym(Sym *s, void *p)
{
int n;
Arsymref *as;
Arfile *ap;
/*
* compare the current member to the name of the pivot member
*/
int
bamatch(char *file, char *pivot)
{
static int state = 0;
switch(state)
{
case 0: /* looking for position file */
if (aflag) {
if (strncmp(file, pivot, ARNAMESIZE) == 0)
state = 1;
} else if (bflag) {
if (strncmp(file, pivot, ARNAMESIZE) == 0) {
state = 2; /* found */
return 1;
}
}
break;
case 1: /* found - after previous file */
state = 2;
return 1;
case 2: /* already found position file */
break;
}
return 0;
}
/*
* output a message, if 'v' option was specified
*/
void
mesg(int c, char *file)
{
if(vflag)
Bprint(&bout, "%c - %s\n", c, file);
}
/*
* isolate file name by stripping leading directories and trailing slashes
*/
void
trim(char *s, char *buf, int n)
{
char *p;
for(;;) {
p = strrchr(s, '/');
if (!p) { /* no slash in name */
strncpy(buf, s, n);
return;
}
if (p[1] != 0) { /* p+1 is first char of file name */
strncpy(buf, p+1, n);
return;
}
*p = 0; /* strip trailing slash */
}
}
n = *ap++;
while(--n>=0 && (mode&*ap++)==0)
ap++;
Bputc(&bout, *ap);
}
/*
* Temp file I/O subsystem. We attempt to cache all three temp files in
* core. When we run out of memory we spill to disk.
* The I/O model assumes that temp files:
* 1) are only written on the end
* 2) are only read from the beginning
* 3) are only read after all writing is complete.
* The architecture uses one control block per temp file. Each control
* block anchors a chain of buffers, each containing an archive member.
*/
Arfile *
newtempfile(char *name) /* allocate a file control block */
{
Arfile *ap;
Armember *
newmember(void) /* allocate a member buffer */
{
return (Armember *)armalloc(sizeof(Armember));
}
void
arread(Biobuf *b, Armember *bp, int n) /* read an image into a member buffer */
{
int i;
bp->member = armalloc(n);
i = Bread(b, bp->member, n);
if (i < 0) {
free(bp->member);
bp->member = 0;
rderr();
}
}
/*
* insert a member buffer into the member chain
*/
void
arinsert(Arfile *ap, Armember *bp)
{
bp->next = 0;
if (!ap->tail)
ap->head = bp;
else
ap->tail->next = bp;
ap->tail = bp;
}
/*
* stream the members in a temp file to the file referenced by 'fd'.
*/
void
arstream(int fd, Arfile *ap)
{
Armember *bp;
int i;
char buf[8192];
if (ap->paged) { /* copy from disk */
seek(ap->fd, 0, 0);
for (;;) {
i = read(ap->fd, buf, sizeof(buf));
if (i < 0)
rderr();
if (i == 0)
break;
if (write(fd, buf, i) != i)
wrerr();
}
close(ap->fd);
ap->paged = 0;
}
/* dump the in-core buffers */
for (bp = ap->head; bp; bp = bp->next) {
if (!arwrite(fd, bp))
wrerr();
}
}
/*
* write a member to 'fd'.
*/
int
arwrite(int fd, Armember *bp)
{
int len;
if(HEADER_IO(write, fd, bp->hdr))
return 0;
len = bp->size;
if (len & 01)
len++;
if (write(fd, bp->member, len) != len)
return 0;
return 1;
}
/*
* Spill a member to a disk copy of a temp file
*/
int
page(Arfile *ap)
{
Armember *bp;
bp = ap->head;
if (!ap->paged) { /* not yet paged - create file */
ap->fname = mktemp(ap->fname);
ap->fd = create(ap->fname, ORDWR|ORCLOSE, 0600);
if (ap->fd < 0) {
fprint(2,"ar: can't create temp file\n");
return 0;
}
ap->paged = 1;
}
if (!arwrite(ap->fd, bp)) /* write member and free buffer block */
return 0;
ap->head = bp->next;
if (ap->tail == bp)
ap->tail = bp->next;
free(bp->member);
free(bp);
return 1;
}
/*
* try to reclaim space by paging. we try to spill the start, middle,
* and end files, in that order. there is no particular reason for the
* ordering.
*/
int
getspace(void)
{
if (astart && astart->head && page(astart))
return 1;
if (amiddle && amiddle->head && page(amiddle))
return 1;
if (aend && aend->head && page(aend))
return 1;
return 0;
}
void
arfree(Arfile *ap) /* free a member buffer */
{
Armember *bp, *next;
for (bp = ap->head; bp; bp = next) {
next = bp->next;
if (bp->member)
free(bp->member);
free(bp);
}
free(ap);
}
/*
* allocate space for a control block or member buffer. if the malloc
* fails we try to reclaim space by spilling previously allocated
* member buffers.
*/
char *
armalloc(int n)
{
char *cp;
do {
cp = malloc(n);
if (cp) {
memset(cp, 0, n);
return cp;
}
} while (getspace());
fprint(2, "ar: out of memory\n");
exits("malloc");
return 0;
}