diff options
Diffstat (limited to 'toys/pending/tar.c')
-rw-r--r-- | toys/pending/tar.c | 377 |
1 files changed, 192 insertions, 185 deletions
diff --git a/toys/pending/tar.c b/toys/pending/tar.c index 97e699b4..d0d840ba 100644 --- a/toys/pending/tar.c +++ b/toys/pending/tar.c @@ -18,11 +18,7 @@ * Extract into dir same as filename, --restrict? "Tarball is splodey" * -USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN)) - -todo: support .txz -todo: directory timestamps not set on extract -todo: extract into chmod 000 directory +USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(group):(owner):(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN)) config TAR bool "tar" @@ -36,10 +32,8 @@ config TAR c Create x Extract t Test f Name of TARFILE C Change to DIR first v Verbose: show filenames o Ignore owner h Follow symlinks m Ignore mtime - j Force bzip2 format z Force gzip format - O Extract to stdout - X File of names to exclude - T File of names to include + j bzip2 compression z gzip compression + O Extract to stdout X exclude names in FILE T include names in FILE --exclude=FILE File pattern(s) to exclude */ @@ -49,14 +43,13 @@ config TAR GLOBALS( char *f, *C; struct arg_list *T, *X; - char *to_command; - struct arg_list *exc; + char *to_command, *owner, *group; + struct arg_list *exclude; -// exc is an argument but inc isn't? - struct arg_list *inc, *pass; + struct double_list *incl, *excl, *seen; void *inodes; char *cwd; - int fd; + int fd, ouid, ggid; // Parsed information about a tar header. struct { @@ -76,6 +69,34 @@ struct tar_hdr { prefix[155], padd[12]; }; +// convert to int to octal (or base-256) +static void itoo(char *str, int len, unsigned long long val) +{ + // Do we need binary encoding? + if (!(val>>(3*(len-1)))) sprintf(str, "%0*llo", len-1, val); + else { + *str = 128; + while (--len) *++str = val>>(3*len); + } +} +#define ITOO(x, y) itoo(x, sizeof(x), y) + +//convert octal (or base-256) to int +static unsigned long long otoi(char *str, unsigned len) +{ + unsigned long long val = 0; + + // When tar value too big or octal, use binary encoding with high bit set + if (128&*str) while (--len) val = (val<<8)+*++str; + else { + while (len && *str>='0' && *str<='7') val = val*8+*str++-'0', len--; + if (len && *str && *str != ' ') error_exit("bad header"); + } + + return val; +} + + struct inode_list { struct inode_list *next; char *arg; @@ -83,13 +104,6 @@ struct inode_list { dev_t dev; }; -//convert to octal -static void itoo(char *str, int len, off_t val) -{ - sprintf(str, "%0*llo", len-1, (unsigned long long)val); -} - -// This really needs a hash table static struct inode_list *seen_inode(void **list, struct stat *st, char *name) { if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) { @@ -121,31 +135,37 @@ static unsigned cksum(void *data) static void write_longname(char *name, char type) { - struct tar_hdr tmp[2]; + struct tar_hdr tmp; int sz = strlen(name) +1; - memset(tmp, 0, sizeof(tmp)); - strcpy(tmp->name, "././@LongLink"); - memset(tmp->mode, '0', sizeof(tmp->mode)-1); - memset(tmp->uid, '0', sizeof(tmp->uid)-1); - memset(tmp->gid, '0', sizeof(tmp->gid)-1); - itoo(tmp->size, sizeof(tmp->size), sz); - memset(tmp->mtime, '0', sizeof(tmp->mtime)-1); - tmp->type = type; - strcpy(tmp->magic, "ustar "); - - // Calculate checksum - itoo(tmp->chksum, sizeof(tmp->chksum), cksum(&tmp)); + memset(&tmp, 0, sizeof(tmp)); + strcpy(tmp.name, "././@LongLink"); + ITOO(tmp.mode, 0); + ITOO(tmp.uid, 0); + ITOO(tmp.gid, 0); + ITOO(tmp.size, sz); + ITOO(tmp.mtime, 0); + tmp.type = type; + strcpy(tmp.magic, "ustar "); + + // Calculate checksum. Since 0777777 is twice 512*255 it can never use more + // than 6 digits, last byte is ' ' or historical reasons. + itoo(tmp.chksum, sizeof(tmp.chksum)-1, cksum(&tmp)); + tmp.chksum[7] = ' '; // write header and name, padded with NUL to block size - xwrite(TT.fd, tmp, sizeof(*tmp)); + xwrite(TT.fd, &tmp, 512); xwrite(TT.fd, name, sz); - xwrite(TT.fd, tmp+1, 512-(sz%512)); + if (sz%512) xwrite(TT.fd, toybuf, 512-(sz%512)); } -static int filter(struct arg_list *lst, char *name) +static struct double_list *filter(struct double_list *lst, char *name) { - for (; lst; lst = lst->next) if (!fnmatch(lst->arg, name, 1<<3)) return 1; + struct double_list *end = lst; + + if (lst) + do if (!fnmatch(lst->data, name, 1<<3)) return lst; + while (end != (lst = lst->next)); return 0; } @@ -169,15 +189,15 @@ static void alloread(void *buf, int len) static void add_file(char **nam, struct stat *st) { struct tar_hdr hdr; - struct passwd *pw; - struct group *gr; + struct passwd *pw = pw; + struct group *gr = gr; struct inode_list *node = node; int i, fd =-1; - char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,}; + char *c, *p, *name = *nam, *lnk, *hname; static int warn = 1; for (p = name; *p; p++) - if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.exc, p)) return; + if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.excl, p)) return; if (S_ISDIR(st->st_mode) && name[strlen(name)-1] != '/') { lnk = xmprintf("%s/",name); @@ -195,55 +215,52 @@ static void add_file(char **nam, struct stat *st) warn = 0; } + if (TT.owner) st->st_uid = TT.ouid; + if (TT.group) st->st_gid = TT.ggid; + memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, hname, sizeof(hdr.name)); - itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777); - itoo(hdr.uid, sizeof(hdr.uid), st->st_uid); - itoo(hdr.gid, sizeof(hdr.gid), st->st_gid); - itoo(hdr.size, sizeof(hdr.size), 0); //set size later - itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime); + ITOO(hdr.mode, st->st_mode &07777); + ITOO(hdr.uid, st->st_uid); + ITOO(hdr.gid, st->st_gid); + ITOO(hdr.size, 0); //set size later + ITOO(hdr.mtime, st->st_mtime); // Hard link or symlink? i = !!S_ISLNK(st->st_mode); if (i || (node = seen_inode(&TT.inodes, st, hname))) { -// TODO: test preserve symlink ownership hdr.type = '1'+i; if (!(lnk = i ? xreadlink(name) : node->arg)) return perror_msg("readlink"); -// TODO: does this need NUL terminator? - if (strlen(lnk) > sizeof(hdr.link)) - write_longname(lnk, 'K'); //write longname LINK -// TODO: this will error_exit() if too long, not truncate. - xstrncpy(hdr.link, lnk, sizeof(hdr.link)); + if (strlen(lnk) > sizeof(hdr.link)) write_longname(lnk, 'K'); + strncpy(hdr.link, lnk, sizeof(hdr.link)); if (i) free(lnk); } else if (S_ISREG(st->st_mode)) { hdr.type = '0'; - if (st->st_size <= (off_t)077777777777LL) - itoo(hdr.size, sizeof(hdr.size), st->st_size); - else { -// TODO: test accept 12 7's but don't emit without terminator - return error_msg("TODO: need base-256 encoding for '%s' '%lld'\n", - hname, (unsigned long long)st->st_size); - } + ITOO(hdr.size, st->st_size); } else if (S_ISDIR(st->st_mode)) hdr.type = '5'; else if (S_ISFIFO(st->st_mode)) hdr.type = '6'; else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { hdr.type = (S_ISCHR(st->st_mode))?'3':'4'; - itoo(hdr.major, sizeof(hdr.major), dev_major(st->st_rdev)); - itoo(hdr.minor, sizeof(hdr.minor), dev_minor(st->st_rdev)); + ITOO(hdr.major, dev_major(st->st_rdev)); + ITOO(hdr.minor, dev_minor(st->st_rdev)); } else return error_msg("unknown file type '%o'", st->st_mode & S_IFMT); - if (strlen(hname) > sizeof(hdr.name)) - write_longname(hname, 'L'); //write longname NAME + if (strlen(hname) > sizeof(hdr.name)) write_longname(hname, 'L'); strcpy(hdr.magic, "ustar "); - if ((pw = bufgetpwuid(st->st_uid))) - snprintf(hdr.uname, sizeof(hdr.uname), "%s", pw->pw_name); - else sprintf(hdr.uname, "%d", st->st_uid); - - if ((gr = bufgetgrgid(st->st_gid))) - snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name); - else sprintf(hdr.gname, "%d", st->st_gid); + if (!FLAG(numeric_owner)) { + if (!TT.owner && !(pw = bufgetpwuid(st->st_uid))) + sprintf(hdr.uname, "%d", st->st_uid); + else snprintf(hdr.uname, sizeof(hdr.uname), "%s", + TT.owner ? TT.owner : pw->pw_name); + if (!TT.group && !(gr = bufgetgrgid(st->st_gid))) + sprintf(hdr.gname, "%d", st->st_gid); + else snprintf(hdr.gname, sizeof(hdr.gname), "%s", + TT.group ? TT.group : gr->gr_name); + } itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr)); + hdr.chksum[7] = ' '; + if (FLAG(v)) printf("%s\n",hname); xwrite(TT.fd, (void*)&hdr, 512); @@ -254,7 +271,7 @@ static void add_file(char **nam, struct stat *st) return; } xsendfile_pad(fd, TT.fd, st->st_size); - if (st->st_size%512) writeall(TT.fd, buf, (512-(st->st_size%512))); + if (st->st_size%512) writeall(TT.fd, toybuf, (512-(st->st_size%512))); close(fd); } @@ -273,8 +290,8 @@ static int add_to_tar(struct dirtree *node) path = dirtree_path(node, 0); add_file(&path, &(node->st)); //path may be modified free(path); - if (FLAG(no_recursion)) return 0; - return ((DIRTREE_RECURSE | (FLAG(h)?DIRTREE_SYMFOLLOW:0))); + + return (DIRTREE_RECURSE|(FLAG(h)?DIRTREE_SYMFOLLOW:0))*!FLAG(no_recursion); } // Does anybody actually use this? @@ -283,13 +300,10 @@ static void extract_to_command(void) int pipefd[2], status = 0; pid_t cpid; - xpipe(pipefd); if (!S_ISREG(TT.hdr.mode)) return; //only regular files are supported. - cpid = fork(); - if (cpid == -1) perror_exit("fork"); - - if (!cpid) { // Child reads from pipe + xpipe(pipefd); + if (!(cpid = xfork())) { // Child reads from pipe char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL}; setenv("TAR_FILETYPE", "f", 1); @@ -327,7 +341,6 @@ static void extract_to_disk(void) char *s; struct stat ex; -// while not if flags = strlen(TT.hdr.name); if (flags>2) if (strstr(TT.hdr.name, "/../") || !strcmp(TT.hdr.name, "../") || @@ -381,25 +394,30 @@ COPY: xsendfile_len(TT.fd, dst_fd, TT.hdr.size); close(dst_fd); - if (!FLAG(o)) { + if (!FLAG(o) && !geteuid()) { //set ownership..., --no-same-owner, --numeric-owner uid_t u = TT.hdr.uid; gid_t g = TT.hdr.gid; - if (!FLAG(numeric_owner)) { - struct group *gr = getgrnam(TT.hdr.gname); + if (TT.owner) u = TT.ouid; + else if (!FLAG(numeric_owner)) { struct passwd *pw = getpwnam(TT.hdr.uname); - if (pw) u = pw->pw_uid; + if (pw && (TT.owner || !FLAG(numeric_owner))) u = pw->pw_uid; + } + + if (TT.group) g = TT.ggid; + else if (!FLAG(numeric_owner)) { + struct group *gr = getgrnam(TT.hdr.gname); if (gr) g = gr->gr_gid; } - if (!geteuid() && lchown(TT.hdr.name, u, g)) + + if (lchown(TT.hdr.name, u, g)) perror_msg("chown %d:%d '%s'", u, g, TT.hdr.name);; } // || !FLAG(no_same_permissions)) if (FLAG(p) && !S_ISLNK(TT.hdr.mode)) chmod(TT.hdr.name, TT.hdr.mode); -// TODO: defer directory mtime until we've finished with contents //apply mtime if (!FLAG(m)) { struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}}; @@ -407,59 +425,30 @@ COPY: } } -static void add_to_list(struct arg_list **llist, char *name) -{ - struct arg_list **list = llist; - - while (*list) list=&((*list)->next); - *list = xzalloc(sizeof(struct arg_list)); - (*list)->arg = name; - if ((name[strlen(name)-1] == '/') && strlen(name) != 1) - name[strlen(name)-1] = '\0'; -} - -static void file_to_list(char *file, struct arg_list **llist) -{ - int fd = xopenro(file); - char *line = 0; - - while ((line = get_line(fd))) add_to_list(llist, xstrdup(line)); - if (fd) close(fd); - free(line); -} - -//convert octal to int -static unsigned long long otoi(char *str, int len) -{ - unsigned long long val; - -// todo: base-256 encoding, just do it symmetrically for all fields - str[len-1] = 0; - val = strtoull(str, &str, 8); - if (*str && *str != ' ') error_exit("bad header"); - - return val; -} - static void unpack_tar(void) { + struct double_list *walk, *delete; struct tar_hdr tar; - int i; + int i, and = 0; char *s; for (;;) { // align to next block and read it if (TT.hdr.size%512) skippy(512-TT.hdr.size%512); + if (!(i = readall(TT.fd, &tar, 512))) return; - i = readall(TT.fd, &tar, 512); if (i != 512) error_exit("read error"); + + // Two consecutive empty headers ends tar even if there's more data + if (!*tar.name) { + if (and++) return; + TT.hdr.size = 0; + continue; + } // ensure null temination even of pathological packets - tar.padd[0] = 0; - // End of tar - if (!*tar.name) return; + tar.padd[0] = and = 0; -// can you append a bzip to a gzip _within_ a tarball? Nested compress? -// Or compressed data after uncompressed data? + // Is this a valid Unix Standard TAR header? if (memcmp(tar.magic, "ustar", 5)) error_exit("bad header"); if (cksum(&tar) != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("bad cksum"); @@ -467,6 +456,8 @@ static void unpack_tar(void) // If this header isn't writing something to the filesystem if (tar.type<'0' || tar.type>'7') { + + // Long name extension header? if (tar.type == 'K') alloread(&TT.hdr.link_target, TT.hdr.size); else if (tar.type == 'L') alloread(&TT.hdr.name, TT.hdr.size); else if (tar.type == 'x') { @@ -479,7 +470,7 @@ static void unpack_tar(void) if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 || len>TT.hdr.size) { - error_msg("corrupted extended header"); + error_msg("bad header"); break; } p[len-1] = 0; @@ -490,14 +481,13 @@ static void unpack_tar(void) } free(buf); - // This could be if (strchr("DMNSVg", tar.type)) but an unknown header - // type with trailing contents is unlikely to have a valid type & cksum + // Ignore everything else. } else skippy(TT.hdr.size); continue; } - // At this point, we're writing something to the filesystem. Parse fields. + // At this point, we have something to output. Convert metadata. TT.hdr.mode = otoi(tar.mode, sizeof(tar.mode)); TT.hdr.mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12; TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid)); @@ -506,57 +496,66 @@ static void unpack_tar(void) TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)), otoi(tar.minor, sizeof(tar.minor))); - TT.hdr.uname = xstrndup(tar.uname, sizeof(tar.uname)); - TT.hdr.gname = xstrndup(tar.gname, sizeof(tar.gname)); + TT.hdr.uname = xstrndup(TT.owner ? TT.owner : tar.uname,sizeof(tar.uname)); + TT.hdr.gname = xstrndup(TT.group ? TT.group : tar.gname,sizeof(tar.gname)); if (!TT.hdr.link_target && *tar.link) TT.hdr.link_target = xstrndup(tar.link, sizeof(tar.link)); if (!TT.hdr.name) { + // Glue prefix and name fields together with / if necessary i = strnlen(tar.prefix, sizeof(tar.prefix)); TT.hdr.name = xmprintf("%.*s%s%.*s", i, tar.prefix, (i && tar.prefix[i-1] != '/') ? "/" : "", (int)sizeof(tar.name), tar.name); } - // Directories sometimes recorded as "file with trailing slash" + // Old broken tar recorded dir as "file with trailing slash" if (S_ISREG(TT.hdr.mode) && (s = strend(TT.hdr.name, "/"))) { *s = 0; TT.hdr.mode = (TT.hdr.mode & ~S_IFMT) | S_IFDIR; } - // Hardlinks, symlinks, and directories do not have contents in archive - // (Neither do fifo, block or char devices, but not testing for that...?) - if ((TT.hdr.link_target && *TT.hdr.link_target) - || S_ISLNK(TT.hdr.mode) || S_ISDIR(TT.hdr.mode)) + // Non-regular files don't have contents stored in archive. + if ((TT.hdr.link_target && *TT.hdr.link_target) || !S_ISREG(TT.hdr.mode)) TT.hdr.size = 0; + // Files are seen even if excluded, so check them here. + // TT.seen points to first seen entry in TT.incl, or NULL if none yet. + if ((delete = filter(TT.incl, TT.hdr.name)) && TT.incl != TT.seen) { + if (!TT.seen) TT.seen = delete; + + // Move seen entry to end of list. + if (TT.incl == delete) TT.incl = TT.incl->next; + else for (walk = TT.incl; walk != TT.seen; walk = walk->next) { + if (walk == delete) { + dlist_pop(&walk); + dlist_add_nomalloc(&TT.incl, delete); + } + } + } + // Skip excluded files - if (filter(TT.exc, TT.hdr.name) || (TT.inc && !filter(TT.inc, TT.hdr.name))) + if (filter(TT.excl, TT.hdr.name) || (TT.incl && !delete)) skippy(TT.hdr.size); - else { - -// TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead - add_to_list(&TT.pass, xstrdup(TT.hdr.name)); - - if (FLAG(t)) { - if (FLAG(v)) { - char perm[11]; - struct tm *lc = localtime(&TT.hdr.mtime); - - mode_to_string(TT.hdr.mode, perm); - printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ", perm, - TT.hdr.uname, TT.hdr.gname, (long)TT.hdr.size, 1900+lc->tm_year, - 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec); - } - printf("%s", TT.hdr.name); - if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target); - xputc('\n'); - skippy(TT.hdr.size); - } else { - if (FLAG(v)) printf("%s\n", TT.hdr.name); - if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size); - else if (FLAG(to_command)) extract_to_command(); - else extract_to_disk(); + else if (FLAG(t)) { + if (FLAG(v)) { + struct tm *lc = localtime(&TT.hdr.mtime); + char perm[11]; + + mode_to_string(TT.hdr.mode, perm); + printf("%s %s/%s %9lld %d-%02d-%02d %02d:%02d:%02d ", perm, + TT.hdr.uname, TT.hdr.gname, (long long)TT.hdr.size, + 1900+lc->tm_year, 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, + lc->tm_min, lc->tm_sec); } + printf("%s", TT.hdr.name); + if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target); + xputc('\n'); + skippy(TT.hdr.size); + } else { + if (FLAG(v)) printf("%s\n", TT.hdr.name); + if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size); + else if (FLAG(to_command)) extract_to_command(); + else extract_to_disk(); } free(TT.hdr.name); @@ -567,24 +566,37 @@ static void unpack_tar(void) } } +// Add copy of filename to TT.incl or TT.excl, minus trailing \n and / +static void trim_list(char **pline, long len) +{ + char *n = strdup(*pline); + int i = strlen(n); + + dlist_add(TT.X ? &TT.excl : &TT.incl, n); + if (i && n[i-1]=='\n') i--; + while (i && n[i-1] == '/') i--; + n[i] = 0; +} + void tar_main(void) { - struct arg_list *tmp; char *s, **args = toys.optargs; // When extracting to command signal(SIGPIPE, SIG_IGN); if (!geteuid()) toys.optflags |= FLAG_p; + if (TT.owner) TT.ouid = xgetuid(TT.owner); + if (TT.group) TT.ggid = xgetgid(TT.group); - // Collect file list - while (*args) add_to_list(&TT.inc, *args++); - for (;TT.T; TT.T = TT.T->next) file_to_list(TT.T->arg, &TT.inc); - for (;TT.X; TT.X = TT.X->next) file_to_list(TT.X->arg, &TT.exc); + // Collect file list. Note: trim_list appends to TT.incl when !TT.X + for (;TT.X; TT.X = TT.X->next) do_lines(xopenro(TT.X->arg), '\n', trim_list); + for (args = toys.optargs; *args; args++) trim_list(args, strlen(*args)); + for (;TT.T; TT.T = TT.T->next) do_lines(xopenro(TT.T->arg), '\n', trim_list); // Open archive file if (FLAG(c)) { - if (!TT.inc) error_exit("empty archive"); + if (!TT.incl) error_exit("empty archive"); TT.fd = 1; } if (TT.f && strcmp(TT.f, "-")) @@ -597,15 +609,6 @@ void tar_main(void) // Are we reading? if (FLAG(x)||FLAG(t)) { -// TODO: autodtect - -// Try detecting .gz or .bz2 by looking for their magic. -// if ((!memcmp(tar.name, "\x1f\x8b", 2) || !memcmp(tar.name, "BZh", 3)) -// && !lseek(TT.fd, -i, SEEK_CUR)) { -// toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z; -// extract_stream(tar_hdl); -// continue; -// } if (FLAG(j)||FLAG(z)) { int pipefd[2] = {TT.fd, -1}; @@ -616,13 +619,18 @@ void tar_main(void) } unpack_tar(); - for (tmp = TT.inc; tmp; tmp = tmp->next) - if (!filter(TT.exc, tmp->arg) && !filter(TT.pass, tmp->arg)) - error_msg("'%s' not in archive", tmp->arg); + if (TT.seen != TT.incl) { + if (!TT.seen) TT.seen = TT.incl; + while (TT.incl != TT.seen) { + error_msg("'%s' not in archive", TT.incl->data); + TT.incl = TT.incl->next; + } + } // are we writing? (Don't have to test flag here one of 3 must be set) } else { -// TODO: autodetect + struct double_list *dl = TT.incl; + if (FLAG(j)||FLAG(z)) { int pipefd[2] = {-1, TT.fd}; @@ -630,10 +638,9 @@ void tar_main(void) close(TT.fd); TT.fd = pipefd[0]; } - for (tmp = TT.inc; tmp; tmp = tmp->next) - dirtree_flagread(tmp->arg, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); + do dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); + while (TT.incl != (dl = dl->next)); - memset(toybuf, 0, 1024); writeall(TT.fd, toybuf, 1024); } } |