--- /dev/null
+From e87ab5b991b08092a7e07af82b3ec822a8604151 Mon Sep 17 00:00:00 2001
+From: Ondrej Oprala <ooprala@redhat.com>
+Date: Wed, 5 Aug 2015 09:15:09 +0200
+Subject: [PATCH] expand,unexpand: add multibyte support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+* NEWS: Mention the changes.
+* bootstrap.conf: Add mbfile to the list of modules.
+* configure.ac: Properly initialize mbfile.
+* src/expand.c (expand): Iterate over multibyte characters properly.
+* src/unexpand.c (unexpand): Iterate over multibyte characters
+properly.
+* tests/local.mk: Add new tests.
+* tests/{expand,unexpand}/mb.sh: New tests.
+
+Co-authored-by: Pádraig Brady <pbrady@redhat.com>
+---
+ bootstrap.conf | 1 +
+ configure.ac | 2 +
+ lib/mbfile.c | 3 +
+ lib/mbfile.h | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ m4/mbfile.m4 | 14 +++
+ src/expand.c | 43 +++++----
+ src/unexpand.c | 54 +++++++----
+ tests/expand/mb.sh | 98 ++++++++++++++++++++
+ tests/local.mk | 2 +
+ tests/unexpand/mb.sh | 97 ++++++++++++++++++++
+ 10 files changed, 535 insertions(+), 34 deletions(-)
+ create mode 100644 lib/mbfile.c
+ create mode 100644 lib/mbfile.h
+ create mode 100644 m4/mbfile.m4
+ create mode 100755 tests/expand/mb.sh
+ create mode 100755 tests/unexpand/mb.sh
+
+diff --git a/bootstrap.conf b/bootstrap.conf
+index 8a0ff31..a1c78b2 100644
+--- a/bootstrap.conf
++++ b/bootstrap.conf
+@@ -152,6 +152,7 @@ gnulib_modules="
+ maintainer-makefile
+ malloc-gnu
+ manywarnings
++ mbfile
+ mbrlen
+ mbrtowc
+ mbsalign
+diff --git a/configure.ac b/configure.ac
+index 1e74b36..24c9725 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -427,6 +427,8 @@ fi
+ # I'm leaving it here for now. This whole thing needs to be modernized...
+ gl_WINSIZE_IN_PTEM
+
++gl_MBFILE
++
+ gl_HEADER_TIOCGWINSZ_IN_TERMIOS_H
+
+ if test $gl_cv_sys_tiocgwinsz_needs_termios_h = no && \
+diff --git a/lib/mbfile.c b/lib/mbfile.c
+new file mode 100644
+index 0000000..b0a468e
+--- /dev/null
++++ b/lib/mbfile.c
+@@ -0,0 +1,3 @@
++#include <config.h>
++#define MBFILE_INLINE _GL_EXTERN_INLINE
++#include "mbfile.h"
+diff --git a/lib/mbfile.h b/lib/mbfile.h
+new file mode 100644
+index 0000000..11f1b12
+--- /dev/null
++++ b/lib/mbfile.h
+@@ -0,0 +1,255 @@
++/* Multibyte character I/O: macros for multi-byte encodings.
++ Copyright (C) 2001, 2005, 2009-2015 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++/* Written by Mitsuru Chinen <mchinen@yamato.ibm.com>
++ and Bruno Haible <bruno@clisp.org>. */
++
++/* The macros in this file implement multi-byte character input from a
++ stream.
++
++ mb_file_t
++ is the type for multibyte character input stream, usable for variable
++ declarations.
++
++ mbf_char_t
++ is the type for multibyte character or EOF, usable for variable
++ declarations.
++
++ mbf_init (mbf, stream)
++ initializes the MB_FILE for reading from stream.
++
++ mbf_getc (mbc, mbf)
++ reads the next multibyte character from mbf and stores it in mbc.
++
++ mb_iseof (mbc)
++ returns true if mbc represents the EOF value.
++
++ Here are the function prototypes of the macros.
++
++ extern void mbf_init (mb_file_t mbf, FILE *stream);
++ extern void mbf_getc (mbf_char_t mbc, mb_file_t mbf);
++ extern bool mb_iseof (const mbf_char_t mbc);
++ */
++
++#ifndef _MBFILE_H
++#define _MBFILE_H 1
++
++#include <assert.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <string.h>
++
++/* Tru64 with Desktop Toolkit C has a bug: <stdio.h> must be included before
++ <wchar.h>.
++ BSD/OS 4.1 has a bug: <stdio.h> and <time.h> must be included before
++ <wchar.h>. */
++#include <stdio.h>
++#include <time.h>
++#include <wchar.h>
++
++#include "mbchar.h"
++
++#ifndef _GL_INLINE_HEADER_BEGIN
++ #error "Please include config.h first."
++#endif
++_GL_INLINE_HEADER_BEGIN
++#ifndef MBFILE_INLINE
++# define MBFILE_INLINE _GL_INLINE
++#endif
++
++struct mbfile_multi {
++ FILE *fp;
++ bool eof_seen;
++ bool have_pushback;
++ mbstate_t state;
++ unsigned int bufcount;
++ char buf[MBCHAR_BUF_SIZE];
++ struct mbchar pushback;
++};
++
++MBFILE_INLINE void
++mbfile_multi_getc (struct mbchar *mbc, struct mbfile_multi *mbf)
++{
++ size_t bytes;
++
++ /* If EOF has already been seen, don't use getc. This matters if
++ mbf->fp is connected to an interactive tty. */
++ if (mbf->eof_seen)
++ goto eof;
++
++ /* Return character pushed back, if there is one. */
++ if (mbf->have_pushback)
++ {
++ mb_copy (mbc, &mbf->pushback);
++ mbf->have_pushback = false;
++ return;
++ }
++
++ /* Before using mbrtowc, we need at least one byte. */
++ if (mbf->bufcount == 0)
++ {
++ int c = getc (mbf->fp);
++ if (c == EOF)
++ {
++ mbf->eof_seen = true;
++ goto eof;
++ }
++ mbf->buf[0] = (unsigned char) c;
++ mbf->bufcount++;
++ }
++
++ /* Handle most ASCII characters quickly, without calling mbrtowc(). */
++ if (mbf->bufcount == 1 && mbsinit (&mbf->state) && is_basic (mbf->buf[0]))
++ {
++ /* These characters are part of the basic character set. ISO C 99
++ guarantees that their wide character code is identical to their
++ char code. */
++ mbc->wc = mbc->buf[0] = mbf->buf[0];
++ mbc->wc_valid = true;
++ mbc->ptr = &mbc->buf[0];
++ mbc->bytes = 1;
++ mbf->bufcount = 0;
++ return;
++ }
++
++ /* Use mbrtowc on an increasing number of bytes. Read only as many bytes
++ from mbf->fp as needed. This is needed to give reasonable interactive
++ behaviour when mbf->fp is connected to an interactive tty. */
++ for (;;)
++ {
++ /* We don't know whether the 'mbrtowc' function updates the state when
++ it returns -2, - this is the ISO C 99 and glibc-2.2 behaviour - or
++ not - amended ANSI C, glibc-2.1 and Solaris 2.7 behaviour. We
++ don't have an autoconf test for this, yet.
++ The new behaviour would allow us to feed the bytes one by one into
++ mbrtowc. But the old behaviour forces us to feed all bytes since
++ the end of the last character into mbrtowc. Since we want to retry
++ with more bytes when mbrtowc returns -2, we must backup the state
++ before calling mbrtowc, because implementations with the new
++ behaviour will clobber it. */
++ mbstate_t backup_state = mbf->state;
++
++ bytes = mbrtowc (&mbc->wc, &mbf->buf[0], mbf->bufcount, &mbf->state);
++
++ if (bytes == (size_t) -1)
++ {
++ /* An invalid multibyte sequence was encountered. */
++ /* Return a single byte. */
++ bytes = 1;
++ mbc->wc_valid = false;
++ break;
++ }
++ else if (bytes == (size_t) -2)
++ {
++ /* An incomplete multibyte character. */
++ mbf->state = backup_state;
++ if (mbf->bufcount == MBCHAR_BUF_SIZE)
++ {
++ /* An overlong incomplete multibyte sequence was encountered. */
++ /* Return a single byte. */
++ bytes = 1;
++ mbc->wc_valid = false;
++ break;
++ }
++ else
++ {
++ /* Read one more byte and retry mbrtowc. */
++ int c = getc (mbf->fp);
++ if (c == EOF)
++ {
++ /* An incomplete multibyte character at the end. */
++ mbf->eof_seen = true;
++ bytes = mbf->bufcount;
++ mbc->wc_valid = false;
++ break;
++ }
++ mbf->buf[mbf->bufcount] = (unsigned char) c;
++ mbf->bufcount++;
++ }
++ }
++ else
++ {
++ if (bytes == 0)
++ {
++ /* A null wide character was encountered. */
++ bytes = 1;
++ assert (mbf->buf[0] == '\0');
++ assert (mbc->wc == 0);
++ }
++ mbc->wc_valid = true;
++ break;
++ }
++ }
++
++ /* Return the multibyte sequence mbf->buf[0..bytes-1]. */
++ mbc->ptr = &mbc->buf[0];
++ memcpy (&mbc->buf[0], &mbf->buf[0], bytes);
++ mbc->bytes = bytes;
++
++ mbf->bufcount -= bytes;
++ if (mbf->bufcount > 0)
++ {
++ /* It's not worth calling memmove() for so few bytes. */
++ unsigned int count = mbf->bufcount;
++ char *p = &mbf->buf[0];
++
++ do
++ {
++ *p = *(p + bytes);
++ p++;
++ }
++ while (--count > 0);
++ }
++ return;
++
++eof:
++ /* An mbchar_t with bytes == 0 is used to indicate EOF. */
++ mbc->ptr = NULL;
++ mbc->bytes = 0;
++ mbc->wc_valid = false;
++ return;
++}
++
++MBFILE_INLINE void
++mbfile_multi_ungetc (const struct mbchar *mbc, struct mbfile_multi *mbf)
++{
++ mb_copy (&mbf->pushback, mbc);
++ mbf->have_pushback = true;
++}
++
++typedef struct mbfile_multi mb_file_t;
++
++typedef mbchar_t mbf_char_t;
++
++#define mbf_init(mbf, stream) \
++ ((mbf).fp = (stream), \
++ (mbf).eof_seen = false, \
++ (mbf).have_pushback = false, \
++ memset (&(mbf).state, '\0', sizeof (mbstate_t)), \
++ (mbf).bufcount = 0)
++
++#define mbf_getc(mbc, mbf) mbfile_multi_getc (&(mbc), &(mbf))
++
++#define mbf_ungetc(mbc, mbf) mbfile_multi_ungetc (&(mbc), &(mbf))
++
++#define mb_iseof(mbc) ((mbc).bytes == 0)
++
++#ifndef _GL_INLINE_HEADER_BEGIN
++ #error "Please include config.h first."
++#endif
++_GL_INLINE_HEADER_BEGIN
++
++#endif /* _MBFILE_H */
+diff --git a/m4/mbfile.m4 b/m4/mbfile.m4
+new file mode 100644
+index 0000000..8589902
+--- /dev/null
++++ b/m4/mbfile.m4
+@@ -0,0 +1,14 @@
++# mbfile.m4 serial 7
++dnl Copyright (C) 2005, 2008-2015 Free Software Foundation, Inc.
++dnl This file is free software; the Free Software Foundation
++dnl gives unlimited permission to copy and/or distribute it,
++dnl with or without modifications, as long as this notice is preserved.
++
++dnl autoconf tests required for use of mbfile.h
++dnl From Bruno Haible.
++
++AC_DEFUN([gl_MBFILE],
++[
++ AC_REQUIRE([AC_TYPE_MBSTATE_T])
++ :
++])
+diff --git a/src/expand.c b/src/expand.c
+index 9fa2e10..380e020 100644
+--- a/src/expand.c
++++ b/src/expand.c
+@@ -37,6 +37,9 @@
+ #include <stdio.h>
+ #include <getopt.h>
+ #include <sys/types.h>
++
++#include <mbfile.h>
++
+ #include "system.h"
+ #include "die.h"
+ #include "xstrndup.h"
+@@ -100,19 +103,19 @@ expand (void)
+ {
+ /* Input stream. */
+ FILE *fp = next_file (NULL);
++ mb_file_t mbf;
++ mbf_char_t c;
+
+ if (!fp)
+ return;
+
++ mbf_init (mbf, fp);
++
+ while (true)
+ {
+- /* Input character, or EOF. */
+- int c;
+-
+ /* If true, perform translations. */
+ bool convert = true;
+
+-
+ /* The following variables have valid values only when CONVERT
+ is true: */
+
+@@ -122,17 +125,23 @@ expand (void)
+ /* Index in TAB_LIST of next tab stop to examine. */
+ size_t tab_index = 0;
+
+-
+ /* Convert a line of text. */
+
+ do
+ {
+- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
+- continue;
++ do {
++ mbf_getc (c, mbf);
++ if (mb_iseof (c))
++ {
++ mbf_init (mbf, fp = next_file (fp));
++ continue;
++ }
++ }
++ while (false);
+
+ if (convert)
+ {
+- if (c == '\t')
++ if (mb_iseq (c, '\t'))
+ {
+ /* Column the next input tab stop is on. */
+ uintmax_t next_tab_column;
+@@ -151,32 +160,34 @@ expand (void)
+ if (putchar (' ') < 0)
+ die (EXIT_FAILURE, errno, _("write error"));
+
+- c = ' ';
++ mb_setascii (&c, ' ');
+ }
+- else if (c == '\b')
++ else if (mb_iseq (c, '\b'))
+ {
+ /* Go back one column, and force recalculation of the
+ next tab stop. */
+ column -= !!column;
+ tab_index -= !!tab_index;
+ }
+- else
++ /* A leading control character could make us trip over. */
++ else if (!mb_iscntrl (c))
+ {
+- column++;
++ column += mb_width (c);
+ if (!column)
+ die (EXIT_FAILURE, 0, _("input line is too long"));
+ }
+
+- convert &= convert_entire_line || !! isblank (c);
++ convert &= convert_entire_line || mb_isblank (c);
+ }
+
+- if (c < 0)
++ if (mb_iseof (c))
+ return;
+
+- if (putchar (c) < 0)
++ mb_putc (c, stdout);
++ if (ferror (stdout))
+ die (EXIT_FAILURE, errno, _("write error"));
+ }
+- while (c != '\n');
++ while (!mb_iseq (c, '\n'));
+ }
+ }
+
+diff --git a/src/unexpand.c b/src/unexpand.c
+index 7801274..569a7ee 100644
+--- a/src/unexpand.c
++++ b/src/unexpand.c
+@@ -38,6 +38,9 @@
+ #include <stdio.h>
+ #include <getopt.h>
+ #include <sys/types.h>
++
++#include <mbfile.h>
++
+ #include "system.h"
+ #include "die.h"
+ #include "xstrndup.h"
+@@ -107,11 +110,12 @@ unexpand (void)
+ {
+ /* Input stream. */
+ FILE *fp = next_file (NULL);
++ mb_file_t mbf;
+
+ /* The array of pending blanks. In non-POSIX locales, blanks can
+ include characters other than spaces, so the blanks must be
+ stored, not merely counted. */
+- char *pending_blank;
++ mbf_char_t *pending_blank;
+
+ if (!fp)
+ return;
+@@ -119,12 +123,14 @@ unexpand (void)
+ /* The worst case is a non-blank character, then one blank, then a
+ tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so
+ allocate MAX_COLUMN_WIDTH bytes to store the blanks. */
+- pending_blank = xmalloc (max_column_width);
++ pending_blank = xmalloc (max_column_width * sizeof (mbf_char_t));
++
++ mbf_init (mbf, fp);
+
+ while (true)
+ {
+ /* Input character, or EOF. */
+- int c;
++ mbf_char_t c;
+
+ /* If true, perform translations. */
+ bool convert = true;
+@@ -158,12 +164,19 @@ unexpand (void)
+
+ do
+ {
+- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
+- continue;
++ do {
++ mbf_getc (c, mbf);
++ if (mb_iseof (c))
++ {
++ mbf_init (mbf, fp = next_file (fp));
++ continue;
++ }
++ }
++ while (false);
+
+ if (convert)
+ {
+- bool blank = !! isblank (c);
++ bool blank = mb_isblank (c);
+
+ if (blank)
+ {
+@@ -180,16 +193,16 @@ unexpand (void)
+ if (next_tab_column < column)
+ die (EXIT_FAILURE, 0, _("input line is too long"));
+
+- if (c == '\t')
++ if (mb_iseq (c, '\t'))
+ {
+ column = next_tab_column;
+
+ if (pending)
+- pending_blank[0] = '\t';
++ mb_setascii (&pending_blank[0], '\t');
+ }
+ else
+ {
+- column++;
++ column += mb_width (c);
+
+ if (! (prev_blank && column == next_tab_column))
+ {
+@@ -197,13 +210,14 @@ unexpand (void)
+ will be replaced by tabs. */
+ if (column == next_tab_column)
+ one_blank_before_tab_stop = true;
+- pending_blank[pending++] = c;
++ mb_copy (&pending_blank[pending++], &c);
+ prev_blank = true;
+ continue;
+ }
+
+ /* Replace the pending blanks by a tab or two. */
+- pending_blank[0] = c = '\t';
++ mb_setascii (&c, '\t');
++ mb_setascii (&pending_blank[0], '\t');
+ }
+
+ /* Discard pending blanks, unless it was a single
+@@ -211,7 +225,7 @@ unexpand (void)
+ pending = one_blank_before_tab_stop;
+ }
+ }
+- else if (c == '\b')
++ else if (mb_iseq (c, '\b'))
+ {
+ /* Go back one column, and force recalculation of the
+ next tab stop. */
+@@ -221,7 +235,7 @@ unexpand (void)
+ }
+ else
+ {
+- column++;
++ column += mb_width (c);
+ if (!column)
+ die (EXIT_FAILURE, 0, _("input line is too long"));
+ }
+@@ -229,8 +243,11 @@ unexpand (void)
+ if (pending)
+ {
+ if (pending > 1 && one_blank_before_tab_stop)
+- pending_blank[0] = '\t';
+- if (fwrite (pending_blank, 1, pending, stdout) != pending)
++ mb_setascii (&pending_blank[0], '\t');
++
++ for (int n = 0; n < pending; ++n)
++ mb_putc (pending_blank[n], stdout);
++ if (ferror (stdout))
+ die (EXIT_FAILURE, errno, _("write error"));
+ pending = 0;
+ one_blank_before_tab_stop = false;
+@@ -240,16 +257,17 @@ unexpand (void)
+ convert &= convert_entire_line || blank;
+ }
+
+- if (c < 0)
++ if (mb_iseof (c))
+ {
+ free (pending_blank);
+ return;
+ }
+
+- if (putchar (c) < 0)
++ mb_putc (c, stdout);
++ if (ferror (stdout))
+ die (EXIT_FAILURE, errno, _("write error"));
+ }
+- while (c != '\n');
++ while (!mb_iseq (c, '\n'));
+ }
+ }
+
+diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh
+new file mode 100755
+index 0000000..7971e18
+--- /dev/null
++++ b/tests/expand/mb.sh
+@@ -0,0 +1,98 @@
++#!/bin/sh
++
++# Copyright (C) 2012-2015 Free Software Foundation, Inc.
++
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
++print_ver_ expand
++
++export LC_ALL=en_US.UTF-8
++
++#input containing multibyte characters
++cat <<\EOF > in || framework_failure_
++1234567812345678123456781
++. . . .
++a b c d
++. . . .
++ä ö ü ß
++. . . .
++EOF
++env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_
++
++cat <<\EOF > exp || framework_failure_
++1234567812345678123456781
++. . . .
++a b c d
++. . . .
++ä ö ü ß
++. . . .
++ äöü . öüä. ä xx
++EOF
++
++expand < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++#test characters with display widths != 1
++env printf '12345678
++e\t|ascii(1)
++\u00E9\t|composed(1)
++e\u0301\t|decomposed(1)
++\u3000\t|ideo-space(2)
++\uFF0D\t|full-hypen(2)
++' > in || framework_failure_
++
++env printf '12345678
++e |ascii(1)
++\u00E9 |composed(1)
++e\u0301 |decomposed(1)
++\u3000 |ideo-space(2)
++\uFF0D |full-hypen(2)
++' > exp || framework_failure_
++
++expand < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++#shouldn't fail with "input line too long"
++#when a line starts with a control character
++env printf '\n' > in || framework_failure_
++
++expand < in > out || fail=1
++compare in out > /dev/null 2>&1 || fail=1
++
++#non-Unicode characters interspersed between Unicode ones
++env printf '12345678
++\t\xFF|
++\xFF\t|
++\t\xFFä|
++ä\xFF\t|
++\tä\xFF|
++\xFF\tä|
++äbcdef\xFF\t|
++' > in || framework_failure_
++
++env printf '12345678
++ \xFF|
++\xFF |
++ \xFFä|
++ä\xFF |
++ ä\xFF|
++\xFF ä|
++äbcdef\xFF |
++' > exp || framework_failure_
++
++expand < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++exit $fail
+diff --git a/tests/local.mk b/tests/local.mk
+index 192f776..8053397 100644
+--- a/tests/local.mk
++++ b/tests/local.mk
+@@ -544,6 +544,7 @@ all_tests = \
+ tests/du/threshold.sh \
+ tests/du/trailing-slash.sh \
+ tests/du/two-args.sh \
++ tests/expand/mb.sh \
+ tests/id/gnu-zero-uids.sh \
+ tests/id/no-context.sh \
+ tests/id/context.sh \
+@@ -684,6 +685,7 @@ all_tests = \
+ tests/touch/read-only.sh \
+ tests/touch/relative.sh \
+ tests/touch/trailing-slash.sh \
++ tests/unexpand/mb.sh \
+ $(all_root_tests)
+
+ # See tests/factor/create-test.sh.
+diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh
+new file mode 100755
+index 0000000..60d4c1a
+--- /dev/null
++++ b/tests/unexpand/mb.sh
+@@ -0,0 +1,97 @@
++#!/bin/sh
++
++# Copyright (C) 2012-2015 Free Software Foundation, Inc.
++
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
++print_ver_ unexpand
++
++export LC_ALL=en_US.UTF-8
++
++#input containing multibyte characters
++cat > in <<\EOF
++1234567812345678123456781
++. . . .
++a b c d
++. . . .
++ä ö ü ß
++. . . .
++ äöü . öüä. ä xx
++EOF
++
++cat > exp <<\EOF
++1234567812345678123456781
++. . . .
++a b c d
++. . . .
++ä ö ü ß
++. . . .
++ äöü . öüä. ä xx
++EOF
++
++unexpand -a < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++#test characters with a display width larger than 1
++
++env printf '12345678
++e |ascii(1)
++\u00E9 |composed(1)
++e\u0301 |decomposed(1)
++\u3000 |ideo-space(2)
++\uFF0D |full-hypen(2)
++' > in || framework_failure_
++
++env printf '12345678
++e\t|ascii(1)
++\u00E9\t|composed(1)
++e\u0301\t|decomposed(1)
++\u3000\t|ideo-space(2)
++\uFF0D\t|full-hypen(2)
++' > exp || framework_failure_
++
++unexpand -a < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++#test input where a blank of width > 1 is not being substituted
++in="$(LC_ALL=en_US.UTF-8 printf ' \u3000 ö ü ß')"
++exp=' ö ü ß'
++
++unexpand -a < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
++
++#non-Unicode characters interspersed between Unicode ones
++env printf '12345678
++ \xFF|
++\xFF |
++ \xFFä|
++ä\xFF |
++ ä\xFF|
++\xFF ä|
++äbcdef\xFF |
++' > in || framework_failure_
++
++env printf '12345678
++\t\xFF|
++\xFF\t|
++\t\xFFä|
++ä\xFF\t|
++\tä\xFF|
++\xFF\tä|
++äbcdef\xFF\t|
++' > exp || framework_failure_
++
++unexpand -a < in > out || fail=1
++compare exp out > /dev/null 2>&1 || fail=1
+--
+2.7.4
+