From b5404209bbf641127e46db14cce9b58d6ef047bb Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 18 May 2016 04:26:43 +0200 Subject: [PATCH] add swidth call to determine string width on terminal on posix platforms, this is a small wrapper around wcswidth(3). --- borg/platform.py | 8 ++++---- borg/platform_base.py | 8 ++++++++ borg/platform_darwin.pyx | 6 ++++++ borg/platform_freebsd.pyx | 6 ++++++ borg/platform_linux.pyx | 6 ++++++ borg/testsuite/platform.py | 15 ++++++++++++++- 6 files changed, 44 insertions(+), 5 deletions(-) diff --git a/borg/platform.py b/borg/platform.py index e57c08707..d05b60fb9 100644 --- a/borg/platform.py +++ b/borg/platform.py @@ -1,10 +1,10 @@ import sys -from .platform_base import acl_get, acl_set, SyncFile, sync_dir, set_flags, get_flags, API_VERSION +from .platform_base import acl_get, acl_set, SyncFile, sync_dir, set_flags, get_flags, swidth, API_VERSION if sys.platform.startswith('linux'): # pragma: linux only - from .platform_linux import acl_get, acl_set, SyncFile, set_flags, get_flags, API_VERSION + from .platform_linux import acl_get, acl_set, SyncFile, set_flags, get_flags, swidth, API_VERSION elif sys.platform.startswith('freebsd'): # pragma: freebsd only - from .platform_freebsd import acl_get, acl_set, API_VERSION + from .platform_freebsd import acl_get, acl_set, swidth, API_VERSION elif sys.platform == 'darwin': # pragma: darwin only - from .platform_darwin import acl_get, acl_set, API_VERSION + from .platform_darwin import acl_get, acl_set, swidth, API_VERSION diff --git a/borg/platform_base.py b/borg/platform_base.py index 675b5af9e..da62260b6 100644 --- a/borg/platform_base.py +++ b/borg/platform_base.py @@ -90,3 +90,11 @@ class SyncFile: self.sync() self.fd.close() sync_dir(os.path.dirname(self.fd.name)) + + +def swidth(s): + """terminal output width of string + + For western scripts, this is just len(s), but for cjk glyphs, 2 cells are used. + """ + return len(s) diff --git a/borg/platform_darwin.pyx b/borg/platform_darwin.pyx index 4dc25b83a..fde6ca0f5 100644 --- a/borg/platform_darwin.pyx +++ b/borg/platform_darwin.pyx @@ -3,6 +3,12 @@ from .helpers import user2uid, group2gid, safe_decode, safe_encode API_VERSION = 3 +cdef extern from "wchar.h": + cdef int wcswidth(const Py_UNICODE *str, size_t n) + +def swidth(s): + return wcswidth(s, len(s)) + cdef extern from "sys/acl.h": ctypedef struct _acl_t: pass diff --git a/borg/platform_freebsd.pyx b/borg/platform_freebsd.pyx index ae69af68a..0d02cd06c 100644 --- a/borg/platform_freebsd.pyx +++ b/borg/platform_freebsd.pyx @@ -7,6 +7,12 @@ cdef extern from "errno.h": int errno int EINVAL +cdef extern from "wchar.h": + cdef int wcswidth(const Py_UNICODE *str, size_t n) + +def swidth(s): + return wcswidth(s, len(s)) + cdef extern from "sys/types.h": int ACL_TYPE_ACCESS int ACL_TYPE_DEFAULT diff --git a/borg/platform_linux.pyx b/borg/platform_linux.pyx index 8eb2b56d9..9a509efdb 100644 --- a/borg/platform_linux.pyx +++ b/borg/platform_linux.pyx @@ -9,6 +9,12 @@ from libc cimport errno API_VERSION = 3 +cdef extern from "wchar.h": + cdef int wcswidth(const Py_UNICODE *str, size_t n) + +def swidth(s): + return wcswidth(s, len(s)) + cdef extern from "sys/types.h": int ACL_TYPE_ACCESS int ACL_TYPE_DEFAULT diff --git a/borg/testsuite/platform.py b/borg/testsuite/platform.py index feb0685c5..9eaede988 100644 --- a/borg/testsuite/platform.py +++ b/borg/testsuite/platform.py @@ -4,7 +4,7 @@ import sys import tempfile import unittest -from ..platform import acl_get, acl_set +from ..platform import acl_get, acl_set, swidth from . import BaseTestCase @@ -138,3 +138,16 @@ class PlatformDarwinTestCase(BaseTestCase): self.set_acl(file2.name, b'!#acl 1\ngroup:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000000:staff:0:allow:read\nuser:FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000:root:0:allow:read\n', numeric_owner=True) self.assert_in(b'group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000000:wheel:0:allow:read', self.get_acl(file2.name)[b'acl_extended']) self.assert_in(b'group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000000::0:allow:read', self.get_acl(file2.name, numeric_owner=True)[b'acl_extended']) + + +@unittest.skipUnless(sys.platform.startswith(('linux', 'freebsd', 'darwin')), 'POSIX only tests') +class PlatformPosixTestCase(BaseTestCase): + + def test_swidth_ascii(self): + self.assert_equal(swidth("borg"), 4) + + def test_swidth_cjk(self): + self.assert_equal(swidth("バックアップ"), 6 * 2) + + def test_swidth_mixed(self): + self.assert_equal(swidth("borgバックアップ"), 4 + 6 * 2)