#ifndef lint
#if 0
static char sccsid[] = "@(#)archive.c 8.3 (Berkeley) 4/2/94";
static char rcsid[] = "$NetBSD: archive.c,v 1.7 1995/03/26 03:27:46 glass Exp $";
#endif
#endif
#include <sys/param.h>
#include <sys/stat.h>
#include <ar.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mach-o/fat.h>
#include "archive.h"
#include "extern.h"
typedef struct ar_hdr HDR;
static char hb[sizeof(HDR) + 1];
int archive_opened_for_writing = 0;
int
open_archive(mode)
int mode;
{
int created, fd, nr, r;
char buf[SARMAG];
created = 0;
if (mode & O_CREAT) {
mode |= O_EXCL;
if ((fd = open(archive, mode, DEFFILEMODE)) >= 0) {
if (!(options & AR_C))
warnx("creating archive %s", archive);
created = 1;
goto opened;
}
if (errno != EEXIST)
error(archive);
mode &= ~O_EXCL;
}
if ((fd = open(archive, mode, DEFFILEMODE)) < 0)
error(archive);
if((mode & O_ACCMODE) == O_RDONLY)
goto skip_flock;
opened:
r = flock(fd, LOCK_EX|LOCK_NB);
if (r && errno == EAGAIN) {
static int tries = 0;
sleep(1);
tries++;
if (tries < 10)
goto opened;
}
if (r) {
switch (errno)
{
case EINTR:
case EACCES:
case EBADF:
case EMFILE:
case EINVAL:
case ESRCH:
case EAGAIN:
case EFAULT:
case EROFS:
case EOVERFLOW:
case EFBIG:
case EISDIR:
case EDEADLK:
case ESTALE:
error(archive);
break;
case ENOLCK:
case ENOTSUP:
case EHOSTUNREACH:
case EBADRPC:
default:
break;
}
}
skip_flock:
if (!created &&
((mode & O_ACCMODE) == O_RDONLY || (mode & O_ACCMODE) == O_RDWR)) {
if ((nr = read(fd, buf, SARMAG) != SARMAG)) {
if (nr >= 0)
badfmt();
error(archive);
} else if (bcmp(buf, ARMAG, SARMAG)) {
uint32_t magic;
memcpy(&magic, buf, sizeof(uint32_t));
#ifdef __BIG_ENDIAN__
if(magic == FAT_MAGIC)
#endif
#ifdef __LITTLE_ENDIAN__
if(magic == FAT_CIGAM)
#endif
fprintf(stderr, "ar: %s is a fat file (use "
"libtool(1) or lipo(1) and ar(1) on "
"it)\n", archive);
badfmt();
}
} else if (write(fd, ARMAG, SARMAG) != SARMAG)
error(archive);
if ((mode & O_ACCMODE) == O_RDWR)
archive_opened_for_writing = 1;
return (fd);
}
void
close_archive(fd)
int fd;
{
(void)close(fd);
}
#define AR_ATOI(from, to, len, base) { \
memmove(buf, from, len); \
buf[len] = '\0'; \
to = strtol(buf, (char **)NULL, base); \
}
int
get_arobj(fd)
int fd;
{
struct ar_hdr *hdr;
int len, nr;
char *p, buf[20];
nr = read(fd, hb, sizeof(HDR));
if (nr != sizeof(HDR)) {
if (!nr)
return (0);
if (nr < 0)
error(archive);
badfmt();
}
hdr = (struct ar_hdr *)hb;
if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1))
badfmt();
#define DECIMAL 10
#define OCTAL 8
AR_ATOI(hdr->ar_date, chdr.date, sizeof(hdr->ar_date), DECIMAL);
AR_ATOI(hdr->ar_uid, chdr.uid, sizeof(hdr->ar_uid), DECIMAL);
AR_ATOI(hdr->ar_gid, chdr.gid, sizeof(hdr->ar_gid), DECIMAL);
AR_ATOI(hdr->ar_mode, chdr.mode, sizeof(hdr->ar_mode), OCTAL);
AR_ATOI(hdr->ar_size, chdr.size, sizeof(hdr->ar_size), DECIMAL);
if (hdr->ar_name[0] == ' ')
badfmt();
if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) {
chdr.lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1);
if (len <= 0 || len > MAXNAMLEN)
badfmt();
nr = read(fd, chdr.name, len);
if (nr != len) {
if (nr < 0)
error(archive);
badfmt();
}
chdr.name[len] = 0;
chdr.size -= len;
} else {
chdr.lname = 0;
memmove(chdr.name, hdr->ar_name, sizeof(hdr->ar_name));
for (p = chdr.name + sizeof(hdr->ar_name) - 1; *p == ' '; --p);
*++p = '\0';
}
return (1);
}
static int already_written;
void
put_arobj(cfp, sb)
CF *cfp;
struct stat *sb;
{
unsigned int lname;
char *name;
struct ar_hdr *hdr;
off_t size;
long int tv_sec;
if (sb) {
name = rname(cfp->rname);
(void)fstat(cfp->rfd, sb);
if (getenv("ZERO_AR_DATE") == NULL)
tv_sec = (long int)sb->st_mtimespec.tv_sec;
else
tv_sec = (long int)0;
lname = strlen(name);
if (options & AR_TR) {
if (lname > OLDARMAXNAME) {
(void)fflush(stdout);
warnx("warning: %s truncated to %.*s",
name, OLDARMAXNAME, name);
(void)fflush(stderr);
}
(void)sprintf(hb, HDR3, name, (long int)tv_sec,
(unsigned int)(u_short)sb->st_uid,
(unsigned int)(u_short)sb->st_gid,
sb->st_mode, sb->st_size, ARFMAG);
lname = 0;
} else if (lname > sizeof(hdr->ar_name) || strchr(name, ' '))
(void)sprintf(hb, HDR1, AR_EFMT1, (lname + 3) & ~3,
(long int)tv_sec,
(unsigned int)(u_short)sb->st_uid,
(unsigned int)(u_short)sb->st_gid,
sb->st_mode, sb->st_size + ((lname + 3) & ~3),
ARFMAG);
else {
lname = 0;
(void)sprintf(hb, HDR2, name, (long int)tv_sec,
(unsigned int)(u_short)sb->st_uid,
(unsigned int)(u_short)sb->st_gid,
sb->st_mode, sb->st_size, ARFMAG);
}
size = sb->st_size;
} else {
lname = chdr.lname;
name = chdr.name;
size = chdr.size;
}
if (write(cfp->wfd, hb, sizeof(HDR)) != sizeof(HDR))
error(cfp->wname);
if (lname) {
if (write(cfp->wfd, name, lname) != (int)lname)
error(cfp->wname);
already_written = lname;
if ((lname % 4) != 0) {
static char pad[3] = "\0\0\0";
if (write(cfp->wfd, pad, 4-(lname%4)) !=
(int)(4-(lname%4)))
error(cfp->wname);
already_written += 4 - (lname % 4);
}
}
copy_ar(cfp, size);
already_written = 0;
}
void
copy_ar(cfp, size)
CF *cfp;
off_t size;
{
static char pad = '\n';
off_t sz;
int from, nr, nw, off, to;
char buf[8*1024];
nr = 0;
if (!(sz = size))
return;
from = cfp->rfd;
to = cfp->wfd;
sz = size;
while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) {
sz -= nr;
for (off = 0; off < nr; nr -= off, off += nw)
if ((nw = write(to, buf + off, nr)) < 0)
error(cfp->wname);
}
if (sz) {
if (nr == 0)
badfmt();
error(cfp->rname);
}
if (cfp->flags & RPAD && (size + chdr.lname) & 1 &&
(nr = read(from, buf, 1)) != 1) {
if (nr == 0)
badfmt();
error(cfp->rname);
}
if (cfp->flags & WPAD && (size + already_written) & 1 &&
write(to, &pad, 1) != 1)
error(cfp->wname);
}
void
skip_arobj(fd)
int fd;
{
off_t len;
len = chdr.size + ((chdr.size + chdr.lname) & 1);
if (lseek(fd, len, SEEK_CUR) == (off_t)-1)
error(archive);
}