]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/mbsalign.c
1 /* Align/Truncate a string in a given screen width
2 Copyright (C) 2009-2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 2.1 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by Pádraig Brady. */
33 /* Replace non printable chars.
34 Note \t and \n etc. are non printable.
35 Return 1 if replacement made, 0 otherwise. */
38 * Counts number of cells in multibyte string. For all control and
39 * non-printable chars is the result width enlarged to store \x?? hex
40 * sequence. See mbs_safe_encode().
42 * Returns: number of cells, @sz returns number of bytes.
44 size_t mbs_safe_nwidth(const char *buf
, size_t bufsz
, size_t *sz
)
46 const char *p
= buf
, *last
= buf
;
47 size_t width
= 0, bytes
= 0;
51 memset(&st
, 0, sizeof(st
));
54 last
= p
+ (bufsz
- 1);
56 while (p
&& *p
&& p
<= last
) {
57 if (iscntrl((unsigned char) *p
)) {
58 width
+= 4, bytes
+= 4; /* *p encoded to \x?? */
64 size_t len
= mbrtowc(&wc
, p
, MB_CUR_MAX
, &st
);
69 if (len
== (size_t) -1 || len
== (size_t) -2) {
71 if (isprint((unsigned char) *p
))
72 width
+= 1, bytes
+= 1;
74 width
+= 4, bytes
+= 4;
76 } else if (!iswprint(wc
)) {
77 width
+= len
* 4; /* hex encode whole sequence */
80 width
+= wcwidth(wc
); /* number of cells */
81 bytes
+= len
; /* number of bytes */
86 else if (!isprint((unsigned char) *p
)) {
87 width
+= 4, bytes
+= 4; /* *p encoded to \x?? */
101 size_t mbs_safe_width(const char *s
)
105 return mbs_safe_nwidth(s
, strlen(s
), NULL
);
109 * Copy @s to @buf and replace control and non-printable chars with
110 * \x?? hex sequence. The @width returns number of cells. The @safechars
113 * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
116 char *mbs_safe_encode_to_buffer(const char *s
, size_t *width
, char *buf
, const char *safechars
)
120 size_t sz
= s
? strlen(s
) : 0;
124 memset(&st
, 0, sizeof(st
));
133 if (safechars
&& strchr(safechars
, *p
)) {
138 if (iscntrl((unsigned char) *p
)) {
139 sprintf(r
, "\\x%02x", (unsigned char) *p
);
147 size_t len
= mbrtowc(&wc
, p
, MB_CUR_MAX
, &st
);
150 break; /* end of string */
152 if (len
== (size_t) -1 || len
== (size_t) -2) {
155 * Not valid multibyte sequence -- maybe it's
156 * printable char according to the current locales.
158 if (!isprint((unsigned char) *p
)) {
159 sprintf(r
, "\\x%02x", (unsigned char) *p
);
166 } else if (!iswprint(wc
)) {
168 for (i
= 0; i
< len
; i
++) {
169 sprintf(r
, "\\x%02x", (unsigned char) *p
);
176 *width
+= wcwidth(wc
);
181 else if (!isprint((unsigned char) *p
)) {
182 sprintf(r
, "\\x%02x", (unsigned char) *p
);
197 size_t mbs_safe_encode_size(size_t bytes
)
199 return (bytes
* 4) + 1;
203 * Returns allocated string where all control and non-printable chars are
204 * replaced with \x?? hex sequence.
206 char *mbs_safe_encode(const char *s
, size_t *width
)
208 size_t sz
= s
? strlen(s
) : 0;
209 char *buf
, *ret
= NULL
;
213 buf
= malloc(mbs_safe_encode_size(sz
));
215 ret
= mbs_safe_encode_to_buffer(s
, width
, buf
, NULL
);
224 wc_ensure_printable (wchar_t *wchars
)
226 bool replaced
= false;
227 wchar_t *wc
= wchars
;
230 if (!iswprint ((wint_t) *wc
))
232 *wc
= 0xFFFD; /* L'\uFFFD' (replacement char) */
240 /* Truncate wchar string to width cells.
241 * Returns number of cells used. */
244 wc_truncate (wchar_t *wc
, size_t width
)
251 next_cells
= wcwidth (*wc
);
252 if (next_cells
== -1) /* non printable */
254 *wc
= 0xFFFD; /* L'\uFFFD' (replacement char) */
257 if (cells
+ next_cells
> width
)
267 /* FIXME: move this function to gnulib as it's missing on:
268 OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS */
271 rpl_wcswidth (const wchar_t *s
, size_t n
)
275 while (n
-- > 0 && *s
!= L
'\0')
277 int nwidth
= wcwidth (*s
++);
278 if (nwidth
== -1) /* non printable */
280 if (ret
> (INT_MAX
- nwidth
)) /* overflow */
287 #endif /* HAVE_WIDECHAR */
289 /* Truncate multi-byte string to @width and returns number of
290 * bytes of the new string @str, and in @width returns number
294 mbs_truncate(char *str
, size_t *width
)
296 ssize_t bytes
= strlen(str
);
298 ssize_t sz
= mbstowcs(NULL
, str
, 0);
301 if (sz
== (ssize_t
) -1)
304 wcs
= calloc(1, (sz
+ 1) * sizeof(wchar_t));
308 if (!mbstowcs(wcs
, str
, sz
))
310 *width
= wc_truncate(wcs
, *width
);
311 bytes
= wcstombs(str
, wcs
, bytes
);
315 if (bytes
>= 0 && *width
< (size_t) bytes
)
323 /* Write N_SPACES space characters to DEST while ensuring
324 nothing is written beyond DEST_END. A terminating NUL
325 is always added to DEST.
326 A pointer to the terminating NUL is returned. */
329 mbs_align_pad (char *dest
, const char* dest_end
, size_t n_spaces
, int padchar
)
331 /* FIXME: Should we pad with "figure space" (\u2007)
332 if non ascii data present? */
333 for (/* nothing */; n_spaces
&& (dest
< dest_end
); n_spaces
--)
340 mbsalign (const char *src
, char *dest
, size_t dest_size
,
341 size_t *width
, mbs_align_t align
, int flags
)
343 return mbsalign_with_padding(src
, dest
, dest_size
, width
, align
, flags
, ' ');
346 /* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
347 characters; write the result into the DEST_SIZE-byte buffer, DEST.
348 ALIGNMENT specifies whether to left- or right-justify or to center.
349 If SRC requires more than *WIDTH columns, truncate it to fit.
350 When centering, the number of trailing spaces may be one less than the
351 number of leading spaces. The FLAGS parameter is unused at present.
352 Return the length in bytes required for the final result, not counting
353 the trailing NUL. A return value of DEST_SIZE or larger means there
354 wasn't enough space. DEST will be NUL terminated in any case.
355 Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
356 or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
357 Update *WIDTH to indicate how many columns were used before padding. */
360 mbsalign_with_padding (const char *src
, char *dest
, size_t dest_size
,
361 size_t *width
, mbs_align_t align
,
365 int flags
__attribute__((__unused__
)),
370 size_t src_size
= strlen (src
) + 1;
372 wchar_t *str_wc
= NULL
;
373 const char *str_to_print
= src
;
374 size_t n_cols
= src_size
- 1;
375 size_t n_used_bytes
= n_cols
; /* Not including NUL */
376 size_t n_spaces
= 0, space_left
;
379 bool conversion
= false;
380 bool wc_enabled
= false;
382 /* In multi-byte locales convert to wide characters
383 to allow easy truncation. Also determine number
384 of screen columns used. */
387 size_t src_chars
= mbstowcs (NULL
, src
, 0);
388 if (src_chars
== (size_t) -1)
390 if (flags
& MBA_UNIBYTE_FALLBACK
)
391 goto mbsalign_unibyte
;
393 goto mbsalign_cleanup
;
395 src_chars
+= 1; /* make space for NUL */
396 str_wc
= malloc (src_chars
* sizeof (wchar_t));
399 if (flags
& MBA_UNIBYTE_FALLBACK
)
400 goto mbsalign_unibyte
;
402 goto mbsalign_cleanup
;
404 if (mbstowcs (str_wc
, src
, src_chars
) != 0)
406 str_wc
[src_chars
- 1] = L
'\0';
408 conversion
= wc_ensure_printable (str_wc
);
409 n_cols
= rpl_wcswidth (str_wc
, src_chars
);
413 /* If we transformed or need to truncate the source string
414 then create a modified copy of it. */
415 if (wc_enabled
&& (conversion
|| (n_cols
> *width
)))
419 /* May have increased the size by converting
420 \t to \uFFFD for example. */
421 src_size
= wcstombs(NULL
, str_wc
, 0) + 1;
423 newstr
= malloc (src_size
);
426 if (flags
& MBA_UNIBYTE_FALLBACK
)
427 goto mbsalign_unibyte
;
429 goto mbsalign_cleanup
;
431 str_to_print
= newstr
;
432 n_cols
= wc_truncate (str_wc
, *width
);
433 n_used_bytes
= wcstombs (newstr
, str_wc
, src_size
);
439 if (n_cols
> *width
) /* Unibyte truncation required. */
442 n_used_bytes
= n_cols
;
445 if (*width
> n_cols
) /* Padding required. */
446 n_spaces
= *width
- n_cols
;
448 /* indicate to caller how many cells needed (not including padding). */
451 /* indicate to caller how many bytes needed (not including NUL). */
452 ret
= n_used_bytes
+ (n_spaces
* 1);
454 /* Write as much NUL terminated output to DEST as possible. */
457 char *dest_end
= dest
+ dest_size
- 1;
463 case MBS_ALIGN_CENTER
:
464 start_spaces
= n_spaces
/ 2 + n_spaces
% 2;
465 end_spaces
= n_spaces
/ 2;
469 end_spaces
= n_spaces
;
471 case MBS_ALIGN_RIGHT
:
472 start_spaces
= n_spaces
;
479 dest
= mbs_align_pad (dest
, dest_end
, start_spaces
, padchar
);
480 space_left
= dest_end
- dest
;
481 dest
= mempcpy (dest
, str_to_print
, min (n_used_bytes
, space_left
));
482 mbs_align_pad (dest
, dest_end
, end_spaces
, padchar
);