shithub: scc

Download patch

ref: 015d343c41900f127914b3f0345353c9b0deff77
parent: 5c59ce4b790db825f40a8b7de0e7086abb14eccb
author: Roberto E. Vargas Caballero <k0ga@shike2.com>
date: Tue Jan 8 06:18:09 EST 2019

[ar] Move ar to cmd directory

--- a/src/Makefile
+++ b/src/Makefile
@@ -3,7 +3,7 @@
 PROJECTDIR = ..
 include $(PROJECTDIR)/scripts/rules.mk
 
-TOOLS = cc1 cc2 ld as objdump ar cmd
+TOOLS = cc1 cc2 ld as objdump cmd
 LIBS  = libscc libc libcrt libmach
 DIRS  = $(TOOLS) $(LIBS)
 
--- a/src/ar/Makefile
+++ /dev/null
@@ -1,22 +1,0 @@
-.POSIX:
-
-PROJECTDIR = ../..
-include $(PROJECTDIR)/scripts/rules.mk
-
-MORECFLAGS = -I$(DRIVER)
-
-OBJS   = main.o \
-         $(DRIVER)/driver.o \
-
-TARGET = $(BINDIR)/ar
-
-all: $(TARGET)
-
-$(TARGET): $(LIBDIR)/libscc.a
-
-$(TARGET): $(OBJS)
-	$(CC) $(SCC_LDFLAGS) $(OBJS) -lscc -o $@
-
-dep: inc-dep
-
-include deps.mk
--- a/src/ar/deps.mk
+++ /dev/null
@@ -1,5 +1,0 @@
-#deps
-main.o: $(INCDIR)/scc/scc/ar.h
-main.o: $(INCDIR)/scc/scc/arg.h
-main.o: $(INCDIR)/scc/scc/scc.h
-posix/driver.o: posix/driver.h
--- a/src/ar/main.c
+++ /dev/null
@@ -1,643 +1,0 @@
-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 <driver.h>
-
-#include <scc/ar.h>
-#include <scc/arg.h>
-#include <scc/scc.h>
-
-enum {
-	BEFORE,
-	INDOT,
-	AFTER,
-};
-
-struct tmp {
-	char *name;
-	FILE *fp;
-} tmps[3];
-
-char *argv0;
-
-static int bflag, vflag, cflag, lflag, uflag, aflag;
-static char *arfile, *posname;
-
-struct member {
-	FILE *src;
-	struct ar_hdr hdr;
-	int cur;
-	char *fname;
-	long size;
-	long mode;
-	long long date;
-};
-
-static void
-cleanup(void)
-{
-	int i;
-
-	for (i = 0; i < 3; i++) {
-		if (tmps[i].name)
-			remove(tmps[i].name);
-	}
-}
-
-/*
- * I do know that you cannot call remove from a signal handler
- * but we only can use stdio function to deal with files
- * because we are C99 compliant, and it is very likely that
- * remove is going to work in this case
- */
-static void
-sigfun(int signum)
-{
-	cleanup();
-	_Exit(1);
-}
-
-static FILE *
-openar(void)
-{
-	FILE *fp;
-	char magic[SARMAG+1];
-
-	if ((fp = fopen(arfile,"r+b")) == NULL) {
-		if (!cflag)
-			fprintf(stderr, "ar: creating %s\n", arfile);
-		if ((fp = fopen(arfile, "w+b")) == NULL) {
-			perror("ar:opening archive");
-			exit(1);
-		}
-		fputs(ARMAG, fp);
-		if (fflush(fp) == EOF) {
-			perror("ar:writing magic number");
-			exit(1);
-		}
-	} else {
-		if (fgets(magic, sizeof(magic), fp) == NULL) {
-			perror("ar:error reading magic number");
-			exit(1);
-		}
-		if (strcmp(magic, ARMAG)) {
-			fprintf(stderr,
-			        "ar:%s:invalid magic number '%s'\n",
-			        arfile,
-			        magic);
-			exit(1);
-		}
-	}
-	return fp;
-}
-
-static void
-archive(char *fname, FILE *to, char letter)
-{
-	int c;
-	size_t n;
-	FILE *from;
-	char mtime[13];
-	struct stat st;
-
-	if (vflag)
-		printf("%c - %s\n", letter, fname);
-	if (strlen(fname) > 16)
-		fprintf(stderr, "ar:%s: too long name\n", fname);
-	if ((from = fopen(fname, "rb")) == NULL) {
-		fprintf(stderr,
-		        "ar:opening member '%s':%s\n",
-		        fname,
-		        strerror(errno));
-		exit(1);
-	}
-	if (stat(fname, &st) < 0) {
-		fprintf(stderr, "ar:error getting '%s' attributes\n", fname);
-		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,
-	        (unsigned long long) 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 *argv[])
-{
-	char *fname;
-
-	if (fseek(fp, 0, SEEK_END) == EOF) {
-		perror("ar:seeking archive");
-		exit(1);
-	}
-
-	for ( ; fname = *argv; ++argv) {
-		*argv = NULL;
-		archive(fname, fp, 'a');
-	}
-
-	if (fclose(fp) == EOF) {
-		perror("ar:error writing archive");
-		exit(1);
-	}
-}
-
-static void
-copy(struct member *m, struct tmp *tmp)
-{
-	int c;
-	size_t siz = m->size;
-	struct ar_hdr *hdr = &m->hdr;
-
-	fwrite(hdr, sizeof(*hdr), 1, tmp->fp);
-	if ((siz & 1) == 1)
-		siz++;
-	while (siz--) {
-		if ((c = getc(m->src)) == EOF)
-			break;
-		fputc(c, tmp->fp);
-	}
-}
-
-static void
-letters(unsigned long val, char *s)
-{
-	*s++ = (val & 04) ? 'r' : '-';
-	*s++ = (val & 02) ? 'w' : '-';
-	*s++ = (val & 01) ? 'x' : '-';
-}
-
-static char *
-perms(struct member *m)
-{
-	static char buf[10];
-
-	letters(m->mode >> 6, buf);
-	letters(m->mode >> 3, buf+3);
-	letters(m->mode, buf +6);
-	buf[9] = '\0';
-
-	return buf;
-}
-
-static int
-inlist(char *fname, int argc, char *argv[])
-{
-	for (; argc-- > 0; ++argv) {
-		if (*argv && !strcmp(*argv, fname)) {
-			*argv = NULL;
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static void
-move(struct member *m, int argc, char *argv[])
-{
-	int where;
-
-	if (inlist(m->fname, argc, argv)) {
-		if (vflag)
-			printf("m - %s\n", m->fname);
-		where = INDOT;
-	} else if (posname && !strcmp(posname, m->fname)) {
-		where = (bflag) ? AFTER : BEFORE;
-		m->cur = AFTER;
-	} else {
-		where = m->cur;
-	}
-	copy(m, &tmps[where]);
-}
-
-static void
-insert(int argc, char *argv[])
-{
-	for (; argc-- > 0; ++argv) {
-		if (*argv) {
-			archive(*argv, tmps[INDOT].fp, 'a');
-			*argv = NULL;
-		}
-	}
-}
-
-static void
-update(struct member *m, int argc, char *argv[])
-{
-	int where;
-	FILE *fp = tmps[BEFORE].fp;
-
-	if (inlist(m->fname, argc, argv)) {
-		archive(m->fname, tmps[m->cur].fp, 'r');
-		return;
-	} else if (posname && !strcmp(posname, m->fname)) {
-		where = (bflag) ? AFTER : BEFORE;
-		m->cur = AFTER;
-	} else {
-		where = m->cur;
-	}
-	copy(m, &tmps[where]);
-}
-
-static void
-extract(struct member *m, int argc, char *argv[])
-{
-	int c;
-	long siz;
-	FILE *fp;
-
-	if (argc > 0 && !inlist(m->fname, argc, argv))
-		return;
-	if (vflag)
-		printf("x - %s\n", m->fname);
-	siz = m->size;
-
-	if ((fp = fopen(m->fname, "wb")) == NULL)
-		goto error_file;
-	while (siz-- > 0 && (c = getc(m->src)) != EOF)
-		putc(c, fp);
-	fflush(fp);
-	if (fclose(fp) == EOF)
-		goto error_file;
-
-	/* TODO: set attributes */
-	return;
-
-
-error_file:
-	perror("ar:error extracting file");
-	exit(1);
-}
-
-static void
-print(struct member *m, int argc, char *argv[])
-{
-	long siz;
-	int c;
-
-	if (argc > 0 && !inlist(m->fname, argc, argv))
-		return;
-	if (vflag)
-		printf("\n<%s>\n\n", m->fname);
-	siz = m->size;
-	while (siz-- > 0 && (c = getc(m->src)) != EOF)
-		putchar(c);
-}
-
-static void
-list(struct member *m, int argc, char *argv[])
-{
-	time_t t;
-	struct ar_hdr *hdr = &m->hdr;
-	char mtime[30];
-
-	if (argc > 0  && !inlist(m->fname, argc, argv))
-		return;
-	if (!vflag) {
-		printf("%s\n", m->fname);
-	} else {
-		t = totime(m->date);
-		strftime(mtime, sizeof(mtime), "%c", localtime(&t));
-		printf("%s %ld/%ld\t%s %s\n",
-		       perms(m),
-		       atol(hdr->ar_uid),
-		       atol(hdr->ar_gid),
-		       mtime,
-		       m->fname);
-	}
-}
-
-static void
-del(struct member *m, int argc, char *argv[])
-{
-	if (inlist(m->fname, argc, argv)) {
-		if (vflag)
-			printf("d - %s\n", m->fname);
-		return;
-	}
-	copy(m, &tmps[BEFORE]);
-}
-
-static char *
-getfname(struct ar_hdr *hdr)
-{
-	static char fname[SARNAM+1];
-	size_t i;
-
-	memcpy(fname, hdr->ar_name, SARNAM);
-	fname[SARNAM] = '\0';
-
-	for (i = SARNAM-1; i >= 0; --i) {
-		if (fname[i] != ' ' && fname[i] != '/')
-			break;
-		fname[i] = '\0';
-	}
-	return fname;
-}
-
-static long long
-getnum(char *s, int size, int base)
-{
-	int c;
-	long long val;
-	char *p;
-	static char digits[] = "0123456789";
-
-	for (val = 0; size > 0; val += c) {
-		--size;
-		if ((c = *s++) == ' ')
-			break;
-		if ((p = strchr(digits, c)) == NULL)
-			return -1;
-		if ((c = p - digits) >= base)
-			return -1;
-		val *= base;
-	}
-
-	while (size > 0 && *s++ == ' ')
-		--size;
-	return (size == 0) ? val : -1;
-}
-
-static int
-valid(struct member *m)
-{
-	struct ar_hdr *hdr = &m->hdr;
-
-	m->fname = getfname(&m->hdr);
-	m->size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10);
-	m->mode = getnum(hdr->ar_mode, sizeof(hdr->ar_mode), 8);
-	m->date = getnum(hdr->ar_date, sizeof(hdr->ar_date), 10);
-
-	if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)) ||
-	    m->size < 0 || m->mode < 0 || m->date < 0) {
-		return 0;
-	}
-	return 1;
-}
-
-static void
-run(FILE *fp, int argc, char *argv[],
-    void (*fun)(struct member *, int argc, char *files[]))
-{
-	struct member m;
-
-	m.src = fp;
-	m.cur = BEFORE;
-
-	while (fread(&m.hdr, sizeof(m.hdr), 1, fp) == 1) {
-		fpos_t pos;
-
-		if (!valid(&m)) {
-			fprintf(stderr,
-			        "ar:corrupted member '%s'\n",
-			        m.fname);
-			exit(1);
-		}
-		fgetpos(fp, &pos);
-		(*fun)(&m, argc, argv);
-		fsetpos(fp, &pos);
-		fseek(fp, m.size+1 & ~1, SEEK_CUR);
-	}
-	if (ferror(fp) || fclose(fp) == EOF) {
-		perror("ar:reading members");
-		exit(1);
-	}
-}
-
-static void
-merge(void)
-{
-	FILE *fp, *fi;
-	int c, i;
-
-
-	if ((fp = fopen(arfile, "wb")) == NULL) {
-		perror("ar:reopening archive");
-		exit(1);
-	}
-
-	fputs(ARMAG, fp);
-
-	for (i = 0; i < 3; i++) {
-		if ((fi = tmps[i].fp) == NULL)
-			continue;
-		fseek(fi, 0, SEEK_SET);
-		while ((c = getc(fi)) != EOF)
-			putc(c, fp);
-		if (ferror(fi)) {
-			perror("ar:error in temporary");
-			exit(1);
-		}
-	}
-
-	if (fclose(fp) == EOF) {
-		perror("ar:writing archive file");
-		exit(1);
-	}
-}
-
-static void
-closetmp(int which)
-{
-	struct tmp *tmp = &tmps[which];
-
-	if (!tmp->fp)
-		return;
-	if (fclose(tmp->fp) == EOF) {
-		perror("ar:closing temporaries");
-		exit(1);
-	}
-}
-
-static void
-opentmp(char *fname, int which)
-{
-	struct tmp *tmp = &tmps[which];
-
-	if (lflag) {
-		tmp->name = fname;
-		tmp->fp = fopen(fname, "w+b");
-	} else {
-		tmp->fp = tmpfile();
-	}
-
-	if (tmp->fp == NULL) {
-		perror("ar:creating temporary");
-		exit(1);
-	}
-}
-
-static void
-doit(int key, char *argv[], int argc)
-{
-	FILE *fp;
-
-	fp = openar();
-	if (argc == 0 &&
-	    (key == 'r' || key == 'd' || key == 'm' || key == 'q')) {
-		if (fclose(fp) == EOF) {
-			perror("ar:early close of archive file");
-			exit(-1);
-		}
-		return;
-	}
-
-	if (key == 'r' || key == 'm' || key == 'd')
-		opentmp("ar.tmp1", BEFORE);
-	if (key == 'r' || key == 'm') {
-		opentmp("ar.tmp2", INDOT);
-		opentmp("ar.tmp3", AFTER);
-	}
-
-	switch (key) {
-	case 'r':
-		run(fp, argc, argv, update);
-		insert(argc, argv);
-		merge();
-		break;
-	case 'm':
-		run(fp, argc, argv, move);
-		merge();
-		break;
-	case 'd':
-		run(fp, argc, argv, del);
-		merge();
-		break;
-	case 't':
-		run(fp, argc, argv, list);
-		break;
-	case 'p':
-		run(fp, argc, argv, print);
-		break;
-	case 'x':
-		run(fp, argc, argv, extract);
-		break;
-	case 'q':
-		append(fp, argv);
-		break;
-	}
-
-	closetmp(BEFORE);
-	closetmp(INDOT);
-	closetmp(AFTER);
-
-	for ( ; argc-- > 0; ++argv) {
-		if (*argv) {
-			fprintf(stderr, "ar: No member named '%s'\n", *argv);
-			exit(1);
-		}
-	}
-}
-
-static void
-usage(void)
-{
-	fputs("ar [-drqtpmx][posname] [-vuaibcl] [posname] arfile name ...\n",
-	      stderr);
-	exit(1);
-}
-
-int
-main(int argc, char *argv[])
-{
-	int key, nkey = 0, pos = 0;
-
-	atexit(cleanup);
-	ARGBEGIN {
-	case 'd':
-		nkey++;
-		key = 'd';
-		break;
-	case 'r':
-		nkey++;
-		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 'i':
-	case 'b':
-		bflag = 1;
-		pos++;
-		posname = EARGF(usage());
-		break;
-	case 'v':
-		vflag = 1;
-		break;
-	case 'c':
-		cflag = 1;
-		break;
-	case 'l':
-		lflag = 1;
-		break;
-	case 'u':
-		/* TODO */
-		abort();
-		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);
-
-	arfile = *argv;
-	doit(key, ++argv, --argc);
-
-	if (fflush(stdout) == EOF) {
-		perror("ar:error writing to stdout");
-		exit(1);
-	}
-
-	return 0;
-}
--- a/src/ar/posix/driver.c
+++ /dev/null
@@ -1,14 +1,0 @@
-static char sccsid[] = "@(#) ./ar/posix/driver.c";
-
-#include "driver.h"
-
-time_t
-totime(long long t)
-{
-	return t;
-}
-
-int
-setattr()
-{
-}
--- a/src/ar/posix/driver.h
+++ /dev/null
@@ -1,5 +1,0 @@
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-extern time_t totime(long long t);
--- a/src/cmd/.gitignore
+++ /dev/null
@@ -1,3 +1,0 @@
-nm
-strip
-size
--- a/src/cmd/Makefile
+++ b/src/cmd/Makefile
@@ -6,24 +6,25 @@
 TARGET = $(BINDIR)/nm \
          $(BINDIR)/strip \
          $(BINDIR)/size \
+         $(BINDIR)/ar \
 
 LIBS   = -lmach
 
 all: $(TARGET)
 
-nm strip: $(LIBDIR)/libmach.a
+$(TARGET): $(LIBDIR)/libmach.a
 
-$(BINDIR)/nm: nm
-	cp nm $@
+$(BINDIR)/nm: nm.o
+	$(CC) $(SCC_LDFLAGS) nm.o -lmach -o $@
 
-$(BINDIR)/strip: strip
-	cp strip $@
+$(BINDIR)/strip: strip.o
+	$(CC) $(SCC_LDFLAGS) strip.o -lmach -o $@
 
-$(BINDIR)/size: size
-	cp size $@
+$(BINDIR)/size: size.o
+	$(CC) $(SCC_LDFLAGS) size.o -lmach -o $@
 
-clean:
-	rm -f nm strip size
+$(BINDIR)/ar: ar.o ar-$(DRIVER).o
+	$(CC) $(SCC_LDFLAGS) ar.o ar-$(DRIVER).o -o $@
 
 dep: inc-dep
 
--- /dev/null
+++ b/src/cmd/ar-posix.c
@@ -1,0 +1,14 @@
+static char sccsid[] = "@(#) ./ar/posix/driver.c";
+
+#include "ar.h"
+
+time_t
+totime(long long t)
+{
+	return t;
+}
+
+int
+setattr()
+{
+}
--- /dev/null
+++ b/src/cmd/ar.c
@@ -1,0 +1,642 @@
+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 "ar.h"
+
+#include <scc/ar.h>
+#include <scc/arg.h>
+
+enum {
+	BEFORE,
+	INDOT,
+	AFTER,
+};
+
+struct tmp {
+	char *name;
+	FILE *fp;
+} tmps[3];
+
+char *argv0;
+
+static int bflag, vflag, cflag, lflag, uflag, aflag;
+static char *arfile, *posname;
+
+struct member {
+	FILE *src;
+	struct ar_hdr hdr;
+	int cur;
+	char *fname;
+	long size;
+	long mode;
+	long long date;
+};
+
+static void
+cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (tmps[i].name)
+			remove(tmps[i].name);
+	}
+}
+
+/*
+ * I do know that you cannot call remove from a signal handler
+ * but we only can use stdio function to deal with files
+ * because we are C99 compliant, and it is very likely that
+ * remove is going to work in this case
+ */
+static void
+sigfun(int signum)
+{
+	cleanup();
+	_Exit(1);
+}
+
+static FILE *
+openar(void)
+{
+	FILE *fp;
+	char magic[SARMAG+1];
+
+	if ((fp = fopen(arfile,"r+b")) == NULL) {
+		if (!cflag)
+			fprintf(stderr, "ar: creating %s\n", arfile);
+		if ((fp = fopen(arfile, "w+b")) == NULL) {
+			perror("ar:opening archive");
+			exit(1);
+		}
+		fputs(ARMAG, fp);
+		if (fflush(fp) == EOF) {
+			perror("ar:writing magic number");
+			exit(1);
+		}
+	} else {
+		if (fgets(magic, sizeof(magic), fp) == NULL) {
+			perror("ar:error reading magic number");
+			exit(1);
+		}
+		if (strcmp(magic, ARMAG)) {
+			fprintf(stderr,
+			        "ar:%s:invalid magic number '%s'\n",
+			        arfile,
+			        magic);
+			exit(1);
+		}
+	}
+	return fp;
+}
+
+static void
+archive(char *fname, FILE *to, char letter)
+{
+	int c;
+	size_t n;
+	FILE *from;
+	char mtime[13];
+	struct stat st;
+
+	if (vflag)
+		printf("%c - %s\n", letter, fname);
+	if (strlen(fname) > 16)
+		fprintf(stderr, "ar:%s: too long name\n", fname);
+	if ((from = fopen(fname, "rb")) == NULL) {
+		fprintf(stderr,
+		        "ar:opening member '%s':%s\n",
+		        fname,
+		        strerror(errno));
+		exit(1);
+	}
+	if (stat(fname, &st) < 0) {
+		fprintf(stderr, "ar:error getting '%s' attributes\n", fname);
+		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,
+	        (unsigned long long) 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 *argv[])
+{
+	char *fname;
+
+	if (fseek(fp, 0, SEEK_END) == EOF) {
+		perror("ar:seeking archive");
+		exit(1);
+	}
+
+	for ( ; fname = *argv; ++argv) {
+		*argv = NULL;
+		archive(fname, fp, 'a');
+	}
+
+	if (fclose(fp) == EOF) {
+		perror("ar:error writing archive");
+		exit(1);
+	}
+}
+
+static void
+copy(struct member *m, struct tmp *tmp)
+{
+	int c;
+	size_t siz = m->size;
+	struct ar_hdr *hdr = &m->hdr;
+
+	fwrite(hdr, sizeof(*hdr), 1, tmp->fp);
+	if ((siz & 1) == 1)
+		siz++;
+	while (siz--) {
+		if ((c = getc(m->src)) == EOF)
+			break;
+		fputc(c, tmp->fp);
+	}
+}
+
+static void
+letters(unsigned long val, char *s)
+{
+	*s++ = (val & 04) ? 'r' : '-';
+	*s++ = (val & 02) ? 'w' : '-';
+	*s++ = (val & 01) ? 'x' : '-';
+}
+
+static char *
+perms(struct member *m)
+{
+	static char buf[10];
+
+	letters(m->mode >> 6, buf);
+	letters(m->mode >> 3, buf+3);
+	letters(m->mode, buf +6);
+	buf[9] = '\0';
+
+	return buf;
+}
+
+static int
+inlist(char *fname, int argc, char *argv[])
+{
+	for (; argc-- > 0; ++argv) {
+		if (*argv && !strcmp(*argv, fname)) {
+			*argv = NULL;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void
+move(struct member *m, int argc, char *argv[])
+{
+	int where;
+
+	if (inlist(m->fname, argc, argv)) {
+		if (vflag)
+			printf("m - %s\n", m->fname);
+		where = INDOT;
+	} else if (posname && !strcmp(posname, m->fname)) {
+		where = (bflag) ? AFTER : BEFORE;
+		m->cur = AFTER;
+	} else {
+		where = m->cur;
+	}
+	copy(m, &tmps[where]);
+}
+
+static void
+insert(int argc, char *argv[])
+{
+	for (; argc-- > 0; ++argv) {
+		if (*argv) {
+			archive(*argv, tmps[INDOT].fp, 'a');
+			*argv = NULL;
+		}
+	}
+}
+
+static void
+update(struct member *m, int argc, char *argv[])
+{
+	int where;
+	FILE *fp = tmps[BEFORE].fp;
+
+	if (inlist(m->fname, argc, argv)) {
+		archive(m->fname, tmps[m->cur].fp, 'r');
+		return;
+	} else if (posname && !strcmp(posname, m->fname)) {
+		where = (bflag) ? AFTER : BEFORE;
+		m->cur = AFTER;
+	} else {
+		where = m->cur;
+	}
+	copy(m, &tmps[where]);
+}
+
+static void
+extract(struct member *m, int argc, char *argv[])
+{
+	int c;
+	long siz;
+	FILE *fp;
+
+	if (argc > 0 && !inlist(m->fname, argc, argv))
+		return;
+	if (vflag)
+		printf("x - %s\n", m->fname);
+	siz = m->size;
+
+	if ((fp = fopen(m->fname, "wb")) == NULL)
+		goto error_file;
+	while (siz-- > 0 && (c = getc(m->src)) != EOF)
+		putc(c, fp);
+	fflush(fp);
+	if (fclose(fp) == EOF)
+		goto error_file;
+
+	/* TODO: set attributes */
+	return;
+
+
+error_file:
+	perror("ar:error extracting file");
+	exit(1);
+}
+
+static void
+print(struct member *m, int argc, char *argv[])
+{
+	long siz;
+	int c;
+
+	if (argc > 0 && !inlist(m->fname, argc, argv))
+		return;
+	if (vflag)
+		printf("\n<%s>\n\n", m->fname);
+	siz = m->size;
+	while (siz-- > 0 && (c = getc(m->src)) != EOF)
+		putchar(c);
+}
+
+static void
+list(struct member *m, int argc, char *argv[])
+{
+	time_t t;
+	struct ar_hdr *hdr = &m->hdr;
+	char mtime[30];
+
+	if (argc > 0  && !inlist(m->fname, argc, argv))
+		return;
+	if (!vflag) {
+		printf("%s\n", m->fname);
+	} else {
+		t = totime(m->date);
+		strftime(mtime, sizeof(mtime), "%c", localtime(&t));
+		printf("%s %ld/%ld\t%s %s\n",
+		       perms(m),
+		       atol(hdr->ar_uid),
+		       atol(hdr->ar_gid),
+		       mtime,
+		       m->fname);
+	}
+}
+
+static void
+del(struct member *m, int argc, char *argv[])
+{
+	if (inlist(m->fname, argc, argv)) {
+		if (vflag)
+			printf("d - %s\n", m->fname);
+		return;
+	}
+	copy(m, &tmps[BEFORE]);
+}
+
+static char *
+getfname(struct ar_hdr *hdr)
+{
+	static char fname[SARNAM+1];
+	size_t i;
+
+	memcpy(fname, hdr->ar_name, SARNAM);
+	fname[SARNAM] = '\0';
+
+	for (i = SARNAM-1; i >= 0; --i) {
+		if (fname[i] != ' ' && fname[i] != '/')
+			break;
+		fname[i] = '\0';
+	}
+	return fname;
+}
+
+static long long
+getnum(char *s, int size, int base)
+{
+	int c;
+	long long val;
+	char *p;
+	static char digits[] = "0123456789";
+
+	for (val = 0; size > 0; val += c) {
+		--size;
+		if ((c = *s++) == ' ')
+			break;
+		if ((p = strchr(digits, c)) == NULL)
+			return -1;
+		if ((c = p - digits) >= base)
+			return -1;
+		val *= base;
+	}
+
+	while (size > 0 && *s++ == ' ')
+		--size;
+	return (size == 0) ? val : -1;
+}
+
+static int
+valid(struct member *m)
+{
+	struct ar_hdr *hdr = &m->hdr;
+
+	m->fname = getfname(&m->hdr);
+	m->size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10);
+	m->mode = getnum(hdr->ar_mode, sizeof(hdr->ar_mode), 8);
+	m->date = getnum(hdr->ar_date, sizeof(hdr->ar_date), 10);
+
+	if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)) ||
+	    m->size < 0 || m->mode < 0 || m->date < 0) {
+		return 0;
+	}
+	return 1;
+}
+
+static void
+run(FILE *fp, int argc, char *argv[],
+    void (*fun)(struct member *, int argc, char *files[]))
+{
+	struct member m;
+
+	m.src = fp;
+	m.cur = BEFORE;
+
+	while (fread(&m.hdr, sizeof(m.hdr), 1, fp) == 1) {
+		fpos_t pos;
+
+		if (!valid(&m)) {
+			fprintf(stderr,
+			        "ar:corrupted member '%s'\n",
+			        m.fname);
+			exit(1);
+		}
+		fgetpos(fp, &pos);
+		(*fun)(&m, argc, argv);
+		fsetpos(fp, &pos);
+		fseek(fp, m.size+1 & ~1, SEEK_CUR);
+	}
+	if (ferror(fp) || fclose(fp) == EOF) {
+		perror("ar:reading members");
+		exit(1);
+	}
+}
+
+static void
+merge(void)
+{
+	FILE *fp, *fi;
+	int c, i;
+
+
+	if ((fp = fopen(arfile, "wb")) == NULL) {
+		perror("ar:reopening archive");
+		exit(1);
+	}
+
+	fputs(ARMAG, fp);
+
+	for (i = 0; i < 3; i++) {
+		if ((fi = tmps[i].fp) == NULL)
+			continue;
+		fseek(fi, 0, SEEK_SET);
+		while ((c = getc(fi)) != EOF)
+			putc(c, fp);
+		if (ferror(fi)) {
+			perror("ar:error in temporary");
+			exit(1);
+		}
+	}
+
+	if (fclose(fp) == EOF) {
+		perror("ar:writing archive file");
+		exit(1);
+	}
+}
+
+static void
+closetmp(int which)
+{
+	struct tmp *tmp = &tmps[which];
+
+	if (!tmp->fp)
+		return;
+	if (fclose(tmp->fp) == EOF) {
+		perror("ar:closing temporaries");
+		exit(1);
+	}
+}
+
+static void
+opentmp(char *fname, int which)
+{
+	struct tmp *tmp = &tmps[which];
+
+	if (lflag) {
+		tmp->name = fname;
+		tmp->fp = fopen(fname, "w+b");
+	} else {
+		tmp->fp = tmpfile();
+	}
+
+	if (tmp->fp == NULL) {
+		perror("ar:creating temporary");
+		exit(1);
+	}
+}
+
+static void
+doit(int key, char *argv[], int argc)
+{
+	FILE *fp;
+
+	fp = openar();
+	if (argc == 0 &&
+	    (key == 'r' || key == 'd' || key == 'm' || key == 'q')) {
+		if (fclose(fp) == EOF) {
+			perror("ar:early close of archive file");
+			exit(-1);
+		}
+		return;
+	}
+
+	if (key == 'r' || key == 'm' || key == 'd')
+		opentmp("ar.tmp1", BEFORE);
+	if (key == 'r' || key == 'm') {
+		opentmp("ar.tmp2", INDOT);
+		opentmp("ar.tmp3", AFTER);
+	}
+
+	switch (key) {
+	case 'r':
+		run(fp, argc, argv, update);
+		insert(argc, argv);
+		merge();
+		break;
+	case 'm':
+		run(fp, argc, argv, move);
+		merge();
+		break;
+	case 'd':
+		run(fp, argc, argv, del);
+		merge();
+		break;
+	case 't':
+		run(fp, argc, argv, list);
+		break;
+	case 'p':
+		run(fp, argc, argv, print);
+		break;
+	case 'x':
+		run(fp, argc, argv, extract);
+		break;
+	case 'q':
+		append(fp, argv);
+		break;
+	}
+
+	closetmp(BEFORE);
+	closetmp(INDOT);
+	closetmp(AFTER);
+
+	for ( ; argc-- > 0; ++argv) {
+		if (*argv) {
+			fprintf(stderr, "ar: No member named '%s'\n", *argv);
+			exit(1);
+		}
+	}
+}
+
+static void
+usage(void)
+{
+	fputs("ar [-drqtpmx][posname] [-vuaibcl] [posname] arfile name ...\n",
+	      stderr);
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int key, nkey = 0, pos = 0;
+
+	atexit(cleanup);
+	ARGBEGIN {
+	case 'd':
+		nkey++;
+		key = 'd';
+		break;
+	case 'r':
+		nkey++;
+		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 'i':
+	case 'b':
+		bflag = 1;
+		pos++;
+		posname = EARGF(usage());
+		break;
+	case 'v':
+		vflag = 1;
+		break;
+	case 'c':
+		cflag = 1;
+		break;
+	case 'l':
+		lflag = 1;
+		break;
+	case 'u':
+		/* TODO */
+		abort();
+		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);
+
+	arfile = *argv;
+	doit(key, ++argv, --argc);
+
+	if (fflush(stdout) == EOF) {
+		perror("ar:error writing to stdout");
+		exit(1);
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/src/cmd/ar.h
@@ -1,0 +1,5 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+extern time_t totime(long long t);
--- a/src/cmd/deps.mk
+++ b/src/cmd/deps.mk
@@ -1,4 +1,8 @@
 #deps
+ar-posix.o: ar.h
+ar.o: $(INCDIR)/scc/scc/ar.h
+ar.o: $(INCDIR)/scc/scc/arg.h
+ar.o: ar.h
 nm.o: $(INCDIR)/scc/scc/arg.h
 nm.o: $(INCDIR)/scc/scc/mach.h
 size.o: $(INCDIR)/scc/scc/arg.h