Printing characters beyond the basic set
#include <locale.h> #include <stdio.h> int main(void) { setlocale(LC_ALL, ""); fputs("fär ínóff\n", stdout); return 0; } The call to setlocale matters: without a suitable locale, extended characters may appear as garbage on the terminal. Even with the call, useful output depends on the system being suitably installed. section 14.3
An extended character set includes characters beyond C’s basic character set. A multibyte character represents one such character as a sequence of bytes; a multibyte string contains these sequences. TAKEAWAY: Multibyte characters contain no null bytes, and multibyte strings are null terminated. Ordinary string functions such as strcpy can therefore handle them, but byte counts are not character counts. section 14.3
void draw_sep(char const start[static 1], char const end[static 1]) { fputs(start, stdout); size_t slen = mbsrlen(start, 0); size_t elen = 90 - mbsrlen(end, 0); for (size_t i = slen; i < elen; ++i) fputs(HBAR, stdout); fputs(end, stdout); fputc('\n', stdout); } !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":4}],[{\"line\":0,\"ch\":14},{\"line\":0,\"ch\":24}],[{\"line\":1,\"ch\":16},{\"line\":1,\"ch\":26}],[{\"line\":0,\"ch\":31},{\"line\":0,\"ch\":37}],[{\"line\":1,\"ch\":31},{\"line\":1,\"ch\":37}],[{\"line\":0,\"ch\":14},{\"line\":0,\"ch\":24}],[{\"line\":1,\"ch\":16},{\"line\":1,\"ch\":26}],[{\"line\":0,\"ch\":31},{\"line\":0,\"ch\":37}],[{\"line\":1,\"ch\":31},{\"line\":1,\"ch\":37}],[{\"line\":2,\"ch\":4},{\"line\":2,\"ch\":9}],[{\"line\":5,\"ch\":41},{\"line\":5,\"ch\":46}],[{\"line\":6,\"ch\":4},{\"line\":6,\"ch\":9}],[{\"line\":2,\"ch\":17},{\"line\":2,\"ch\":23}],[{\"line\":5,\"ch\":53},{\"line\":5,\"ch\":59}],[{\"line\":6,\"ch\":15},{\"line\":6,\"ch\":21}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":22}],[{\"line\":3,\"ch\":4},{\"line\":3,\"ch\":10}],[{\"line\":4,\"ch\":4},{\"line\":4,\"ch\":10}],[{\"line\":5,\"ch\":9},{\"line\":5,\"ch\":15}],[{\"line\":3,\"ch\":4},{\"line\":3,\"ch\":10}],[{\"line\":4,\"ch\":4},{\"line\":4,\"ch\":10}],[{\"line\":5,\"ch\":9},{\"line\":5,\"ch\":15}],[{\"line\":5,\"ch\":4},{\"line\":5,\"ch\":7}],[{\"line\":3,\"ch\":4},{\"line\":3,\"ch\":10}],[{\"line\":4,\"ch\":4},{\"line\":4,\"ch\":10}],[{\"line\":5,\"ch\":9},{\"line\":5,\"ch\":15}],[{\"line\":2,\"ch\":4},{\"line\":2,\"ch\":9}],[{\"line\":5,\"ch\":41},{\"line\":5,\"ch\":46}],[{\"line\":6,\"ch\":4},{\"line\":6,\"ch\":9}],[{\"line\":2,\"ch\":17},{\"line\":2,\"ch\":23}],[{\"line\":5,\"ch\":53},{\"line\":5,\"ch\":59}],[{\"line\":6,\"ch\":15},{\"line\":6,\"ch\":21}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":22}],[{\"line\":2,\"ch\":4},{\"line\":2,\"ch\":9}],[{\"line\":5,\"ch\":41},{\"line\":5,\"ch\":46}],[{\"line\":6,\"ch\":4},{\"line\":6,\"ch\":9}],[{\"line\":2,\"ch\":17},{\"line\":2,\"ch\":23}],[{\"line\":5,\"ch\":53},{\"line\":5,\"ch\":59}],[{\"line\":6,\"ch\":15},{\"line\":6,\"ch\":21}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":22}],[{\"line\":7,\"ch\":4},{\"line\":7,\"ch\":9}],[{\"line\":2,\"ch\":17},{\"line\":2,\"ch\":23}],[{\"line\":5,\"ch\":53},{\"line\":5,\"ch\":59}],[{\"line\":6,\"ch\":15},{\"line\":6,\"ch\":21}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":22}]]"} !@%STYLE%@!
The draw_sep function prints a line with fputs and fills the space between its ends with horizontal-bar characters. It uses mbsrlen rather than strlen to calculate how many characters the strings represent. section 14.3
The helper mbsrlen is nonstandard. It measures a multibyte string as a wide-character string and reports an encoding error with a return value of -1. Its conversion state must match the character at which counting begins; the helper does not modify that state. section 14.3
30 size_t mbsrlen(char const*s, mbstate_t const*restrict state) {
31 if (!state) state = MBSTATE;
32 mbstate_t st = *state;
33 size_t mblen = mbsrtowcs(0, &s, 0, &st);
34 if (mblen == -1) errno = 0;
35 return mblen;
36 }
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":2}],[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":11}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":13}],[{\"line\":0,\"ch\":20},{\"line\":0,\"ch\":30}],[{\"line\":0,\"ch\":34},{\"line\":0,\"ch\":43}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":16}],[{\"line\":0,\"ch\":25},{\"line\":0,\"ch\":30}],[{\"line\":0,\"ch\":50},{\"line\":0,\"ch\":58}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":2}],[{\"line\":1,\"ch\":7},{\"line\":1,\"ch\":9}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":9}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":2}],[{\"line\":0,\"ch\":34},{\"line\":0,\"ch\":43}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":16}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":2}],[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":11}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":13}],[{\"line\":3,\"ch\":14},{\"line\":3,\"ch\":19}],[{\"line\":4,\"ch\":11},{\"line\":4,\"ch\":16}],[{\"line\":5,\"ch\":14},{\"line\":5,\"ch\":19}],[{\"line\":3,\"ch\":22},{\"line\":3,\"ch\":31}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":2}],[{\"line\":1,\"ch\":7},{\"line\":1,\"ch\":9}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":9}],[{\"line\":3,\"ch\":14},{\"line\":3,\"ch\":19}],[{\"line\":4,\"ch\":11},{\"line\":4,\"ch\":16}],[{\"line\":5,\"ch\":14},{\"line\":5,\"ch\":19}],[{\"line\":4,\"ch\":24},{\"line\":4,\"ch\":29}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":2}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":13}],[{\"line\":3,\"ch\":14},{\"line\":3,\"ch\":19}],[{\"line\":4,\"ch\":11},{\"line\":4,\"ch\":16}],[{\"line\":5,\"ch\":14},{\"line\":5,\"ch\":19}],[{\"line\":6,\"ch\":0},{\"line\":6,\"ch\":2}]]"}
!@%STYLE%@!
The mbsrlen implementation copies its conversion state, then calls mbsrtowcs with no destination to obtain the length. The separate mbsrdup example uses that length to allocate and convert a wide-character copy. section 14.3
TAKEAWAY: Use multibyte strings for extended-character input and output, but check conversions and do not infer their character length from strlen or a char array’s size. section 14.3 section 14.6

