diff --git a/borg/archiver.py b/borg/archiver.py index 242de2de4..097d5b137 100644 --- a/borg/archiver.py +++ b/borg/archiver.py @@ -15,7 +15,7 @@ import textwrap import traceback from . import __version__ -from .helpers import Error, location_validator, format_time, format_file_size, \ +from .helpers import Error, location_validator, format_time, format_line, safe_timestamp, format_file_size, \ parse_pattern, PathPrefixPattern, to_localtime, timestamp, \ get_cache_dir, get_keys_dir, prune_within, prune_split, \ Manifest, remove_surrogates, update_excludes, format_archive, check_extension_modules, Statistics, \ @@ -442,6 +442,13 @@ class Archiver: for item in archive.iter_items(): print(remove_surrogates(item[b'path'])) else: + longformat="{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NEWLINE}" + userformat=longformat + if args.listformat: + userformat=args.listformat + + archive_name=archive.name + for item in archive.iter_items(): mode = stat.filemode(item[b'mode']) type = mode[0] @@ -451,12 +458,12 @@ class Archiver: size = sum(size for _, size, _ in item[b'chunks']) except KeyError: pass - try: - mtime = datetime.fromtimestamp(bigint_to_int(item[b'mtime']) / 1e9) - except OverflowError: - # likely a broken mtime and datetime did not want to go beyond year 9999 - mtime = datetime(9999, 12, 31, 23, 59, 59) + atime=safe_timestamp(item[b'atime']) + ctime=safe_timestamp(item[b'ctime']) + mtime=safe_timestamp(item[b'mtime']) + if b'source' in item: + source = item[b'source'] if type == 'l': extra = ' -> %s' % item[b'source'] else: @@ -464,10 +471,40 @@ class Archiver: extra = ' link to %s' % item[b'source'] else: extra = '' - print('%s %-6s %-6s %8d %s %s%s' % ( - mode, item[b'user'] or item[b'uid'], - item[b'group'] or item[b'gid'], size, format_time(mtime), - remove_surrogates(item[b'path']), extra)) + source = '' + + formatdata={ + 'mode': mode, + 'bmode': item[b'mode'], + 'type': type, + 'source': source, + 'linktarget': source, + 'user': item[b'user'] or item[b'uid'], + 'uid': item[b'uid'], + 'group': item[b'group'] or item[b'gid'], + 'gid': item[b'gid'], + 'size': size, + 'isomtime': format_time(mtime), + 'mtime': mtime, + 'isoctime': format_time(ctime), + 'ctime': ctime, + 'isoatime': format_time(atime), + 'atime': atime, + 'path': remove_surrogates(item[b'path']), + 'extra': extra, + 'archivename': archive_name, + 'SPACE': " ", + 'TAB': "\t", + 'LF': "\n", + 'CR': "\r", + 'NEWLINE': os.linesep, + 'formatkeys': () + } + formatdata["formatkeys"]=list(formatdata.keys()) + + + print(format_line(userformat, formatdata), end='') + else: for archive_info in manifest.list_archive_infos(sort_by='ts'): if args.prefix and not archive_info.name.startswith(args.prefix): @@ -1098,6 +1135,8 @@ class Archiver: subparser.add_argument('--short', dest='short', action='store_true', default=False, help='only print file/directory names, nothing else') + subparser.add_argument('--list-format', dest='listformat', type=str, + help='Format archive listing line') subparser.add_argument('-P', '--prefix', dest='prefix', type=str, help='only consider archive names starting with this prefix') subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='', diff --git a/borg/helpers.py b/borg/helpers.py index 4f230e6d1..a7b5be260 100644 --- a/borg/helpers.py +++ b/borg/helpers.py @@ -518,6 +518,28 @@ def dir_is_tagged(path, exclude_caches, exclude_if_present): tag_paths.append(tag_path) return tag_paths +def format_line(formatstr, data): + """Filter out unwanted properties of str.format(), because "formatstr" + is user provided. + """ + try: + return(formatstr.format(**data)) + except KeyError as e: + print('Error in lineformat: "{}" - reason "{}"'.format(formatstr, str(e))) + except ValueError as e: + print('Error in lineformat: "{}" - reason "{}"'.format(formatstr, str(e))) + except: + print('Line format error') + raise + return '' + +def safe_timestamp(timedata): + try: + return datetime.fromtimestamp(bigint_to_int(timedata) / 1e9) + except OverflowError: + # likely a broken file time and datetime did not want to go beyond year 9999 + return datetime(9999, 12, 31, 23, 59, 59) + def format_time(t): """use ISO-8601 date and time format diff --git a/docs/usage.rst b/docs/usage.rst index 519834d79..128d92680 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -334,7 +334,23 @@ Examples -rw-r--r-- root root 1383 May 22 22:25 etc/ImageMagick-6/colors.xml ... + $ borg list /mnt/backup::archiveA --list-format="{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NEWLINE}" + drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 . + drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 code + drwxrwxr-x user user 0 Sun, 2015-02-01 11:00:00 code/myproject + -rw-rw-r-- user user 1416192 Sun, 2015-02-01 11:00:00 code/myproject/file.ext + ... + # see what is change between archives, based on file modification time, size and file path + $ borg list /mnt/backup::archiveA --list-format="{mtime:%s}{TAB}{size}{TAB}{path}{LF}" |sort -n > /tmp/list.archiveA + $ borg list /mnt/backup::archiveB --list-format="{mtime:%s}{TAB}{size}{TAB}{path}{LF}" |sort -n > /tmp/list.archiveB + $ diff -y /tmp/list.archiveA /tmp/list.archiveB + 1422781200 0 . 1422781200 0 . + 1422781200 0 code 1422781200 0 code + 1422781200 0 code/myproject 1422781200 0 code/myproject + 1422781200 1416192 code/myproject/file.ext | 1454664653 1416192 code/myproject/file.ext + ... + .. include:: usage/prune.rst.inc Examples