summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.x-sc_program_name1
-rw-r--r--gl/lib/mbsalign.c109
-rw-r--r--gl/lib/mbsalign.h23
-rw-r--r--gl/modules/mbsalign-tests11
-rw-r--r--gl/tests/test-mbsalign.c93
5 files changed, 208 insertions, 29 deletions
diff --git a/.x-sc_program_name b/.x-sc_program_name
index eb4855f59..ace3c8ff0 100644
--- a/.x-sc_program_name
+++ b/.x-sc_program_name
@@ -1,2 +1,3 @@
gl/lib/randint.c
lib/euidaccess-stat.c
+gl/tests/test-mbsalign.c
diff --git a/gl/lib/mbsalign.c b/gl/lib/mbsalign.c
index be259568d..5b55ec2d0 100644
--- a/gl/lib/mbsalign.c
+++ b/gl/lib/mbsalign.c
@@ -32,6 +32,7 @@
#endif
/* Replace non printable chars.
+ Note \t and \n etc. are non printable.
Return 1 if replacement made, 0 otherwise. */
static bool
@@ -119,17 +120,17 @@ mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
ALIGNMENT specifies whether to left- or right-justify or to center.
If SRC requires more than *WIDTH columns, truncate it to fit.
When centering, the number of trailing spaces may be one less than the
- number of leading spaces. The FLAGS parameter is unused at present.
+ number of leading spaces.
Return the length in bytes required for the final result, not counting
the trailing NUL. A return value of DEST_SIZE or larger means there
wasn't enough space. DEST will be NUL terminated in any case.
Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
- or malloc failure).
+ or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
Update *WIDTH to indicate how many columns were used before padding. */
size_t
mbsalign (const char *src, char *dest, size_t dest_size,
- size_t *width, mbs_align_t align, int flags _UNUSED_PARAMETER_)
+ size_t *width, mbs_align_t align, int flags)
{
size_t ret = -1;
size_t src_size = strlen (src) + 1;
@@ -149,12 +150,22 @@ mbsalign (const char *src, char *dest, size_t dest_size,
{
size_t src_chars = mbstowcs (NULL, src, 0);
if (src_chars == (size_t) -1)
- goto mbsalign_cleanup;
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
src_chars += 1; /* make space for NUL */
str_wc = malloc (src_chars * sizeof (wchar_t));
if (str_wc == NULL)
- goto mbsalign_cleanup;
- if (mbstowcs (str_wc, src, src_chars) > 0)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ if (mbstowcs (str_wc, src, src_chars) != 0)
{
str_wc[src_chars - 1] = L'\0';
wc_enabled = true;
@@ -165,27 +176,30 @@ mbsalign (const char *src, char *dest, size_t dest_size,
/* If we transformed or need to truncate the source string
then create a modified copy of it. */
- if (conversion || (n_cols > *width))
+ if (wc_enabled && (conversion || (n_cols > *width)))
{
- newstr = malloc (src_size);
- if (newstr == NULL)
- goto mbsalign_cleanup;
- str_to_print = newstr;
- if (wc_enabled)
+ newstr = malloc (src_size);
+ if (newstr == NULL)
{
- n_cols = wc_truncate (str_wc, *width);
- n_used_bytes = wcstombs (newstr, str_wc, src_size);
- }
- else
- {
- n_cols = *width;
- n_used_bytes = n_cols;
- memcpy (newstr, src, n_cols);
- newstr[n_cols] = '\0';
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
}
+ str_to_print = newstr;
+ n_cols = wc_truncate (str_wc, *width);
+ n_used_bytes = wcstombs (newstr, str_wc, src_size);
+ }
+
+mbsalign_unibyte:
+
+ if (n_cols > *width) /* Unibyte truncation required. */
+ {
+ n_cols = *width;
+ n_used_bytes = n_cols;
}
- if (*width > n_cols)
+ if (*width > n_cols) /* Padding required. */
n_spaces = *width - n_cols;
/* indicate to caller how many cells needed (not including padding). */
@@ -197,16 +211,11 @@ mbsalign (const char *src, char *dest, size_t dest_size,
/* Write as much NUL terminated output to DEST as possible. */
if (dest_size != 0)
{
+ size_t start_spaces, end_spaces;
char *dest_end = dest + dest_size - 1;
- size_t start_spaces = n_spaces / 2 + n_spaces % 2;
- size_t end_spaces = n_spaces / 2;
switch (align)
{
- case MBS_ALIGN_CENTER:
- start_spaces = n_spaces / 2 + n_spaces % 2;
- end_spaces = n_spaces / 2;
- break;
case MBS_ALIGN_LEFT:
start_spaces = 0;
end_spaces = n_spaces;
@@ -215,10 +224,16 @@ mbsalign (const char *src, char *dest, size_t dest_size,
start_spaces = n_spaces;
end_spaces = 0;
break;
+ case MBS_ALIGN_CENTER:
+ default:
+ start_spaces = n_spaces / 2 + n_spaces % 2;
+ end_spaces = n_spaces / 2;
+ break;
}
dest = mbs_align_pad (dest, dest_end, start_spaces);
- dest = mempcpy(dest, str_to_print, MIN (n_used_bytes, dest_end - dest));
+ size_t space_left = dest_end - dest;
+ dest = mempcpy (dest, str_to_print, MIN (n_used_bytes, space_left));
mbs_align_pad (dest, dest_end, end_spaces);
}
@@ -229,3 +244,39 @@ mbsalign_cleanup:
return ret;
}
+
+/* A wrapper around mbsalign() to dynamically allocate the
+ minimum amount of memory to store the result.
+ Return NULL on failure. */
+
+char *
+ambsalign (const char *src, size_t *width, mbs_align_t align, int flags)
+{
+ size_t orig_width = *width;
+ size_t size = *width; /* Start with enough for unibyte mode. */
+ size_t req = size;
+ char *buf = NULL;
+
+ while (req >= size)
+ {
+ size = req + 1; /* Space for NUL. */
+ char *nbuf = realloc (buf, size);
+ if (nbuf == NULL)
+ {
+ free (buf);
+ buf = NULL;
+ break;
+ }
+ buf = nbuf;
+ *width = orig_width;
+ req = mbsalign (src, buf, size, width, align, flags);
+ if (req == (size_t) -1)
+ {
+ free (buf);
+ buf = NULL;
+ break;
+ }
+ }
+
+ return buf;
+}
diff --git a/gl/lib/mbsalign.h b/gl/lib/mbsalign.h
index a4ec69395..41bd49092 100644
--- a/gl/lib/mbsalign.h
+++ b/gl/lib/mbsalign.h
@@ -18,6 +18,29 @@
typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
+enum {
+ /* Use unibyte mode for invalid multibyte strings or
+ or when heap memory is exhausted. */
+ MBA_UNIBYTE_FALLBACK = 0x0001,
+
+#if 0 /* Other possible options. */
+ /* Skip invalid multibyte chars rather than failing */
+ MBA_IGNORE_INVALID = 0x0002,
+
+ /* Align multibyte strings using "figure space" (\u2007) */
+ MBA_USE_FIGURE_SPACE = 0x0004,
+
+ /* Don't add any padding */
+ MBA_TRUNCATE_ONLY = 0x0008,
+
+ /* Don't truncate */
+ MBA_PAD_ONLY = 0x0010,
+#endif
+};
+
size_t
mbsalign (const char *src, char *dest, size_t dest_size,
size_t *width, mbs_align_t align, int flags);
+
+char *
+ambsalign (const char *src, size_t *width, mbs_align_t align, int flags);
diff --git a/gl/modules/mbsalign-tests b/gl/modules/mbsalign-tests
new file mode 100644
index 000000000..8e0d138fe
--- /dev/null
+++ b/gl/modules/mbsalign-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-mbsalign.c
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-mbsalign
+check_PROGRAMS += test-mbsalign
diff --git a/gl/tests/test-mbsalign.c b/gl/tests/test-mbsalign.c
new file mode 100644
index 000000000..9f8935732
--- /dev/null
+++ b/gl/tests/test-mbsalign.c
@@ -0,0 +1,93 @@
+/* Test that mbsalign works as advertised.
+ Copyright (C) 2010 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 Pádraig Brady. */
+
+#include <config.h>
+
+#include "mbsalign.h"
+#include "macros.h"
+#include <stdlib.h>
+#include <locale.h>
+
+int
+main (void)
+{
+ char dest[4 * 16 + 1];
+ size_t width, n;
+
+ /* Test unibyte truncation. */
+ width = 4;
+ n = mbsalign ("t\tés", dest, sizeof dest, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (n == 4);
+
+ /* Test center alignment. */
+ width = 4;
+ n = mbsalign ("es", dest, sizeof dest, &width, MBS_ALIGN_CENTER, 0);
+ ASSERT (*dest == ' ' && *(dest + n - 1) == ' ');
+
+ if (setlocale (LC_ALL, "en_US.UTF8"))
+ {
+ /* Check invalid input is flagged. */
+ width = 4;
+ n = mbsalign ("t\xe1\xe2s", dest, sizeof dest, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (n == (size_t) -1);
+
+ /* Check invalid input is treated as unibyte */
+ width = 4;
+ n = mbsalign ("t\xe1\xe2s", dest, sizeof dest, &width,
+ MBS_ALIGN_LEFT, MBA_UNIBYTE_FALLBACK);
+ ASSERT (n == 4);
+
+ /* Test multibyte center alignment. */
+ width = 4;
+ n = mbsalign ("és", dest, sizeof dest, &width, MBS_ALIGN_CENTER, 0);
+ ASSERT (*dest == ' ' && *(dest + n - 1) == ' ');
+
+ /* Test multibyte left alignment. */
+ width = 4;
+ n = mbsalign ("és", dest, sizeof dest, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (*(dest + n - 1) == ' ' && *(dest + n - 2) == ' ');
+
+ /* Test multibyte right alignment. */
+ width = 4;
+ n = mbsalign ("és", dest, sizeof dest, &width, MBS_ALIGN_RIGHT, 0);
+ ASSERT (*(dest) == ' ' && *(dest + 1) == ' ');
+
+ /* multibyte multicell truncation. */
+ width = 4; /* cells */
+ n = mbsalign ("日月火水", dest, sizeof dest, &width,
+ MBS_ALIGN_LEFT, 0);
+ ASSERT (n == 6); /* 2 characters */
+
+ /* multibyte unicell truncation. */
+ width = 3; /* cells */
+ n = mbsalign ("¹²³⁴", dest, sizeof dest, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (n == 6); /* 3 characters */
+
+ /* Check independence from dest buffer. */
+ width = 4; /* cells */
+ n = mbsalign ("¹²³⁴", dest, 0, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (n == 9); /* 4 characters */
+
+ /* Check that width is updated with cells required before padding. */
+ width = 4; /* cells */
+ n = mbsalign ("¹²³", dest, 0, &width, MBS_ALIGN_LEFT, 0);
+ ASSERT (width == 3);
+ }
+
+ return 0;
+}