]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/mbsalign.c
libblkid: fix return codes from PART_ENTRY_* probing
[thirdparty/util-linux.git] / lib / mbsalign.c
CommitLineData
104b92f8
PB
1/* Align/Truncate a string in a given screen width
2 Copyright (C) 2009-2010 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
36c7f785
PB
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
104b92f8
PB
7 (at your option) any later version.
8
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.
13
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/>. */
16
17/* Written by Pádraig Brady. */
18
19#include <config.h>
20
21#include <stdlib.h>
22#include <string.h>
23#include <stdio.h>
24#include <stdbool.h>
25#include <limits.h>
1b1f66e4 26#include <ctype.h>
104b92f8
PB
27
28#include "c.h"
29#include "mbsalign.h"
30#include "widechar.h"
31
104b92f8
PB
32#ifdef HAVE_WIDECHAR
33/* Replace non printable chars.
34 Note \t and \n etc. are non printable.
35 Return 1 if replacement made, 0 otherwise. */
36
1b1f66e4
KZ
37/*
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().
41 */
42size_t mbs_safe_width(const char *s)
43{
44 mbstate_t st;
45 const char *p = s;
46 size_t width = 0;
47
48 memset(&st, 0, sizeof(st));
49
50 while (p && *p) {
51 if (iscntrl((unsigned char) *p)) {
52 width += 4; /* *p encoded to \x?? */
53 p++;
54 }
55#ifdef HAVE_WIDECHAR
56 else {
57 wchar_t wc;
58 size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
59
60 if (len == 0)
61 break;
62
63 if (len == (size_t) -1 || len == (size_t) -2) {
64 len = 1;
65 width += (isprint((unsigned char) *p) ? 1 : 4);
66
67 } if (!iswprint(wc))
68 width += len * 4; /* hex encode whole sequence */
69 else
70 width += wcwidth(wc); /* number of cells */
71 p += len;
72 }
73#else
74 else if (!isprint((unsigned char) *p)) {
75 width += 4; /* *p encoded to \x?? */
76 p++;
77 } else {
78 width++;
79 p++;
80 }
81#endif
82 }
83
84 return width;
85}
86
87/*
88 * Returns allocated string where all control and non-printable chars are
89 * replaced with \x?? hex sequence.
90 */
91char *mbs_safe_encode(const char *s, size_t *width)
92{
93 mbstate_t st;
94 const char *p = s;
95 char *res, *r;
96 size_t sz = s ? strlen(s) : 0;
97
98
99 if (!sz)
100 return NULL;
101
102 memset(&st, 0, sizeof(st));
103
104 res = malloc((sz * 4) + 1);
105 if (!res)
106 return NULL;
107
108 r = res;
109 *width = 0;
110
111 while (p && *p) {
112 if (iscntrl((unsigned char) *p)) {
113 sprintf(r, "\\x%02x", (unsigned char) *p);
114 r += 4;
115 *width += 4;
116 p++;
117 }
118#ifdef HAVE_WIDECHAR
119 else {
120 wchar_t wc;
121 size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
122
123 if (len == 0)
124 break; /* end of string */
125
126 if (len == (size_t) -1 || len == (size_t) -2) {
127 len = 1;
128 /*
129 * Not valid multibyte sequence -- maybe it's
130 * printable char according to the current locales.
131 */
132 if (!isprint((unsigned char) *p)) {
133 sprintf(r, "\\x%02x", (unsigned char) *p);
134 r += 4;
135 *width += 4;
136 } else {
137 width++;
138 *r++ = *p;
139 }
140 } else if (!iswprint(wc)) {
141 size_t i;
142 for (i = 0; i < len; i++) {
143 sprintf(r, "\\x%02x", (unsigned char) *p);
144 r += 4;
145 *width += 4;
146 }
147 } else {
148 memcpy(r, p, len);
149 r += len;
150 *width += wcwidth(wc);
151 }
152 p += len;
153 }
154#else
155 else if (!isprint((unsigned char) *p)) {
156 sprintf(r, "\\x%02x", (unsigned char) *p);
157 p++;
158 r += 4;
159 *width += 4;
160 } else {
161 *r++ = *p++;
162 *width++;
163 }
164#endif
165 }
166
167 *r = '\0';
168
169 return res;
170}
171
104b92f8
PB
172static bool
173wc_ensure_printable (wchar_t *wchars)
174{
175 bool replaced = false;
176 wchar_t *wc = wchars;
177 while (*wc)
178 {
179 if (!iswprint ((wint_t) *wc))
180 {
181 *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
182 replaced = true;
183 }
184 wc++;
185 }
186 return replaced;
187}
188
189/* Truncate wchar string to width cells.
190 * Returns number of cells used. */
191
192static size_t
193wc_truncate (wchar_t *wc, size_t width)
194{
195 size_t cells = 0;
196 int next_cells = 0;
197
198 while (*wc)
199 {
200 next_cells = wcwidth (*wc);
201 if (next_cells == -1) /* non printable */
202 {
203 *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
204 next_cells = 1;
205 }
206 if (cells + next_cells > width)
207 break;
208 cells += next_cells;
209 wc++;
210 }
211 *wc = L'\0';
212 return cells;
213}
214
215/* FIXME: move this function to gnulib as it's missing on:
216 OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS */
217
218static int
219rpl_wcswidth (const wchar_t *s, size_t n)
220{
221 int ret = 0;
222
223 while (n-- > 0 && *s != L'\0')
224 {
225 int nwidth = wcwidth (*s++);
226 if (nwidth == -1) /* non printable */
227 return -1;
228 if (ret > (INT_MAX - nwidth)) /* overflow */
229 return -1;
230 ret += nwidth;
231 }
232
233 return ret;
234}
235#endif
236
5f94ca33
KZ
237/* Truncate multi-byte string to @width and returns number of
238 * bytes of the new string @str, and in @width returns number
239 * of cells.
240 */
241size_t
242mbs_truncate(char *str, size_t *width)
243{
2897f29a 244 ssize_t bytes = strlen(str);
5f94ca33 245#ifdef HAVE_WIDECHAR
2897f29a 246 ssize_t sz = mbstowcs(NULL, str, 0);
5f94ca33
KZ
247 wchar_t *wcs = NULL;
248
2897f29a 249 if (sz == (ssize_t) -1)
5f94ca33
KZ
250 goto done;
251
252 wcs = malloc((sz + 1) * sizeof(wchar_t));
253 if (!wcs)
254 goto done;
255
256 if (!mbstowcs(wcs, str, sz))
257 goto done;
258 *width = wc_truncate(wcs, *width);
259 bytes = wcstombs(str, wcs, bytes);
260done:
261 free(wcs);
262#else
263 if (*width < bytes)
264 bytes = *width;
265#endif
266 if (bytes >= 0)
267 str[bytes] = '\0';
268 return bytes;
269}
270
104b92f8
PB
271/* Write N_SPACES space characters to DEST while ensuring
272 nothing is written beyond DEST_END. A terminating NUL
273 is always added to DEST.
274 A pointer to the terminating NUL is returned. */
275
276static char*
277mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
278{
279 /* FIXME: Should we pad with "figure space" (\u2007)
280 if non ascii data present? */
281 while (n_spaces-- && (dest < dest_end))
282 *dest++ = ' ';
283 *dest = '\0';
284 return dest;
285}
286
287/* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
288 characters; write the result into the DEST_SIZE-byte buffer, DEST.
289 ALIGNMENT specifies whether to left- or right-justify or to center.
290 If SRC requires more than *WIDTH columns, truncate it to fit.
291 When centering, the number of trailing spaces may be one less than the
292 number of leading spaces. The FLAGS parameter is unused at present.
293 Return the length in bytes required for the final result, not counting
294 the trailing NUL. A return value of DEST_SIZE or larger means there
295 wasn't enough space. DEST will be NUL terminated in any case.
296 Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
297 or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
298 Update *WIDTH to indicate how many columns were used before padding. */
299
300size_t
301mbsalign (const char *src, char *dest, size_t dest_size,
302 size_t *width, mbs_align_t align, int flags)
303{
304 size_t ret = -1;
305 size_t src_size = strlen (src) + 1;
306 char *newstr = NULL;
307 wchar_t *str_wc = NULL;
308 const char *str_to_print = src;
309 size_t n_cols = src_size - 1;
310 size_t n_used_bytes = n_cols; /* Not including NUL */
3acc206d 311 size_t n_spaces = 0, space_left;
104b92f8
PB
312 bool conversion = false;
313 bool wc_enabled = false;
314
315#ifdef HAVE_WIDECHAR
316 /* In multi-byte locales convert to wide characters
317 to allow easy truncation. Also determine number
318 of screen columns used. */
319 if (MB_CUR_MAX > 1)
320 {
321 size_t src_chars = mbstowcs (NULL, src, 0);
322 if (src_chars == (size_t) -1)
323 {
324 if (flags & MBA_UNIBYTE_FALLBACK)
325 goto mbsalign_unibyte;
326 else
327 goto mbsalign_cleanup;
328 }
329 src_chars += 1; /* make space for NUL */
330 str_wc = malloc (src_chars * sizeof (wchar_t));
331 if (str_wc == NULL)
332 {
333 if (flags & MBA_UNIBYTE_FALLBACK)
334 goto mbsalign_unibyte;
335 else
336 goto mbsalign_cleanup;
337 }
338 if (mbstowcs (str_wc, src, src_chars) != 0)
339 {
340 str_wc[src_chars - 1] = L'\0';
341 wc_enabled = true;
342 conversion = wc_ensure_printable (str_wc);
343 n_cols = rpl_wcswidth (str_wc, src_chars);
344 }
345 }
346
347 /* If we transformed or need to truncate the source string
348 then create a modified copy of it. */
349 if (wc_enabled && (conversion || (n_cols > *width)))
350 {
351 if (conversion)
352 {
353 /* May have increased the size by converting
354 \t to \uFFFD for example. */
355 src_size = wcstombs(NULL, str_wc, 0) + 1;
356 }
357 newstr = malloc (src_size);
358 if (newstr == NULL)
359 {
360 if (flags & MBA_UNIBYTE_FALLBACK)
361 goto mbsalign_unibyte;
362 else
363 goto mbsalign_cleanup;
364 }
365 str_to_print = newstr;
366 n_cols = wc_truncate (str_wc, *width);
367 n_used_bytes = wcstombs (newstr, str_wc, src_size);
368 }
369#endif
370
371mbsalign_unibyte:
372
373 if (n_cols > *width) /* Unibyte truncation required. */
374 {
375 n_cols = *width;
376 n_used_bytes = n_cols;
377 }
378
379 if (*width > n_cols) /* Padding required. */
380 n_spaces = *width - n_cols;
381
382 /* indicate to caller how many cells needed (not including padding). */
383 *width = n_cols;
384
385 /* indicate to caller how many bytes needed (not including NUL). */
386 ret = n_used_bytes + (n_spaces * 1);
387
388 /* Write as much NUL terminated output to DEST as possible. */
389 if (dest_size != 0)
390 {
391 char *dest_end = dest + dest_size - 1;
f7ff0414
SK
392 size_t start_spaces;
393 size_t end_spaces;
104b92f8
PB
394
395 switch (align)
396 {
397 case MBS_ALIGN_CENTER:
398 start_spaces = n_spaces / 2 + n_spaces % 2;
399 end_spaces = n_spaces / 2;
400 break;
401 case MBS_ALIGN_LEFT:
402 start_spaces = 0;
403 end_spaces = n_spaces;
404 break;
405 case MBS_ALIGN_RIGHT:
406 start_spaces = n_spaces;
407 end_spaces = 0;
408 break;
ce7b11a9
SK
409 default:
410 abort();
104b92f8
PB
411 }
412
413 dest = mbs_align_pad (dest, dest_end, start_spaces);
3acc206d 414 space_left = dest_end - dest;
104b92f8
PB
415 dest = mempcpy (dest, str_to_print, min (n_used_bytes, space_left));
416 mbs_align_pad (dest, dest_end, end_spaces);
417 }
418
419mbsalign_cleanup:
420
421 free (str_wc);
422 free (newstr);
423
424 return ret;
425}