1 /* Align/Truncate a string in a given screen width
2 Copyright (C) 2009 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 General Public License as published by
6 the Free Software Foundation, either version 3 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. */
31 # define MIN(a, b) ((a) < (b) ? (a) : (b))
34 /* Replace non printable chars.
35 Return 1 if replacement made, 0 otherwise. */
38 wc_ensure_printable (wchar_t *wchars
)
40 bool replaced
= false;
44 if (!iswprint ((wint_t) *wc
))
46 *wc
= 0xFFFD; /* L'\uFFFD' (replacement char) */
54 /* Truncate wchar string to width cells.
55 * Returns number of cells used. */
58 wc_truncate (wchar_t *wc
, size_t width
)
65 next_cells
= wcwidth (*wc
);
66 if (next_cells
== -1) /* non printable */
68 *wc
= 0xFFFD; /* L'\uFFFD' (replacement char) */
71 if (cells
+ next_cells
> width
)
80 /* FIXME: move this function to gnulib as it's missing on:
81 OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS */
84 rpl_wcswidth (const wchar_t *s
, size_t n
)
88 while (n
-- > 0 && *s
!= L
'\0')
90 int nwidth
= wcwidth (*s
++);
91 if (nwidth
== -1) /* non printable */
93 if (ret
> (INT_MAX
- nwidth
)) /* overflow */
101 /* Write N_SPACES space characters to DEST while ensuring
102 nothing is written beyond DEST_END. A terminating NUL
103 is always added to DEST.
104 A pointer to the terminating NUL is returned. */
107 mbs_align_pad (char *dest
, const char* dest_end
, size_t n_spaces
)
109 /* FIXME: Should we pad with "figure space" (\u2007)
110 if non ascii data present? */
111 while (n_spaces
-- && (dest
< dest_end
))
117 /* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
118 characters; write the result into the DEST_SIZE-byte buffer, DEST.
119 ALIGNMENT specifies whether to left- or right-justify or to center.
120 If SRC requires more than *WIDTH columns, truncate it to fit.
121 When centering, the number of trailing spaces may be one less than the
122 number of leading spaces. The FLAGS parameter is unused at present.
123 Return the length in bytes required for the final result, not counting
124 the trailing NUL. A return value of DEST_SIZE or larger means there
125 wasn't enough space. DEST will be NUL terminated in any case.
126 Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
128 Update *WIDTH to indicate how many columns were used before padding. */
131 mbsalign (const char *src
, char *dest
, size_t dest_size
,
132 size_t *width
, mbs_align_t align
, int flags
)
135 size_t src_size
= strlen (src
) + 1;
137 wchar_t *str_wc
= NULL
;
138 const char *str_to_print
= src
;
139 size_t n_cols
= src_size
- 1;
140 size_t n_used_bytes
= n_cols
; /* Not including NUL */
142 bool conversion
= false;
143 bool wc_enabled
= false;
145 /* In multi-byte locales convert to wide characters
146 to allow easy truncation. Also determine number
147 of screen columns used. */
150 size_t src_chars
= mbstowcs (NULL
, src
, 0);
151 if (src_chars
== (size_t) -1)
152 goto mbsalign_cleanup
;
153 src_chars
+= 1; /* make space for NUL */
154 str_wc
= malloc (src_chars
* sizeof (wchar_t));
156 goto mbsalign_cleanup
;
157 if (mbstowcs (str_wc
, src
, src_chars
) > 0)
159 str_wc
[src_chars
- 1] = L
'\0';
161 conversion
= wc_ensure_printable (str_wc
);
162 n_cols
= rpl_wcswidth (str_wc
, src_chars
);
166 /* If we transformed or need to truncate the source string
167 then create a modified copy of it. */
168 if (conversion
|| (n_cols
> *width
))
170 newstr
= malloc (src_size
);
172 goto mbsalign_cleanup
;
173 str_to_print
= newstr
;
176 n_cols
= wc_truncate (str_wc
, *width
);
177 n_used_bytes
= wcstombs (newstr
, str_wc
, src_size
);
182 n_used_bytes
= n_cols
;
183 memcpy (newstr
, src
, n_cols
);
184 newstr
[n_cols
] = '\0';
189 n_spaces
= *width
- n_cols
;
191 /* indicate to caller how many cells needed (not including padding). */
194 /* indicate to caller how many bytes needed (not including NUL). */
195 ret
= n_used_bytes
+ (n_spaces
* 1);
197 /* Write as much NUL terminated output to DEST as possible. */
200 char *dest_end
= dest
+ dest_size
- 1;
201 size_t start_spaces
= n_spaces
/ 2 + n_spaces
% 2;
202 size_t end_spaces
= n_spaces
/ 2;
206 case MBS_ALIGN_CENTER
:
207 start_spaces
= n_spaces
/ 2 + n_spaces
% 2;
208 end_spaces
= n_spaces
/ 2;
212 end_spaces
= n_spaces
;
214 case MBS_ALIGN_RIGHT
:
215 start_spaces
= n_spaces
;
220 dest
= mbs_align_pad (dest
, dest_end
, start_spaces
);
221 dest
= mempcpy(dest
, str_to_print
, MIN (n_used_bytes
, dest_end
- dest
));
222 dest
= mbs_align_pad (dest
, dest_end
, end_spaces
);