Make --xform work on archive creation side, with some tests.
diff --git a/tests/tar.test b/tests/tar.test
index df63c2f..43005a4 100755
--- a/tests/tar.test
+++ b/tests/tar.test
@@ -32,6 +32,11 @@
tar tv "$@" | sed 's/[ \t][ \t]*/ /g'
}
+function LST2()
+{
+ LST | sed 's/^.* 23:31 //'
+}
+
# Check that stored empty file is binary identical and decodes as expected.
touch file
testing "store file" "$TAR file | SUM 3" \
@@ -318,16 +323,49 @@
"three/four/five/six\n" "" ""
# toybox tar --xform depends on toybox sed
-sed --tarxform '' </dev/null 2>/dev/null || SKIP=99
-testing "--xform" "$TAR one --xform=s@three/four/@zero@ | tar t | grep six" \
+[ -z "$TEST_HOST" ] && ! sed --tarxform '' </dev/null 2>/dev/null && SKIP=99
+
+mkdir uno
+ln -s tres uno/dos
+touch uno/tres
+ln uno/tres uno/quatro
+testing 'xform S' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 's/uno/one/S;s/dos/two/S;s/tres/three/S;s/quatro/four/S' | LST2" \
+ "one/\none/two -> tres\none/three\none/four link to one/three\n" "" ""
+
+testing 'xform flags=rh starts with all disabled' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 's/uno/one/;flags=rh;s/dos/two/;s/tres/three/;s/quatro/four/' | LST2" \
+ "one/\none/two -> tres\none/three\none/four link to one/three\n" "" ""
+
+testing 'xform flags=rHhsS toggles' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 's/uno/one/;flags=rHhsS;s/dos/two/;s/tres/three/;s/quatro/four/' | LST2" \
+ "one/\none/two -> tres\none/three\none/four link to one/three\n" "" ""
+
+testing 'xform flags= is not a delta from previous' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 'flags=s;flags=rh;s/uno/one/;s/dos/two/;s/tres/three/;s/quatro/four/' | LST2" \
+ "one/\none/two -> tres\none/three\none/four link to one/three\n" "" ""
+
+testing 'xform H' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 'flags=rsH;s/uno/one/;s/dos/two/;s/tres/three/;s/quatro/four/' | LST2" \
+ "one/\none/two -> three\none/three\none/four link to uno/tres\n" "" ""
+
+testing 'xform R' \
+ "$TAR --no-recursion uno uno/{dos,tres,quatro} --xform 'flags=rshR;s/uno/one/;s/dos/two/;s/tres/three/;s/quatro/four/' | LST2" \
+ "uno/\nuno/dos -> three\nuno/tres\nuno/quatro link to one/three\n" "" ""
+
+testing "xform path" "$TAR one --xform=s@three/four/@zero@ | tar t | grep six" \
"one/two/zerofive/six\n" "" ""
-rm -rf one
+
+testing "xform trailing slash special case" \
+ "$TAR --xform 's#^.+/##x' one/two/three/four/five | tar t" 'five/\nsix\n' '' ''
+rm -rf uno
SKIP=0
+rm -rf one
testing '-P' "$TAR -P --no-recursion -C / /// .. | SUM 3" \
"a3e94211582da121845d823374d8f41ead62d7bd\n" "" ""
-testing 'without -P' "$TAR --no-recursion -C / /// .. | SUM 3" \
+testing 'without -P' "$TAR --no-recursion -C / /// .. 2>/dev/null | SUM 3" \
"077d03243e247b074806904885e6da272fd5857a\n" "" ""
if false
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index 7441a85..67795af 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -474,7 +474,7 @@
// xform matches ending in / aren't allowed to match entire line
if ((command->sflags & SFLAG_slash) && mlen==len) {
- while (len && line[--len]=='/') bonk++;
+ while (len && ++bonk && line[--len]=='/');
continue;
}
@@ -793,9 +793,10 @@
if (!*line) return;
if (FLAG(tarxform) && strstart(&line, "flags=")) {
+ TT.xflags = 7;
while (0<=(i = stridx("rRsShH", *line))) {
- if (i&1) TT.xflags |= i>>1;
- else TT.xflags &= ~(i>>1);
+ if (i&1) TT.xflags |= 1<<(i>>1);
+ else TT.xflags &= ~(1<<(i>>1));
line++;
}
continue;
diff --git a/toys/posix/tar.c b/toys/posix/tar.c
index f75b33f..44a3b3b 100644
--- a/toys/posix/tar.c
+++ b/toys/posix/tar.c
@@ -236,7 +236,7 @@
}
// Consume the 1 extra byte alocated in dirtree_path()
- if (S_ISDIR(st->st_mode) && name[i-1] != '/') strcat(name, "/");
+ if (S_ISDIR(st->st_mode) && lnk[-1] != '/') strcpy(lnk, "/");
// remove leading / and any .. entries from saved name
if (!FLAG(P)) {
@@ -254,7 +254,6 @@
}
if (!*hname) hname = "./";
}
-
if (!*hname) goto done;
if (TT.warn && hname != name) {
@@ -263,6 +262,7 @@
TT.warn = 0;
}
+ // Override dentry data from command line and fill out header data
if (TT.owner) st->st_uid = TT.ouid;
if (TT.group) st->st_gid = TT.ggid;
if (TT.mode) st->st_mode = string_to_mode(TT.mode, st->st_mode);
@@ -275,19 +275,13 @@
ITOO(hdr.mtime, st->st_mtime);
strcpy(hdr.magic, "ustar ");
- xfname = xform(&hname, 'r');
- strncpy(hdr.name, hname, sizeof(hdr.name));
-
- // Hard link or symlink? i=0 neither, i=1 hardlink, i=2 symlink
-
// Are there hardlinks to a non-directory entry?
+ lnk = 0;
if (st->st_nlink>1 && !S_ISDIR(st->st_mode)) {
// Have we seen this dev&ino before?
for (i = 0; i<TT.hlc; i++) if (same_dev_ino(st, &TT.hlx[i].di)) break;
- if (i != TT.hlc) {
- lnk = TT.hlx[i].arg;
- i = 1;
- } else {
+ if (i != TT.hlc) lnk = TT.hlx[i].arg;
+ else {
// first time we've seen it. Store as normal file, but remember it.
if (!(TT.hlc&255))
TT.hlx = xrealloc(TT.hlx, sizeof(*TT.hlx)*(TT.hlc+256));
@@ -295,20 +289,25 @@
TT.hlx[TT.hlc].di.ino = st->st_ino;
TT.hlx[TT.hlc].di.dev = st->st_dev;
TT.hlc++;
- i = 0;
}
- } else i = 0;
+ }
- // Handle file types
- if (i || S_ISLNK(st->st_mode)) {
- hdr.type = '1'+!i;
- if (!i && !(lnk = xreadlink(name))) {
+ xfname = xform(&hname, 'r');
+ strncpy(hdr.name, hname, sizeof(hdr.name));
+
+ // Handle file types: 0=reg, 1=hardlink, 2=sym, 3=chr, 4=blk, 5=dir, 6=fifo
+ if (lnk || S_ISLNK(st->st_mode)) {
+ hdr.type = '1'+!lnk;
+ if (lnk) {
+ if (!xform(&lnk, 'h')) lnk = xstrdup(lnk);
+ } else if (!(lnk = xreadlink(name))) {
perror_msg("readlink");
goto done;
- }
+ } else xform(&lnk, 's');
+
maybe_prefix_block(lnk, sizeof(hdr.link), 'K');
strncpy(hdr.link, lnk, sizeof(hdr.link));
- if (!i) free(lnk);
+ free(lnk);
} else if (S_ISREG(st->st_mode)) {
hdr.type = '0';
ITOO(hdr.size, st->st_size);
@@ -833,7 +832,8 @@
lc->tm_mday, lc->tm_hour, lc->tm_min, FLAG(full_time) ? perm : "");
}
printf("%s", name);
- if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target);
+ if (TT.hdr.link_target)
+ printf(" %s %s", tar.type=='2' ? "->" : "link to", TT.hdr.link_target);
xputc('\n');
skippy(TT.hdr.size);
} else {