ref: 0ac77da6b080c9fdd74a036666ed560add8d3810
dir: /ar/main.c/
static char sccsid[] = "@(#) ./ar/main.c";
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stat.h>
#include "../inc/ar.h"
#include "../inc/arg.h"
#include "../inc/scc.h"
char *argv0;
static int bflag, iflag, vflag, cflag, lflag, uflag, aflag;
static char *posname, *tmpafile;
struct arop {
FILE *src;
FILE *dst;
struct ar_hdr hdr;
char *fname;
};
static void
cleanup(void)
{
if (tmpafile)
remove(tmpafile);
}
static void
sigfun(int signum)
{
cleanup();
exit(1);
}
static void
usage(void)
{
fputs("ar [-drqtpmx][posname] [-vuaibcl] [posname] afile name ...\n", stderr);
exit(1);
}
FILE *
openar(char *afile)
{
FILE *fp;
char magic[SARMAG+1];
struct stat st;
if ((fp = fopen(afile,"rb")) == NULL) {
if (!cflag)
fprintf(stderr, "ar: creating %s\n", afile);
if ((fp = fopen(afile, "w+b")) == NULL)
goto file_error;
fputs(ARMAG, fp);
fflush(fp);
} else {
if (fgets(magic, sizeof(magic), fp) == NULL)
goto file_error;
if (strcmp(magic, ARMAG)) {
fprintf(stderr,
"ar:%s:invalid magic number '%s'\n",
afile,
magic);
exit(1);
}
}
if (ferror(fp))
goto file_error;
return fp;
file_error:
perror("ar:opening archive");
exit(1);
}
static void
archieve(char *fname, FILE *to)
{
int c;
size_t n;
FILE *from;
char mtime[13];
struct stat st;
if (vflag)
printf("a - %s\n", fname);
if (strlen(fname) > 16)
fprintf(stderr, "ar:%s: too long name\n", fname);
if (stat(fname, &st) < 0) {
fprintf(stderr, "ar:error getting '%s' attributes\n", fname);
exit(1);
}
if ((from = fopen(fname, "rb")) == NULL) {
fprintf(stderr,
"ar:opening member '%s':%s\n",
fname,
strerror(errno));
exit(1);
}
strftime(mtime, sizeof(mtime), "%s", gmtime(&st.st_mtime));
fprintf(to,
"%-16.16s%-12s%-6u%-6u%-8o%-10llu`\n",
fname,
mtime,
st.st_uid,
st.st_gid,
st.st_mode,
st.st_size);
for (n = 0; (c = getc(from)) != EOF; n++)
putc(c, to);
if (n & 1)
putc('\n', to);
if (ferror(from)) {
fprintf(stderr,
"ar:reading input '%s':%s\n",
fname, strerror(errno));
exit(1);
}
fclose(from);
}
static void
append(FILE *fp, char *list[])
{
char *fname;
if (fseek(fp, 0, SEEK_END) == EOF) {
perror("ar:seeking archive");
exit(1);
}
while ((fname = *list++) != NULL)
archieve(fname, fp);
if (fclose(fp) == EOF) {
perror("ar:error writing archive");
exit(1);
}
}
static void
copymember(char *fname, struct ar_hdr *hdr, FILE *dst, FILE *src)
{
int c;
long siz, n;
if (vflag)
printf("a - %s\n", fname);
fwrite(&hdr, sizeof(hdr), 1, dst);
siz = atol(hdr->ar_size);
if (siz < 0) {
fprintf(stderr, "ar:corrupted member header '%s'\n", fname);
exit(1);
}
if ((siz & 1) == 1)
siz++;
while (siz--) {
if ((c = getc(src)) == EOF)
break;
fputc(c, dst);
}
if (ferror(src)) {
perror("ar:writing temporary");
exit(1);
}
}
static void
letters(unsigned long val, char *s)
{
*s++ = (val & 04) ? 'r' : '-';
*s++ = (val & 02) ? 'w' : '-';
*s++ = (val & 01) ? 'x' : '-';
}
static char *
perms(struct ar_hdr *hdr)
{
size_t siz;
int c;
long val;
char *p, *q;
static char buf[10];
siz = sizeof(hdr->ar_mode);
p = hdr->ar_mode;
for (val = 0; siz-- > 0; val += c - '0') {
c = *p++;
if ((q = strchr("01234567", c)) == NULL) {
fputs("ar: corrupted header\n", stderr);
exit(1);
}
}
letters(val >> 6, buf);
letters(val >> 3, buf+3);
letters(val, buf +6);
buf[9] = '\0';
return buf;
}
static void
list(struct arop *op, char *files[])
{
int print = 0;
char **bp;
time_t t;
struct ar_hdr *hdr = &op->hdr;
if (*files == NULL) {
print = 1;
} else {
for (bp = files; *bp && strcmp(*bp, op->fname); ++bp)
;
print = *bp != NULL;
}
if (!print)
return;
if (!vflag) {
printf("%s\n", op->fname);
} else {
printf("%s %d/%d\t%s %s\n",
perms(hdr),
hdr->ar_uid, hdr->ar_gid,
"", /* TODO: ctime(&hdr->ar_date), */
op->fname);
}
}
static void
del(struct arop *op, char *files[])
{
char **bp;
for (bp = files; *bp && strcmp(*bp, op->fname); ++bp)
;
if (*bp)
return;
copymember(op->fname, &op->hdr, op->dst, op->src);
}
static char *
getfname(struct ar_hdr *hdr)
{
static char fname[FILENAME_MAX];
size_t i;
char *bp = fname;
for (i = 0; i < sizeof(hdr->ar_name); i++) {
if ((*bp = hdr->ar_name[i]) == ' ')
break;
++bp;
}
*bp = '\0';
return fname;
}
static void
run(FILE *fp, FILE *tmp, char *files[], void (*fun)(struct arop *, char *files[]))
{
struct arop op;
if (*files == NULL)
return;
op.src = fp;
op.dst = tmp;
while (!ferror(fp) && fread(&op.hdr, sizeof(op.hdr), 1, fp) == 1) {
fpos_t pos;
long len;
char *fname;
if (strncmp(op.hdr.ar_fmag, ARFMAG, sizeof(op.hdr.ar_fmag)) ||
(len = atol(op.hdr.ar_size)) < 0) {
fputs("ar:corrupted member\n", stderr);
exit(1);
}
op.fname = getfname(&op.hdr);
fgetpos(fp, &pos);
(*fun)(&op, files);
fsetpos(fp, &pos);
fseek(fp, len+1 & ~1, SEEK_CUR);
}
if (ferror(fp)) {
perror("ar:reading members");
exit(1);
}
fclose(fp);
}
static void
closetmp(FILE *tmp, char *afile)
{
int c;
FILE *fp;
if (fflush(tmp) == EOF) {
perror("ar:writing members");
exit(1);
}
if (tmpafile) {
fclose(tmp);
if (rename(tmpafile, afile) < 0) {
perror("ar:renaming temporary");
exit(1);
}
tmpafile = NULL;
} else {
if ((fp = fopen(afile, "wb")) == NULL) {
perror("ar:reopening archive file");
exit(1);
}
rewind(tmp);
while ((c = getc(tmp)) != EOF)
fputc(c, tmp);
fflush(fp);
if (ferror(fp) || ferror(tmp)) {
perror("ar:copying from temporary");
exit(1);
}
fclose(fp);
fclose(tmp);
}
}
static FILE *
opentmp(void)
{
FILE *tmp;
if (lflag) {
tmpafile = "ar.tmp";
tmp = fopen(tmpafile, "wb");
} else {
tmp = tmpfile();
}
if (tmp == NULL) {
perror("ar:creating temporary");
exit(1);
}
fputs(ARMAG, tmp);
return tmp;
}
int
main(int argc, char *argv[])
{
int key, nkey = 0, pos = 0;
char *afile;
FILE *fp, *tmp;;
void (*fun)(struct arop *, char *files[]);
atexit(cleanup);
ARGBEGIN {
case 'd':
nkey++;
key = 'd';
break;
case 'r':
key = 'r';
break;
case 'q':
nkey++;
key = 'q';
break;
case 't':
nkey++;
key = 't';
break;
case 'p':
nkey++;
key = 'p';
break;
case 'm':
nkey++;
key = 'm';
break;
case 'x':
nkey++;
key = 'x';
break;
case 'a':
aflag = 1;
pos++;
posname = EARGF(usage());
break;
case 'b':
bflag = 1;
pos++;
posname = EARGF(usage());
break;
case 'i':
iflag = 1;
pos++;
posname = EARGF(usage());
break;
case 'v':
vflag = 1;
break;
case 'c':
cflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'u':
uflag = 1;
break;
default:
usage();
} ARGEND
if (nkey == 0 || nkey > 1 || pos > 1 || argc == 0)
usage();
signal(SIGINT, sigfun);
signal(SIGQUIT, sigfun);
signal(SIGTERM, sigfun);
afile = *argv++;
fp = openar(afile);
switch (key) {
case 'q':
tmp = NULL;
fun = NULL;
append(fp, argv);
break;
case 'd':
tmp = opentmp();
fun = del;
break;
case 't':
tmp = NULL;
fun = list;
break;
case 'r':
case 'p':
case 'm':
case 'x':
/* TODO */
;
}
if (fun)
run(fp, tmp, argv, fun);
if (tmp)
closetmp(tmp, afile);
return 0;
}