/* * Archiver: * * A free translation of ar.s */ char usage[] "Usage: ar -drtx archive [file ...]"; char noar[] "No archive file"; char noup[] "Archive file not updated"; char nocreat[] "Can't create archive file"; char badform[] "Bad archive format"; char readio[] "Archive file read i/o error"; char inpio[] "Input file i/o error"; char tmpio[] "Temp file i/o error"; char tfname[] "/tmp/arXXXXXXX"; int magic 0177555; /* magic word to identify archive format file */ struct { /* archive file header */ char fname[8]; int mtime[2]; char uid; char mode; int size; } dir; struct { /* buffer for stat() */ int s_dev; int s_ino; int s_flags; char s_nlinks; char s_uid; char s_gid; char s_size0; int s_size1; int s_addr[8]; int s_atime[2]; int s_mtime[2]; } stbuf; char **ffile, **lfile; /* first & last file in arg list */ int nfiles; /* no. of files in arg list */ char *curname; /* name of current file arg */ char *afname; /* name of archive file */ int afmode; /* protect mode of archive file */ int afdev; /* device on which archive file resides */ int afi, tfi; /* fd for archive & temp files */ int buff[128]; int nerr; /* count of errors */ int vflag; /* 'verbose'*/ int zero; /* zero for padding */ main(argc, argv) char *argv[]; { extern dexit(); register char *p; register opt; if (argc < 3) error(usage); /* get option & check verbose flag */ if (*(p = argv[1]) == '-') p++; if ((opt = *p++) == 'v') { vflag++; opt = *p; } else if (*p == 'v') vflag++; /* set up list of file args */ afname = argv[2]; ffile = &argv[3]; lfile = &argv[argc]; nfiles = argc - 3; /* set up interrupt exit */ if ((signal(2, 1)&01) == 0) { signal(1, &dexit); signal(2, &dexit); } /* call appropriate command routine */ switch(opt) { case 'd': delete(); break; case 'r': replace(); break; case 't': table(); break; case 'x': extract(); break; default: error(usage); } } /* * Delete files from archive */ delete() { mktemp(); if (!getaf()) error(noar); while (getdir()) if (match()) { msg('d'); skipfl(); } else { msg('c'); copyfl(); } while (leftover()) nfound(); copyback(); } /* * Replace or append files in the archive */ replace() { mktemp(); if (getaf()) while (getdir()) if (match()) { msg('r'); skipfl(); infl(); } else { msg('c'); copyfl(); } while (leftover()) { msg('a'); infl(); } copyback(); } /* * List table of contents */ table() { if (!getaf()) error(noar); while (getdir()) { if (all() || match()) tabfl(); skipfl(); } while (leftover()) nfound(); } /* * Extract files from the archive */ extract() { if (!getaf()) error(noar); while (getdir()) if (all() || match()) { msg('x'); exfl(); } else skipfl(); while (leftover()) nfound(); } /* * Open archive file & check format */ getaf() { if (stat(afname, &stbuf) < 0) { afmode = 0666; afi = -1; return(0); } if ((afi = open(afname, 0)) < 0) error("Can't open archive file"); afmode = stbuf.s_flags & 0777; afdev = stbuf.s_dev; readaf(sizeof magic); if (buff[0] != magic) error(badform); return(1); } /* * Create temporary file */ mktemp() { register pid; register char *p; /* use process id to create unique name in /tmp directory */ for (p = tfname; *p; p++) ; pid = getpid(); while (*--p == 'X') { *p = (pid&7) + '0'; pid =>> 3; } /* * create file & reopen in input/output mode */ if ((tfi = creat(tfname, 0600)) < 0) error("Can't create temp file"); close(tfi); if ((tfi = open(tfname, 2)) < 0) error("Can't open temp file???"); /* write 'magic word' archive header */ write(tfi, &magic, sizeof magic); } /* * Read from archive file, checking for i/o errors */ readaf(size) { register len; if ((len = read(afi, buff, size)) == size) return; if (size < 0) /* i/o error */ error(readio); error(badform); /* premature eof */ } /* * Read next file header */ getdir() { register len; if ((len = read(afi, &dir, sizeof dir)) == sizeof dir) return(1); if (len == 0) /* end of file */ return(0); if (len < 0) /* i/o error */ error(readio); error(badform); /* premature eof */ } /* * Test whether current header filename matches any argument file */ match() { register char **fn; register i; for (fn = ffile; fn < lfile; fn++) if (*fn && cmp8(trim(*fn), dir.fname)) { curname = *fn; *fn = 0; return(1); } return(0); } /* * If no filename arguments, match all files in archive */ all() { static char aname[12]; if (nfiles) return(0); cpy8(dir.fname, aname); curname = aname; return(1); } /* * Compare two 8-char strings */ cmp8(s1, s2) char *s1, *s2; { register char *p, *q; register i; p = s1; q = s2; for (i=0; i<8; i++) { if (p[i] != q[i]) return(0); if (p[i] == '\0') break; } return(1); } /* * Copy 8 chars of a filename */ cpy8(s1, s2) char *s1, *s2; { register char *p, *q; register i; p = s1; q = s2; for (i=0; i < 8; i++) if ((q[i] = p[i]) == '\0') break; while (i < 8) q[i++] = '\0'; } /* * Given a pathname, return the last component */ trim(fn) char *fn; { register char *fp, *p; register c; p = fp = fn; while (c = *fp++) if (c == '/') p = fp; return(p); } /* * Return any file as yet unmatched in arg list */ leftover() { register char **fn; for (fn = ffile; fn < lfile; fn++) if (*fn) { curname = *fn; *fn = 0; cpy8(trim(curname), dir.fname); return(1); } return(0); } /* * Print one table of contents entry */ tabfl() { extern char *ctime(); register char *p; printf("%-8.8s", dir.fname); if (vflag) { printf("%8d", dir.size); p = ctime(dir.mtime); printf(" %-12.12s", &p[4]); } putchar('\n'); } /* * Extract a file from the archive */ exfl() { register ofi, len, size; /* create new file with mode & owner of archived file */ if ((ofi = creat(curname, dir.mode|0400)) < 0) { printf("-- %s: can't create\n", dir.fname); nerr++; skipfl(); return; } /* copy file */ for (size = dir.size; size >= 512; size =- 512) { readaf(512); write(ofi, buff, 512); } if (size) { readaf(size); write(ofi, buff, size); } close(ofi); /* pad to halfword if necessary */ if (size & 01) seek(afi, 1, 1); } /* * Copy a file into the (temporary) archive */ infl() { register size, ifi; if (stat(curname, &stbuf) < 0 || (ifi = open(curname, 0)) < 0) { printf("-- %s: can't open\n", curname); nerr++; return; } /* make header */ cpy8(trim(curname), dir.fname); dir.mtime[0] = stbuf.s_mtime[0]; dir.mtime[1] = stbuf.s_mtime[1]; dir.uid = stbuf.s_uid; dir.mode = stbuf.s_flags & 0377; dir.size = stbuf.s_size1; write(tfi, &dir, sizeof dir); /* copy file */ for (size = dir.size; size >= 512; size =- 512) { if (read(ifi, buff, 512) != 512) error(inpio); write(tfi, buff, 512); } if (size) { if (read(ifi, buff, size) != size) error(inpio); write(tfi, buff, size); } close(ifi); /* pad to halfword if necessary */ if (size & 01) write(tfi, &zero, 1); } /* * Copy a file from the archive to the temporary file */ copyfl() { register size; /* copy the header */ write(tfi, &dir, sizeof dir); /* copy the file */ for (size = dir.size; size >= 512; size =- 512) { readaf(512); write(tfi, buff, 512); } if (size) { readaf(size); write(tfi, buff, size); } /* pad to halfword if necessary */ if (size & 01) { seek(afi, 1, 1); write(tfi, &zero, 1); } } /* * Skip to the next file in the archive */ skipfl() { register s; s = (dir.size+1) & ~01; seek(afi, s, 1); } /* * Verbose option -- running commentary */ msg(ch) { if (vflag) printf("%c %.8s\n", ch, dir.fname); } /* * Error message for missing file */ nfound() { printf("-- %.8s not found\n", trim(curname)); /* record error so archive will not be updated */ nerr++; } /* * All finished -- replace archive with new temporary file */ copyback() { register fd, len; /* if any errors have occured, don't update the archive */ if (nerr) error(noup); /* create archive file if it didn't exist before */ if (afi < 0) { if ((fd = creat(afname, afmode)) < 0) error(nocreat); fstat(fd, &stbuf); afdev = stbuf.s_dev; close(fd); } else close(afi); /* prevent interrupts while moving back */ signal(1, 1); signal(2, 1); /* if temp & archive files on same device, just do a mv */ stat(tfname, &stbuf); if (stbuf.s_dev == afdev) { unlink(afname); if (link(tfname, afname) < 0) error(nocreat); unlink(tfname); chmod(afname, afmode); return; } /* copy temp file to archive file */ if ((fd = creat(afname, afmode)) < 0) error(nocreat); seek(tfi, 0, 0); while ((len = read(tfi, buff, 512)) > 0) write(fd, buff, len); if (len < 0) error(tmpio); unlink(tfname); } /* * Error exit */ error(s) { printf("%s\n", s); dexit(); } /* * Delete temp file & exit */ dexit() { if (tfi) unlink(tfname); exit(1); }