> Please take a look how it's done in module 'mbsstr-tests': > - test-mbsstr1.c is a test that doesn't need a particular locale. > - test-mbsstr2.c is a test that requires a UTF-8 locale. We use the > French one for simplicity. (If a system does not have fr_FR.UTF-8 > installed, it would be unlikely that it has ru_RU.UTF-8 installed.) > - test-mbsstr2.sh is a wrapper script that uses the LOCALE_FR_UTF8 > value, determined by m4/locale-fr.m4, and invokes test-mbsstr2.
I have created separate files but haven't hooked it up in the system yet. -- Regards Vladimir 'φ-coder/phcoder' Serbinenko
diff --git a/lib/argp-fmtstream.c b/lib/argp-fmtstream.c
index 70e3eb8..2db995c 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 a126acb..2bfd6a3 100644
--- a/lib/argp-help.c
+++ b/lib/argp-help.c
@@ -49,6 +49,7 @@
#include "argp.h"
#include "argp-fmtstream.h"
#include "argp-namefrob.h"
+#include "mbswidth.h"
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
@@ -1451,7 +1452,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 d81b5c8..aa6be62 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 690a5a7..1a5fe44 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 8d49681..7dce95a 100644
--- a/modules/argp
+++ b/modules/argp
@@ -39,6 +39,7 @@ stdalign
strerror
memchr
memmove
+mbswidth
configure.ac:
gl_ARGP
diff --git a/tests/test-argp-2.sh b/tests/test-argp-2.sh
index 49eff9c..935cce5 100755
--- a/tests/test-argp-2.sh
+++ b/tests/test-argp-2.sh
@@ -33,10 +33,11 @@ func_compare() {
####
# Test --usage output
cat > $TMP <<EOT
-Usage: test-argp [-tvCSOlp?V] [-f FILE] [-r FILE] [-o[ARG]] [--test]
- [--file=FILE] [--input=FILE] [--read=FILE] [--verbose] [--cantiga]
- [--sonet] [--option] [--optional[=ARG]] [--limerick] [--poem]
- [--help] [--usage] [--version] ARGS...
+Usage: test-argp [-tvCSOlp?V] [-c ÐиÑиллиÑа] [-f FILE] [-r FILE] [-o[ARG]]
+ [--ÑÑÑÑкий=ÐиÑиллиÑа] [--test] [--file=FILE] [--input=FILE]
+ [--read=FILE] [--verbose] [--cantiga] [--sonet] [--option]
+ [--optional[=ARG]] [--limerick] [--poem] [--help] [--usage]
+ [--version] ARGS...
EOT
./test-argp$EXEEXT --usage | func_compare || ERR=1
@@ -45,10 +46,10 @@ EOT
# Test working usage-indent format
cat > $TMP <<EOT
-Usage: test-argp [-tvCSOlp?V] [-f FILE] [-r FILE] [-o[ARG]] [--test]
-[--file=FILE] [--input=FILE] [--read=FILE] [--verbose] [--cantiga] [--sonet]
-[--option] [--optional[=ARG]] [--limerick] [--poem] [--help] [--usage]
-[--version] ARGS...
+Usage: test-argp [-tvCSOlp?V] [-c ÐиÑиллиÑа] [-f FILE] [-r FILE] [-o[ARG]]
+[--ÑÑÑÑкий=ÐиÑиллиÑа] [--test] [--file=FILE] [--input=FILE] [--read=FILE]
+[--verbose] [--cantiga] [--sonet] [--option] [--optional[=ARG]] [--limerick]
+[--poem] [--help] [--usage] [--version] ARGS...
EOT
ARGP_HELP_FMT='usage-indent=0' ./test-argp$EXEEXT --usage | func_compare || ERR=1
@@ -60,6 +61,8 @@ Usage: test-argp [OPTION...] ARGS...
documentation string
Main options
+ -c, --ÑÑÑÑкий=ÐиÑиллиÑа ÐлиннÑй ÑекÑÑ, доÑÑаÑоÑно длиннÑй, ÑÑÐ¾Ð±Ñ Ð¿ÑиÑлоÑÑ
+ пеÑенеÑÑи его на вÑоÑÑÑ ÑÑÑокÑ
-t, --test
Option Group 1
diff --git a/tests/test-argp.c b/tests/test-argp.c
index 6ba55e9..1a3f7dc 100644
--- a/tests/test-argp.c
+++ b/tests/test-argp.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <locale.h>
#if HAVE_STRINGS_H
# include <strings.h>
#endif
@@ -213,6 +214,8 @@ struct argp_child group2_1_child = {
static struct argp_option main_options[] = {
{ NULL, 0, NULL, 0, "Main options", 0 },
{ "test", 't', NULL, 0, NULL, 1 },
+ { "ÑÑÑÑкий", 'c', "ÐиÑиллиÑа", 0, "ÐлиннÑй ÑекÑÑ, доÑÑаÑоÑно длиннÑй, "
+ "ÑÑÐ¾Ð±Ñ Ð¿ÑиÑлоÑÑ Ð¿ÐµÑенеÑÑи его на вÑоÑÑÑ ÑÑÑокÑ", 1},
{ NULL, 0, NULL, 0, NULL, 0 }
};
@@ -459,6 +462,7 @@ main (int argc, char **argv)
struct argp_child argp_children[3], group1_children[2], group2_children[2];
test_fp *fun;
+ setlocale (LC_ALL, "");
set_program_name (argv[0]);
group1_children[0] = group1_1_child;
signature.asc
Description: OpenPGP digital signature
