ast@cs.vu.nl (Andy Tanenbaum) (05/30/88)
/* ar - archiver Author: Michiel Huisjes */ /* V7 upgrade Author: Monty Walls */ /* * Usage: ar 'key' [posname] archive [file] ... * * where 'key' is one of: qrqtpmx * * q: quickly append to the end of the archive file * m: move named files * r: replace (append when not in archive) * d: delete * t: print contents of archive * p: print named files * x: extract * * concatencated with one or more of: vuaibcl * * l: local temporary file for work instead of /tmp/ar.$$$$$ * v: verbose * a: after 'posname' * b: before 'posname' * i: before 'posname' * c: create (suppresses creation message) * u: replace only if dated later than member in archive */ /* mods: * 1.2 upgrade. * local directory support (mrw). * full V7 functionality + complete rewrite (mrw). * changed verbose mode to give member name on print (mrw). * fixed up error messages to give more info (mrw). * * notes: * pdp11 long format & Intel long format are different. * * change log: * forgot that ar_size is a long for printing - 2/14/88 - mrw * got the mode bit maps mirrored - 2/19/88 - mrw * print & extract member logic fixed - 2/19/88 - mrw */ /* include files */ #include <stdio.h> #include <sys/stat.h> #include <signal.h> /* ar.c header file V7 */ #define ARMAG 0177545l struct ar_hdr { char ar_name[14]; long ar_date; /* not Intel format */ char ar_uid; char ar_gid; int ar_mode; long ar_size; /* not Intel format */ }; /* macro functions */ #define FOREVER (32766) #define odd(nr) (nr & 1) #define even(nr) (odd(nr) ? nr + 1 : nr) #define quit(pid,sig) (kill(pid,sig),sleep(FOREVER)) #ifndef tell # define tell(f) (lseek(f, 0l, 1)) #endif /* option switches */ /* major options */ #define EXTRACT 0x01 #define REPLACE 0x02 #define PRINT 0x04 #define TABLE 0x08 #define DELETE 0x10 #define APPEND 0x20 #define MOVE 0x40 /* minor options */ #define BEFORE 0x01 #define AFTER 0x02 #define LOCAL 0x01 #define VERBOSE 0x01 #define CREATE 0x01 #define ONLY 0x01 /* mode bits maps */ #define EXEC_OWNER 00100 #define EXEC_GROUP 00010 #define EXEC_ALL 00001 #define READ_OWNER 00400 #define READ_GROUP 00040 #define READ_ALL 00004 #define WRITE_OWNER 00200 #define WRITE_GROUP 00020 #define WRITE_ALL 00002 #define SET_UID 04000 #define SET_GID 02000 /* global defines */ #define BUFFERSIZE 4096 #define WRITE 2 /* both read & write */ #define READ 0 #define MAGICSIZE sizeof(short) /* size of magic number in file */ /* option switches */ char verbose = 0; char local = 0; char create = 0; char only = 0; char major = 0; char minor = 0; /* global variables */ char *tmp1; char *tmp2; char *progname; char *posname = NULL; char *afile; char buffer[BUFFERSIZE]; long pos_offset = -1; int mypid; /* keep track of member moves using this struct */ struct mov_list { long pos; struct mov_list *next; } *moves = NULL; /* forward declarations and external references */ extern char *malloc(); extern char *mktemp(), *rindex(); extern int strcmp(); extern print_date(); extern user_abort(), usage(); extern long lseek(); extern char *basename(); int main(argc, argv) int argc; char **argv; { int ac, opts_seen = 0, rc; char *av; progname = argv[0]; if (argc < 3) usage(); for (av = argv[1]; *av; ++av) { switch (*av) { /* major option */ case 'q': major |= APPEND; ++opts_seen; break; case 'r': major |= REPLACE; ++opts_seen; break; case 'x': major |= EXTRACT; ++opts_seen; break; case 'p': major |= PRINT; ++opts_seen; break; case 'm': major |= MOVE; ++opts_seen; break; case 'd': major |= DELETE; ++opts_seen; break; case 't': major |= TABLE; ++opts_seen; break; case 'l': local |= LOCAL; break; case 'a': minor |= AFTER; break; case 'i': case 'b': minor |= BEFORE; break; case 'v': verbose |= VERBOSE; break; case 'c': create |= CREATE; break; case 'u': only |= ONLY; break; default: usage(); } } if (opts_seen != 1) usage(); /* now do edits on options */ if (!(major & (REPLACE | MOVE))) { if (minor & (AFTER | BEFORE)) usage(); } else if (major & MOVE) { if (!(minor & (AFTER | BEFORE))) usage(); } else if (only & ONLY) if (!(major & REPLACE)) usage(); if (local) tmp1 = mktemp("./ar.1.XXXXXX"); else tmp1 = mktemp("/tmp/ar.1.XXXXXX"); /* now if minor says AFTER or BEFORE - then get posname */ if (minor & (AFTER | BEFORE) && argc >= 4) { posname = argv[2]; afile = argv[3]; ac = 4; } else { posname = (char *)NULL; afile = argv[2]; ac = 3; } /* exit logic consists of doing a kill on my pid to insure that we */ /* get the current clean up and exit logic */ mypid = getpid(); signal(SIGINT, user_abort); switch (major) { case REPLACE: case DELETE: case MOVE: ar_members(ac, argc, argv); break; case EXTRACT: case TABLE: case PRINT: ar_common(ac, argc, argv); break; case APPEND: append_members(ac, argc, argv); break; default: usage(); } for (rc = 0; ac < argc; ++ac) { if (*argv[ac] != '\0') { /* no processing done on this name */ fprintf(stderr,"Error %s: %s not found in %s\n", progname, argv[ac], afile); rc = 1; } } fflush(stdout); exit(rc); } usage() { fprintf(stderr,"Usage: %s [qrxdpmt][abivulc] [posname] afile name ... \n",progname); exit(1); } user_abort() { unlink(tmp1); exit(1); } insert_abort() { unlink(tmp1); unlink(tmp2); exit(1); } mwrite(fd, address, bytes) int fd; register char *address; register int bytes; { if (write(fd, address, bytes) != bytes) { fprintf(stderr," Error: %s - Write error\n",progname); quit(mypid, SIGINT); } } long swap(l) long l; { union { struct { int word1, word2; } words; long n; } u_in, u_out; u_in.n = l; u_out.words.word1 = u_in.words.word2; u_out.words.word2 = u_in.words.word1; return (u_out.n); } addmove(pos) long pos; { struct mov_list *newmove; newmove = (struct mov_list *)malloc(sizeof(struct mov_list)); newmove->pos = pos; newmove->next = moves; moves = newmove; } struct ar_hdr * get_member(fd) int fd; { int ret; static struct ar_hdr member; if ((ret = read(fd, &member, sizeof(struct ar_hdr))) <= 0) return ((struct ar_hdr *)NULL); if (ret != sizeof(struct ar_hdr)) { fprintf(stderr,"Error: ar corrupted archive %s\n",afile); quit(mypid,SIGINT); } /* the archive long format is pdp11 not intel * therefore we must reformat them for our internal use */ member.ar_date = swap(member.ar_date); member.ar_size = swap(member.ar_size); return (&member); } int open_archive(filename, opt, to_create) char *filename; int opt; { static unsigned short magic; int fd; /* to_create can have values of 0,1,2 */ /* 0 - don't create a file. */ /* 1 - create file but use create switch message mode */ /* 2 - create file but don't talk about it */ if (to_create) { if ((fd = creat(filename, 0644)) < 0) { fprintf(stderr, "Error: %s can not create %s\n",progname, filename); quit(mypid,SIGINT); } if (!create && to_create == 1) fprintf(stderr, "%s:%s created\n", progname, filename); magic = ARMAG; mwrite(fd, &magic, MAGICSIZE); return (fd); } else { if ((fd = open(filename, opt)) < 0) { if (opt == WRITE) return (open_archive(filename, opt, 1)); else { fprintf(stderr, "Error: %s can not open %s\n",progname, filename); quit(mypid,SIGINT); } } /* now check the magic number for ar V7 file */ lseek(fd, 0l, 0); read(fd, &magic, MAGICSIZE); if (magic != ARMAG) { fprintf(stderr, "Error: not %s V7 format - %s\n",progname, filename); quit(mypid,SIGINT); } if (major & APPEND) lseek(fd, 0l, 2); /* seek eof position */ return (fd); } } int rebuild(fd, tempfd) register int fd, tempfd; { register int n; /* after we have built the archive to a temporary file and */ /* everything has worked out- we copy the archive back to */ /* original file */ signal(SIGINT, SIG_IGN); close(fd); close(tempfd); fd = open_archive(afile, WRITE, 2); tempfd = open_archive(tmp1, WRITE, 0); while ((n = read(tempfd, buffer, BUFFERSIZE)) > 0) mwrite(fd, buffer, n); close(tempfd); unlink(tmp1); return (fd); } print_mode(mode) short mode; { char g_ex, o_ex, all_ex; char g_rd, o_rd, all_rd; char g_wr, o_wr, all_wr; g_ex = EXEC_GROUP & mode ? 'x' : '-'; o_ex = EXEC_OWNER & mode ? 'x' : '-'; all_ex = EXEC_ALL & mode ? 'x' : '-'; g_ex = SET_GID & mode ? 's' : g_ex; o_ex = SET_UID & mode ? 's' : o_ex; g_rd = READ_GROUP & mode ? 'r' : '-'; o_rd = READ_OWNER & mode ? 'r' : '-'; all_rd = READ_ALL & mode ? 'r' : '-'; g_wr = WRITE_GROUP & mode ? 'w' : '-'; o_wr = WRITE_OWNER & mode ? 'w' : '-'; all_wr = WRITE_ALL & mode ? 'w' : '-'; fprintf(stdout,"%c%c%c",o_rd, o_wr, o_ex); fprintf(stdout,"%c%c%c",g_rd, g_wr, g_ex); fprintf(stdout,"%c%c%c",all_rd, all_wr, all_ex); } print_header(member) struct ar_hdr *member; { if (verbose) { print_mode(member->ar_mode); fprintf(stdout,"%3.3d",member->ar_uid); fprintf(stdout,"/%-3.3d ",member->ar_gid); fprintf(stdout,"%5.5D",member->ar_size); /* oops is long - mrw */ print_date(member->ar_date); } fprintf(stdout,"%-14.14s\n",member->ar_name); } print(fd,member) int fd; struct ar_hdr *member; { int outfd; long size; register int cnt, ret; int do_align; if (major & EXTRACT) { if ((outfd = creat(member->ar_name,0666)) < 0) { fprintf(stderr,"Error: %s could not creat %s\n",progname, member->ar_name); quit(mypid,SIGINT); } if (verbose) fprintf(stdout,"x - %s\n",member->ar_name); } else { if (verbose) { fprintf(stdout,"p - %s\n",member->ar_name); fflush(stdout); } outfd = fileno(stdout); } /* changed loop to use long size for correct extracts */ for (size = member->ar_size; size > 0; size -= ret) { cnt = (size < BUFFERSIZE ? size : BUFFERSIZE); ret = read(fd, buffer, cnt); if (ret > 0) write(outfd,buffer, ret); } if (odd(member->ar_size)) lseek(fd,1l,1); /* realign ourselves */ if (major & EXTRACT) { close(outfd); chmod(member->ar_name, member->ar_mode); } } /* copy a given member from fd1 to fd2 */ copy_member(infd, outfd, member) int infd, outfd; struct ar_hdr *member; { int n, cnt; long m, size; /* save copies for our use */ m = size = member->ar_size; /* format for disk usage */ member->ar_size = swap(member->ar_size); member->ar_date = swap(member->ar_date); mwrite(outfd, member, sizeof(struct ar_hdr)); for (; m > 0; m -= n) { cnt = (m < BUFFERSIZE ? m : BUFFERSIZE); if ((n = read(infd, buffer, cnt)) != cnt) { fprintf(stderr,"Error: %s - read error on %s\n",progname, member->ar_name); quit(mypid, SIGINT); } mwrite(outfd, buffer, n); } if (odd(size)) { /* pad to word boundary */ mwrite(outfd, buffer, 1); lseek(infd,1l,1); /* realign reading fd */ } } /* insert at current offset - name file */ insert(fd, name, mess, oldmember) int fd; char *name, *mess; struct ar_hdr *oldmember; { static struct ar_hdr member; static struct stat status; int in_fd; if (stat(name, &status) < 0) { fprintf(stderr,"Error: %s cannot find file %s\n",progname,name); quit(mypid,SIGINT); } else if ((in_fd = open(name, READ)) < 0) { fprintf(stderr,"Error: %s cannot open file %s\n",progname,name); quit(mypid,SIGINT); } strcpy(member.ar_name, basename(name)); member.ar_uid = status.st_uid; member.ar_gid = status.st_gid; member.ar_mode = status.st_mode & 07777; member.ar_date = status.st_mtime; member.ar_size = status.st_size; if (only & ONLY) if (oldmember != (struct ar_hdr *)NULL) if (member.ar_date <= oldmember->ar_date) { close(in_fd); if (verbose) fprintf(stdout, "not %s - %s\n",mess, name); return (-1); } copy_member(in_fd, fd, &member); if (verbose) fprintf(stdout, "%s - %s\n",mess, name); close(in_fd); return (1); } int ar_move(oldfd, arfd,mov) int oldfd, arfd; struct mov_list *mov; { long pos; int cnt, want, a, newfd; struct ar_hdr *member; if (local) tmp2 = mktemp("./ar.2.XXXXXX"); else tmp2 = mktemp("/tmp/ar.2.XXXXXX"); close(oldfd); /* close old temp file */ signal(SIGINT, insert_abort); /* set new signal handler */ newfd = open_archive(tmp2, WRITE, 2); /* open new tmp file */ oldfd = open_archive(tmp1, WRITE, 0); /* reopen old tmp file */ /* copy archive till we get to pos_offset */ for (pos = pos_offset; pos > 0; pos -= cnt) { want = (pos < BUFFERSIZE ? pos : BUFFERSIZE); if ((cnt = read(oldfd, buffer, want)) > 0) mwrite(newfd, buffer, cnt); } /* if minor = 'a' then skip over posname */ if (minor & AFTER) { if ((member = get_member(oldfd)) != NULL) copy_member(oldfd, newfd, member); } /* move members in the library */ while (mov != NULL) { lseek(arfd, mov->pos, 0); if ((member = get_member(arfd)) != NULL) copy_member(arfd, newfd, member); mov = mov->next; if (verbose) fprintf(stdout, "m - %s\n", member->ar_name); } /* copy rest of library into new tmp file */ while ((member = get_member(oldfd)) != NULL) copy_member(oldfd, newfd, member); /* detach old temp file */ close(oldfd); unlink(tmp1); /* change context temp file */ tmp1 = tmp2; return (newfd); } int ar_insert(oldfd, ac, argc, argv) int oldfd; int ac, argc; char **argv; { long pos; int cnt, want, a, newfd; struct ar_hdr *member; if (local) tmp2 = mktemp("./ar.2.XXXXXX"); else tmp2 = mktemp("/tmp/ar.2.XXXXXX"); close(oldfd); /* close old temp file */ signal(SIGINT, insert_abort); /* set new signal handler */ newfd = open_archive(tmp2, WRITE, 2); /* open new tmp file */ oldfd = open_archive(tmp1, WRITE, 0); /* reopen old tmp file */ /* copy archive till we get to pos_offset */ for (pos = pos_offset; pos > 0; pos -= cnt) { want = (pos < BUFFERSIZE ? pos : BUFFERSIZE); if ((cnt = read(oldfd, buffer, want)) > 0) mwrite(newfd, buffer, cnt); } /* if minor = 'a' then skip over posname */ if (minor & AFTER) { if ((member = get_member(oldfd)) != NULL) copy_member(oldfd, newfd, member); } /* copy new members into the library */ for (a = ac+1; a <= argc; ++a) if (argv[a-1] && *argv[a-1] != '\0') { insert(newfd, argv[a-1], "a", (struct ar_hdr *)NULL); *argv[a-1] = '\0'; } /* copy rest of library into new tmp file */ while ((member = get_member(oldfd)) != NULL) copy_member(oldfd, newfd, member); /* detach old temp file */ close(oldfd); unlink(tmp1); /* change context temp file */ tmp1 = tmp2; return (newfd); } ar_common(ac, argc, argv) int ac, argc; char **argv; { int a, fd, did_print; struct ar_hdr *member; fd = open_archive(afile, READ, 0); while ((member = get_member(fd)) != NULL) { did_print = 0; if (ac < argc) { for (a = ac+1; a <= argc; ++a) { if (strcmp(basename(argv[a-1]),member->ar_name) == 0) { if (major & TABLE) print_header(member); else if (major & (PRINT | EXTRACT)) { print(fd, member); did_print = 1; } *argv[a-1] = '\0'; break; } } } else { if (major & TABLE) print_header(member); else if (major & (PRINT | EXTRACT)) { print(fd, member); did_print = 1; } } /* if we didn't print the member or didn't use it we will * have to seek over its contents */ if (!did_print) lseek(fd, (long)even(member->ar_size), 1); } } ar_members(ac, argc, argv) int ac, argc; char **argv; { int a, fd, tempfd, rc; struct ar_hdr *member; long *lpos; fd = open_archive(afile, WRITE, 0); tempfd = open_archive(tmp1, WRITE, 2); while ((member = get_member(fd)) != NULL) { /* if posname specified check for our member */ /* if our member save his starting pos in our working file*/ if (posname && strcmp(posname, member->ar_name) == 0) pos_offset = tell(tempfd) - MAGICSIZE; if (ac < argc) { /* we have a list of members to check */ for (a = ac+1; a <= argc; ++a) if (strcmp(basename(argv[a-1]),member->ar_name) == 0) { if (major & REPLACE) { if (insert(tempfd,argv[a-1],"r", member) < 0) copy_member(fd, tempfd, member); else lseek(fd, (long)even(member->ar_size), 1); } else if (major & MOVE) { /* cheat by saving pos in archive */ addmove((tell(fd) - sizeof(struct ar_hdr))); lseek(fd, (long)even(member->ar_size), 1); } *argv[a-1] = '\0'; break; } } if (ac >= argc || a > argc) /*nomatch on a member name */ copy_member(fd, tempfd, member); else if (major & DELETE) { if (verbose) fprintf(stdout,"d - %s\n",member->ar_name); lseek(fd, (long)even(member->ar_size), 1); } } if (major & MOVE) { if (posname == NULL) pos_offset = lseek(fd, 0l, 2); else if (pos_offset == (-1)) { fprintf(stderr,"Error: %s cannot find file %s\n",progname,posname); quit(mypid,SIGINT); } tempfd = ar_move(tempfd, fd, moves); } else if (major & REPLACE) { /* take care to add left overs */ /* if the posname is not found we just add to end of ar */ if (posname && pos_offset != (-1)) { tempfd = ar_insert(tempfd, ac, argc, argv); } else { for (a = ac+1; a <= argc; ++a) if (*argv[a-1]) { insert(tempfd, argv[a-1], "a", (struct ar_hdr *)NULL); *argv[a-1] = '\0'; } } } fd = rebuild(fd, tempfd); close(fd); } append_members(ac, argc, argv) int ac, argc; char **argv; { int a, fd; struct ar_hdr *member; /* quickly append members don't worry about dups in ar */ fd = open_archive(afile, WRITE, 0); if (ac < argc) { if (odd(lseek(fd, 0l, 2))) mwrite(fd, buffer, 1); /* while not end of member list insert member at end */ for (a = ac+1; a <= argc; ++a) { insert(fd, argv[a-1], "a", (struct ar_hdr *)NULL); *argv[a-1] = '\0'; } } close(fd); } char *basename(path) char *path; { register char *ptr = path; register char *last = (char *)NULL; while (*ptr != '\0') { if (*ptr == '/') last = ptr; ptr++; } if (last == (char *)NULL) return path; if (*(last + 1) == '\0') { *last = '\0'; return basename(path); } return last + 1; } #define MINUTE 60L #define HOUR (60L * MINUTE) #define DAY (24L * HOUR) #define YEAR (365L * DAY) #define LYEAR (366L * DAY) int mo[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; char *moname[] = { " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ", " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec " }; /* Print the date. This only works from 1970 to 2099. */ print_date(t) long t; { int i, year, day, month, hour, minute; long length, time(), original; year = 1970; original = t; while (t > 0) { length = (year % 4 == 0 ? LYEAR : YEAR); if (t < length) break; t -= length; year++; } /* Year has now been determined. Now the rest. */ day = (int) (t / DAY); t -= (long) day * DAY; hour = (int) (t / HOUR); t -= (long) hour * HOUR; minute = (int) (t / MINUTE); /* Determine the month and day of the month. */ mo[1] = (year % 4 == 0 ? 29 : 28); month = 0; i = 0; while (day >= mo[i]) { month++; day -= mo[i]; i++; } /* At this point, 'year', 'month', 'day', 'hour', 'minute' ok */ fprintf(stdout, "%s%2.2d ",moname[month],++day); if (time((long *)NULL) - original >= YEAR / 2L) fprintf(stdout,"%4.4D ",(long)year); else fprintf(stdout,"%02.2d:%02.2d ",hour, minute); }