| diff --git a/lib/argp-fmtstream.c b/lib/argp-fmtstream.c |
| index 7aa317c..02406ff 100644 |
| --- a/lib/argp-fmtstream.c |
| +++ b/lib/argp-fmtstream.c |
| @@ -29,9 +29,11 @@ |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <ctype.h> |
| +#include <wchar.h> |
| |
| #include "argp-fmtstream.h" |
| #include "argp-namefrob.h" |
| +#include "mbswidth.h" |
| |
| #ifndef ARGP_FMTSTREAM_USE_LINEWRAP |
| |
| @@ -116,6 +118,51 @@ weak_alias (__argp_fmtstream_free, argp_fmtstream_free) |
| #endif |
| #endif |
| |
| + |
| +/* Return the pointer to the first character that doesn't fit in l columns. */ |
| +static inline const ptrdiff_t |
| +add_width (const char *ptr, const char *end, size_t l) |
| +{ |
| + mbstate_t ps; |
| + const char *ptr0 = ptr; |
| + |
| + memset (&ps, 0, sizeof (ps)); |
| + |
| + while (ptr < end) |
| + { |
| + wchar_t wc; |
| + size_t s, k; |
| + |
| + s = mbrtowc (&wc, ptr, end - ptr, &ps); |
| + if (s == (size_t) -1) |
| + break; |
| + if (s == (size_t) -2) |
| + { |
| + if (1 >= l) |
| + break; |
| + l--; |
| + ptr++; |
| + continue; |
| + } |
| + |
| + if (wc == '\e' && ptr + 3 < end |
| + && ptr[1] == '[' && (ptr[2] == '0' || ptr[2] == '1') |
| + && ptr[3] == 'm') |
| + { |
| + ptr += 4; |
| + continue; |
| + } |
| + |
| + k = wcwidth (wc); |
| + |
| + if (k >= l) |
| + break; |
| + l -= k; |
| + ptr += s; |
| + } |
| + return ptr - ptr0; |
| +} |
| + |
| /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the |
| end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ |
| void |
| @@ -168,14 +215,15 @@ __argp_fmtstream_update (argp_fmtstream_t fs) |
| |
| if (!nl) |
| { |
| + size_t display_width = mbsnwidth (buf, fs->p - buf, MBSW_STOP_AT_NUL); |
| /* The buffer ends in a partial line. */ |
| |
| - if (fs->point_col + len < fs->rmargin) |
| + if (fs->point_col + display_width < fs->rmargin) |
| { |
| /* The remaining buffer text is a partial line and fits |
| within the maximum line width. Advance point for the |
| characters to be written and stop scanning. */ |
| - fs->point_col += len; |
| + fs->point_col += display_width; |
| break; |
| } |
| else |
| @@ -183,14 +231,18 @@ __argp_fmtstream_update (argp_fmtstream_t fs) |
| the end of the buffer. */ |
| nl = fs->p; |
| } |
| - else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) |
| - { |
| - /* The buffer contains a full line that fits within the maximum |
| - line width. Reset point and scan the next line. */ |
| - fs->point_col = 0; |
| - buf = nl + 1; |
| - continue; |
| - } |
| + else |
| + { |
| + size_t display_width = mbsnwidth (buf, nl - buf, MBSW_STOP_AT_NUL); |
| + if (display_width < (ssize_t) fs->rmargin) |
| + { |
| + /* The buffer contains a full line that fits within the maximum |
| + line width. Reset point and scan the next line. */ |
| + fs->point_col = 0; |
| + buf = nl + 1; |
| + continue; |
| + } |
| + } |
| |
| /* This line is too long. */ |
| r = fs->rmargin - 1; |
| @@ -226,7 +278,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) |
| char *p, *nextline; |
| int i; |
| |
| - p = buf + (r + 1 - fs->point_col); |
| + p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col)); |
| while (p >= buf && !isblank ((unsigned char) *p)) |
| --p; |
| nextline = p + 1; /* This will begin the next line. */ |
| @@ -244,7 +296,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) |
| { |
| /* A single word that is greater than the maximum line width. |
| Oh well. Put it on an overlong line by itself. */ |
| - p = buf + (r + 1 - fs->point_col); |
| + p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col)); |
| /* Find the end of the long word. */ |
| if (p < nl) |
| do |
| @@ -278,7 +330,8 @@ __argp_fmtstream_update (argp_fmtstream_t fs) |
| && fs->p > nextline) |
| { |
| /* The margin needs more blanks than we removed. */ |
| - if (fs->end - fs->p > fs->wmargin + 1) |
| + if (mbsnwidth (fs->p, fs->end - fs->p, MBSW_STOP_AT_NUL) |
| + > fs->wmargin + 1) |
| /* Make some space for them. */ |
| { |
| size_t mv = fs->p - nextline; |
| diff --git a/lib/argp-help.c b/lib/argp-help.c |
| index 354f1e2..2914f47 100644 |
| --- a/lib/argp-help.c |
| +++ b/lib/argp-help.c |
| @@ -50,6 +50,7 @@ |
| #include "argp.h" |
| #include "argp-fmtstream.h" |
| #include "argp-namefrob.h" |
| +#include "mbswidth.h" |
| |
| #ifndef SIZE_MAX |
| # define SIZE_MAX ((size_t) -1) |
| @@ -1452,7 +1453,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state, |
| |
| /* Manually do line wrapping so that it (probably) won't get wrapped at |
| any embedded spaces. */ |
| - space (stream, 1 + nl - cp); |
| + space (stream, 1 + mbsnwidth (cp, nl - cp, MBSW_STOP_AT_NUL)); |
| |
| __argp_fmtstream_write (stream, cp, nl - cp); |
| } |
| diff --git a/lib/mbswidth.c b/lib/mbswidth.c |
| index 7c2dfce..baa4f27 100644 |
| --- a/lib/mbswidth.c |
| +++ b/lib/mbswidth.c |
| @@ -90,6 +90,9 @@ mbsnwidth (const char *string, size_t nbytes, int flags) |
| p++; |
| width++; |
| break; |
| + case '\0': |
| + if (flags & MBSW_STOP_AT_NUL) |
| + return width; |
| default: |
| /* If we have a multibyte sequence, scan it up to its end. */ |
| { |
| @@ -168,6 +171,9 @@ mbsnwidth (const char *string, size_t nbytes, int flags) |
| { |
| unsigned char c = (unsigned char) *p++; |
| |
| + if (c == 0 && (flags & MBSW_STOP_AT_NUL)) |
| + return width; |
| + |
| if (isprint (c)) |
| { |
| if (width == INT_MAX) |
| diff --git a/lib/mbswidth.h b/lib/mbswidth.h |
| index e9c0b03..d7207c5 100644 |
| --- a/lib/mbswidth.h |
| +++ b/lib/mbswidth.h |
| @@ -45,6 +45,9 @@ extern "C" { |
| control characters and 1 otherwise. */ |
| #define MBSW_REJECT_UNPRINTABLE 2 |
| |
| +/* If this bit is set \0 is treated as the end of string. |
| + Otherwise it's treated as a normal one column width character. */ |
| +#define MBSW_STOP_AT_NUL 4 |
| |
| /* Returns the number of screen columns needed for STRING. */ |
| #define mbswidth gnu_mbswidth /* avoid clash with UnixWare 7.1.1 function */ |
| diff --git a/modules/argp b/modules/argp |
| index 125046a..6f14d10 100644 |
| --- a/modules/argp |
| +++ b/modules/argp |
| @@ -40,6 +40,7 @@ stdalign |
| strerror |
| memchr |
| memmove |
| +mbswidth |
| |
| configure.ac: |
| gl_ARGP |
| diff --git a/modules/argp-tests b/modules/argp-tests |
| index 8f92a4d..0463927 100644 |
| --- a/modules/argp-tests |
| +++ b/modules/argp-tests |
| @@ -1,11 +1,13 @@ |
| Files: |
| tests/test-argp.c |
| tests/test-argp-2.sh |
| +tests/test-argp-2-utf.sh |
| |
| Depends-on: |
| progname |
| |
| Makefile.am: |
| TESTS += test-argp test-argp-2.sh |
| -check_PROGRAMS += test-argp |
| +TESTS += test-argp test-argp-2.sh test-argp-2-utf.sh |
| +check_PROGRAMS += test-argp test-argp-utf8 |
| test_argp_LDADD = $(LDADD) @LIBINTL@ |