]> arthur.barton.de Git - bup.git/commitdiff
Fix the ls -laAF options with respect to the VFS synthetic directories.
authorRob Browning <rlb@defaultvalue.org>
Thu, 30 Jan 2014 22:26:44 +0000 (16:26 -0600)
committerRob Browning <rlb@defaultvalue.org>
Tue, 4 Feb 2014 19:22:41 +0000 (13:22 -0600)
Before this, they wouldn't work in various cases where the VFS was
creating synthetic data (like the top-level branch and commit-list
trees).

Oh, and how about some tests?

Thanks to Gabriel Filion <gabster@lelutin.ca> for reporting the
problem.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Makefile
lib/bup/ls.py
lib/bup/metadata.py
lib/bup/xstat.py
t/test-ls.sh [new file with mode: 0755]

index 8eaec0b450781ac5dd97a085fbe19e0837bfd95d..a2a22bf7c8e1875c3e4cdfb3a7effde151577997 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -94,6 +94,7 @@ runtests-cmdline: all
        TMPDIR="$(test_tmp)" t/test-fsck.sh
        TMPDIR="$(test_tmp)" t/test-index-clear.sh
        TMPDIR="$(test_tmp)" t/test-index-check-device.sh
+       TMPDIR="$(test_tmp)" t/test-ls.sh
        TMPDIR="$(test_tmp)" t/test-meta.sh
        TMPDIR="$(test_tmp)" t/test-restore-map-owner.sh
        TMPDIR="$(test_tmp)" t/test-restore-single-file.sh
index 002e15d60f824f97c474deb96fa50b7c31d5352a..66e421d40c254b33744aa4f85f91a26418c84753 100644 (file)
@@ -1,5 +1,5 @@
 """Common code for listing files from a bup repository."""
-import copy, stat
+import copy, stat, xstat
 from bup import metadata, options, vfs
 from helpers import *
 
@@ -11,36 +11,32 @@ def node_info(n, name,
               numeric_ids = False,
               human_readable = False):
     """Return a string containing the information to display for the node
-    n.  Classification may be "all", "type", or None.
-
-    """
+    n.  Classification may be "all", "type", or None."""
     result = ''
     if show_hash:
         result += "%s " % n.hash.encode('hex')
     if long_fmt:
         meta = copy.copy(n.metadata())
-        meta.size = n.size()
+        if meta:
+            meta.path = name
+        else:
+            # Fake it -- summary_str() is designed to handle a fake.
+            meta = metadata.Metadata()
+            meta.size = n.size()
+            meta.mode = n.mode
+            meta.path = name
+            meta.atime, meta.mtime, meta.ctime = n.atime, n.mtime, n.ctime
+            if stat.S_ISLNK(meta.mode):
+                meta.symlink_target = n.readlink()
         result += metadata.summary_str(meta,
                                        numeric_ids = numeric_ids,
-                                       human_readable = human_readable) + ' '
-    result += name
-    if classification:
-        if n.metadata():
-            mode = n.metadata().mode
-        else:
-            mode = n.mode
-        if stat.S_ISREG(mode):
-            if classification == 'all' \
-               and stat.S_IMODE(mode) & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
-                result += '*'
-        elif stat.S_ISDIR(mode):
-            result += '/'
-        elif stat.S_ISLNK(mode):
-            result += '@'
-        elif stat.S_ISFIFO(mode):
-            result += '|'
-        elif stat.S_ISSOCK(mode):
-            result += '='
+                                       classification = classification,
+                                       human_readable = human_readable)
+    else:
+        result += name
+        if classification:
+            mode = n.metadata() and n.metadata().mode or n.mode
+            result += xstat.classification_str(mode, classification == 'all')
     return result
 
 
@@ -106,8 +102,12 @@ def do_ls(args, pwd, default='.', onabort=None, spec_prefix=''):
 
             if stat.S_ISDIR(n.mode):
                 if show_hidden == 'all':
-                    for implied, name in ((n, '.'), (n.parent, '..')):
-                        output_node_info(implied, name)
+                    output_node_info(n, '.')
+                    # Match non-bup "ls -a ... /".
+                    if n.parent:
+                        output_node_info(n.parent, '..')
+                    else:
+                        output_node_info(n, '..')
                 for sub in n:
                     name = sub.name
                     if show_hidden in ('almost', 'all') \
index 0622d2b98652fb374f61cfba16acb00cdaa81c00..05b47bd7b1b8f4c70394ab1eeee7c2c5c10c4cdc 100644 (file)
@@ -889,31 +889,57 @@ all_fields = frozenset(['path',
                         'posix1e-acl'])
 
 
-def summary_str(meta, numeric_ids = False, human_readable = False):
-    mode_val = xstat.mode_str(meta.mode)
-    user_val = meta.user
-    if numeric_ids or not user_val:
-        user_val = str(meta.uid)
-    group_val = meta.group
-    if numeric_ids or not group_val:
-        group_val = str(meta.gid)
-    size_or_dev_val = '-'
-    if stat.S_ISCHR(meta.mode) or stat.S_ISBLK(meta.mode):
-        size_or_dev_val = '%d,%d' % (os.major(meta.rdev), os.minor(meta.rdev))
-    elif meta.size != None:
-        size_or_dev_val = meta.size
-        if human_readable:
-            size_or_dev_val = format_filesize(meta.size)
-    mtime_secs = xstat.fstime_floor_secs(meta.mtime)
-    time_val = time.strftime('%Y-%m-%d %H:%M', time.localtime(mtime_secs))
-    path_val = meta.path or ''
-    if stat.S_ISLNK(meta.mode):
-        path_val += ' -> ' + meta.symlink_target
-    return '%-10s %-11s %11s %16s %s' % (mode_val,
-                                         user_val + "/" + group_val,
-                                         size_or_dev_val,
-                                         time_val,
-                                         path_val)
+def summary_str(meta, numeric_ids = False, classification = None,
+                human_readable = False):
+
+    """Return a string containing the "ls -l" style listing for meta.
+    Classification may be "all", "type", or None."""
+    user_str = group_str = size_or_dev_str = '?'
+    symlink_target = None
+    if meta:
+        name = meta.path
+        mode_str = xstat.mode_str(meta.mode)
+        symlink_target = meta.symlink_target
+        mtime_secs = xstat.fstime_floor_secs(meta.mtime)
+        mtime_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(mtime_secs))
+        if meta.user and not numeric_ids:
+            user_str = meta.user
+        elif meta.uid != None:
+            user_str = str(meta.uid)
+        if meta.group and not numeric_ids:
+            group_str = meta.group
+        elif meta.gid != None:
+            group_str = str(meta.gid)
+        if stat.S_ISCHR(meta.mode) or stat.S_ISBLK(meta.mode):
+            if meta.rdev:
+                size_or_dev_str = '%d,%d' % (os.major(meta.rdev),
+                                             os.minor(meta.rdev))
+        elif meta.size:
+            if human_readable:
+                size_or_dev_str = format_filesize(meta.size)
+            else:
+                size_or_dev_str = str(meta.size)
+        else:
+            size_or_dev_str = '-'
+        if classification:
+            classification_str = \
+                xstat.classification_str(meta.mode, classification == 'all')
+    else:
+        mode_str = '?' * 10
+        mtime_str = '????-??-?? ??:??'
+        classification_str = '?'
+
+    name = name or ''
+    if classification:
+        name += classification_str
+    if symlink_target:
+        name += ' -> ' + meta.symlink_target
+
+    return '%-10s %-11s %11s %16s %s' % (mode_str,
+                                         user_str + "/" + group_str,
+                                         size_or_dev_str,
+                                         mtime_str,
+                                         name)
 
 
 def detailed_str(meta, fields = None):
index 8daa2c2509e34246f917cadf08e94046aa4d02f1..0eee9b2af2d956d495d4775f207ecd0e613c414f 100644 (file)
@@ -116,6 +116,7 @@ def lstat(path):
 
 def mode_str(mode):
     result = ''
+    # FIXME: Other types?
     if pystat.S_ISREG(mode):
         result += '-'
     elif pystat.S_ISDIR(mode):
@@ -143,3 +144,23 @@ def mode_str(mode):
     result += 'w' if (mode & pystat.S_IWOTH) else '-'
     result += 'x' if (mode & pystat.S_IXOTH) else '-'
     return result
+
+
+def classification_str(mode, include_exec):
+    if pystat.S_ISREG(mode):
+        if include_exec \
+           and (pystat.S_IMODE(mode) \
+                & (pystat.S_IXUSR | pystat.S_IXGRP | pystat.S_IXOTH)):
+            return '*'
+        else:
+            return ''
+    elif pystat.S_ISDIR(mode):
+        return '/'
+    elif pystat.S_ISLNK(mode):
+        return '@'
+    elif pystat.S_ISFIFO(mode):
+        return '|'
+    elif pystat.S_ISSOCK(mode):
+        return '='
+    else:
+        return ''
diff --git a/t/test-ls.sh b/t/test-ls.sh
new file mode 100755 (executable)
index 0000000..db5772a
--- /dev/null
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+. ./wvtest-bup.sh
+
+set -o pipefail
+
+top="$(WVPASS pwd)" || exit $?
+tmpdir="$(WVPASS wvmktempdir)" || exit $?
+
+export BUP_DIR="$tmpdir/bup"
+export GIT_DIR="$tmpdir/bup"
+
+bup() { "$top/bup" "$@"; }
+
+WVPASS bup init
+WVPASS cd "$tmpdir"
+
+WVPASS mkdir src
+WVPASS touch -t 191111111111 src/.dotfile
+WVPASS date > src/file
+WVPASS touch -t 191111111111 src/file
+(WVPASS cd src; WVPASS ln -s file symlink) || exit $?
+WVPASS mkfifo src/fifo
+WVPASS touch -t 191111111111 src/fifo
+WVPASS "$top"/t/mksock src/socket
+WVPASS touch -t 191111111111 src/socket
+WVPASS touch -t 191111111111 src/executable
+WVPASS chmod u+x src/executable
+WVPASS touch -t 191111111111 src/executable
+WVPASS touch -t 191111111111 src
+WVPASS touch -t 191111111111 .
+WVPASS chmod -R u=rwX,g-rwx,o-rwx .
+WVPASS bup index src
+WVPASS bup save -n src src
+
+WVSTART "ls (short)"
+
+WVPASSEQ "$(WVPASS bup ls /)" "src"
+
+WVPASSEQ "$(WVPASS bup ls -A /)" ".commit
+.tag
+src"
+
+WVPASSEQ "$(WVPASS bup ls -AF /)" ".commit/
+.tag/
+src/"
+
+WVPASSEQ "$(WVPASS bup ls -a /)" ".
+..
+.commit
+.tag
+src"
+
+WVPASSEQ "$(WVPASS bup ls -aF /)" "./
+../
+.commit/
+.tag/
+src/"
+
+WVPASSEQ "$(WVPASS bup ls src/latest/"$tmpdir"/src)" "executable
+fifo
+file
+socket
+symlink"
+
+WVPASSEQ "$(WVPASS bup ls -A src/latest/"$tmpdir"/src)" ".dotfile
+executable
+fifo
+file
+socket
+symlink"
+
+WVPASSEQ "$(WVPASS bup ls -a src/latest/"$tmpdir"/src)" ".
+..
+.dotfile
+executable
+fifo
+file
+socket
+symlink"
+
+WVPASSEQ "$(WVPASS bup ls -F src/latest/"$tmpdir"/src)" "executable*
+fifo|
+file
+socket=
+symlink@"
+
+WVPASSEQ "$(WVPASS bup ls --file-type src/latest/"$tmpdir"/src)" "executable
+fifo|
+file
+socket=
+symlink@"
+
+WVSTART "ls (long)"
+
+WVPASSEQ "$(WVPASS bup ls -l / | tr -s ' ' ' ')" \
+"d--------- ?/? - 1969-12-31 18:00 src"
+
+WVPASSEQ "$(WVPASS bup ls -lA / | tr -s ' ' ' ')" \
+"d--------- ?/? - 1969-12-31 18:00 .commit
+d--------- ?/? - 1969-12-31 18:00 .tag
+d--------- ?/? - 1969-12-31 18:00 src"
+
+WVPASSEQ "$(WVPASS bup ls -lAF / | tr -s ' ' ' ')" \
+"d--------- ?/? - 1969-12-31 18:00 .commit/
+d--------- ?/? - 1969-12-31 18:00 .tag/
+d--------- ?/? - 1969-12-31 18:00 src/"
+
+WVPASSEQ "$(WVPASS bup ls -la / | tr -s ' ' ' ')" \
+"d--------- ?/? - 1969-12-31 18:00 .
+d--------- ?/? - 1969-12-31 18:00 ..
+d--------- ?/? - 1969-12-31 18:00 .commit
+d--------- ?/? - 1969-12-31 18:00 .tag
+d--------- ?/? - 1969-12-31 18:00 src"
+
+WVPASSEQ "$(WVPASS bup ls -laF / | tr -s ' ' ' ')" \
+"d--------- ?/? - 1969-12-31 18:00 ./
+d--------- ?/? - 1969-12-31 18:00 ../
+d--------- ?/? - 1969-12-31 18:00 .commit/
+d--------- ?/? - 1969-12-31 18:00 .tag/
+d--------- ?/? - 1969-12-31 18:00 src/"
+
+symlink_date="$(bup ls -l src/latest"$tmpdir"/src | grep symlink)"
+symlink_date="$(echo "$symlink_date" \
+  | perl -ne 'm/.*? - (\d\d\d\d-\d\d-\d\d \d\d:\d\d)/ and print $1')"
+uid="$(id -u)" || exit $?
+gid="$(id -g)" || exit $?
+user="$(id -un)" || exit $?
+group="$(id -gn)" || exit $?
+
+WVPASSEQ "$(bup ls -l src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"-rwx------ $user/$group - 1911-11-11 11:11 executable
+prw------- $user/$group - 1911-11-11 11:11 fifo
+-rw------- $user/$group - 1911-11-11 11:11 file
+srwx------ $user/$group - 1911-11-11 11:11 socket
+lrwxrwxrwx $user/$group - $symlink_date symlink -> file"
+
+WVPASSEQ "$(bup ls -la src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"drwx------ $user/$group - 1911-11-11 11:11 .
+drwx------ $user/$group - 1911-11-11 11:11 ..
+-rw------- $user/$group - 1911-11-11 11:11 .dotfile
+-rwx------ $user/$group - 1911-11-11 11:11 executable
+prw------- $user/$group - 1911-11-11 11:11 fifo
+-rw------- $user/$group - 1911-11-11 11:11 file
+srwx------ $user/$group - 1911-11-11 11:11 socket
+lrwxrwxrwx $user/$group - $symlink_date symlink -> file"
+
+WVPASSEQ "$(bup ls -lA src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"-rw------- $user/$group - 1911-11-11 11:11 .dotfile
+-rwx------ $user/$group - 1911-11-11 11:11 executable
+prw------- $user/$group - 1911-11-11 11:11 fifo
+-rw------- $user/$group - 1911-11-11 11:11 file
+srwx------ $user/$group - 1911-11-11 11:11 socket
+lrwxrwxrwx $user/$group - $symlink_date symlink -> file"
+
+WVPASSEQ "$(bup ls -lF src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"-rwx------ $user/$group - 1911-11-11 11:11 executable*
+prw------- $user/$group - 1911-11-11 11:11 fifo|
+-rw------- $user/$group - 1911-11-11 11:11 file
+srwx------ $user/$group - 1911-11-11 11:11 socket=
+lrwxrwxrwx $user/$group - $symlink_date symlink@ -> file"
+
+WVPASSEQ "$(bup ls -l --file-type src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"-rwx------ $user/$group - 1911-11-11 11:11 executable
+prw------- $user/$group - 1911-11-11 11:11 fifo|
+-rw------- $user/$group - 1911-11-11 11:11 file
+srwx------ $user/$group - 1911-11-11 11:11 socket=
+lrwxrwxrwx $user/$group - $symlink_date symlink@ -> file"
+
+WVPASSEQ "$(bup ls -ln src/latest"$tmpdir"/src | tr -s ' ' ' ')" \
+"-rwx------ $uid/$gid - 1911-11-11 11:11 executable
+prw------- $uid/$gid - 1911-11-11 11:11 fifo
+-rw------- $uid/$gid - 1911-11-11 11:11 file
+srwx------ $uid/$gid - 1911-11-11 11:11 socket
+lrwxrwxrwx $uid/$gid - $symlink_date symlink -> file"
+
+WVSTART "ls (backup set - long)"
+WVPASSEQ "$(bup ls -l src | cut -d' ' -f 1-2)" \
+"l--------- ?/?
+l--------- ?/?"
+
+WVPASS rm -rf "$tmpdir"