1*c39afc64Skettenis /* $OpenBSD: string.c,v 1.2 2020/07/14 16:40:04 kettenis Exp $ */
2638aa2bcSschwarze /*
3638aa2bcSschwarze * Copyright (c) 2020 Ingo Schwarze <schwarze@openbsd.org>
4638aa2bcSschwarze *
5638aa2bcSschwarze * Permission to use, copy, modify, and distribute this software for any
6638aa2bcSschwarze * purpose with or without fee is hereby granted, provided that the above
7638aa2bcSschwarze * copyright notice and this permission notice appear in all copies.
8638aa2bcSschwarze *
9638aa2bcSschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10638aa2bcSschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11638aa2bcSschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12638aa2bcSschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13638aa2bcSschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14638aa2bcSschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15638aa2bcSschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16638aa2bcSschwarze *
17638aa2bcSschwarze * Test the %c, %lc, %s, and %ls conversion specifiers with all their
18638aa2bcSschwarze * modifiers, in particular with the minus flag, width, and maxbytes.
19638aa2bcSschwarze * Also verify that other flags do nothing useful.
20638aa2bcSschwarze */
21638aa2bcSschwarze #include <err.h>
22638aa2bcSschwarze #include <errno.h>
23638aa2bcSschwarze #include <locale.h>
24638aa2bcSschwarze #include <stdio.h>
25638aa2bcSschwarze #include <stdlib.h>
26638aa2bcSschwarze #include <string.h>
27638aa2bcSschwarze #include <unistd.h>
28638aa2bcSschwarze #include <wchar.h>
29638aa2bcSschwarze
30638aa2bcSschwarze void tc(const char *, int, const char *);
31638aa2bcSschwarze void tlc(const char *, wint_t, const char *);
32638aa2bcSschwarze void tlc_expect_fail(const char *, wint_t);
33638aa2bcSschwarze void ts(const char *, const char *, const char *);
34638aa2bcSschwarze void tls(const char *, const wchar_t *, const char *);
35638aa2bcSschwarze void tls_expect_fail(const char *, const wchar_t *);
36638aa2bcSschwarze
37638aa2bcSschwarze static int badret, badlen, badout; /* Error counters. */
38638aa2bcSschwarze static int verbose; /* For debugging. */
39638aa2bcSschwarze
40638aa2bcSschwarze
41638aa2bcSschwarze /*
42638aa2bcSschwarze * Print the single-byte character c with the format fmt,
43638aa2bcSschwarze * check that the result matches what we want,
44638aa2bcSschwarze * and report and count the error on failure.
45638aa2bcSschwarze */
46638aa2bcSschwarze void
tc(const char * fmt,int c,const char * want)47638aa2bcSschwarze tc(const char *fmt, int c, const char *want)
48638aa2bcSschwarze {
49638aa2bcSschwarze char buf[32];
50638aa2bcSschwarze size_t len;
51638aa2bcSschwarze int irc, happy;
52638aa2bcSschwarze
53638aa2bcSschwarze happy = 1;
54638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, c);
55638aa2bcSschwarze len = strlen(want);
56638aa2bcSschwarze if (irc < 0) {
57638aa2bcSschwarze warn("printf(\"%s\", %d) returned %d", fmt, c, irc);
58638aa2bcSschwarze badret++;
59638aa2bcSschwarze return;
60638aa2bcSschwarze }
61638aa2bcSschwarze if ((unsigned long long)irc != len) {
62638aa2bcSschwarze warnx("printf(\"%s\", %d) returned %d (expected %zu)",
63638aa2bcSschwarze fmt, c, irc, len);
64638aa2bcSschwarze badlen++;
65638aa2bcSschwarze happy = 0;
66638aa2bcSschwarze }
67638aa2bcSschwarze if (strcmp(buf, want) != 0) {
68638aa2bcSschwarze warnx("printf(\"%s\", %d) wrote \"%s\" (expected \"%s\")",
69638aa2bcSschwarze fmt, c, buf, want);
70638aa2bcSschwarze badout++;
71638aa2bcSschwarze happy = 0;
72638aa2bcSschwarze }
73638aa2bcSschwarze if (verbose && happy)
74638aa2bcSschwarze warnx("printf(\"%s\", %d) wrote \"%s\" length %d (OK)",
75638aa2bcSschwarze fmt, c, buf, irc);
76638aa2bcSschwarze }
77638aa2bcSschwarze
78638aa2bcSschwarze /*
79638aa2bcSschwarze * Print the wide character wc with the format fmt,
80638aa2bcSschwarze * check that the result matches what we want,
81638aa2bcSschwarze * and report and count the error on failure.
82638aa2bcSschwarze */
83638aa2bcSschwarze void
tlc(const char * fmt,wint_t wc,const char * want)84638aa2bcSschwarze tlc(const char *fmt, wint_t wc, const char *want)
85638aa2bcSschwarze {
86638aa2bcSschwarze char buf[32];
87638aa2bcSschwarze const char *charset;
88638aa2bcSschwarze size_t len;
89638aa2bcSschwarze int irc, happy;
90638aa2bcSschwarze
91638aa2bcSschwarze happy = 1;
92638aa2bcSschwarze charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII";
93638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, wc);
94638aa2bcSschwarze len = strlen(want);
95638aa2bcSschwarze if (irc < 0) {
96638aa2bcSschwarze warn("%s printf(\"%s\", U+%.4X) returned %d",
97638aa2bcSschwarze charset, fmt, (unsigned int)wc, irc);
98638aa2bcSschwarze badret++;
99638aa2bcSschwarze return;
100638aa2bcSschwarze }
101638aa2bcSschwarze if ((unsigned long long)irc != len) {
102638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X) returned %d (expected %zu)",
103638aa2bcSschwarze charset, fmt, (unsigned int)wc, irc, len);
104638aa2bcSschwarze badlen++;
105638aa2bcSschwarze happy = 0;
106638aa2bcSschwarze }
107638aa2bcSschwarze if (strcmp(buf, want) != 0) {
108638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X) "
109638aa2bcSschwarze "wrote \"%s\" (expected \"%s\")",
110638aa2bcSschwarze charset, fmt, (unsigned int)wc, buf, want);
111638aa2bcSschwarze badout++;
112638aa2bcSschwarze happy = 0;
113638aa2bcSschwarze }
114638aa2bcSschwarze if (verbose && happy)
115638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X) wrote \"%s\" length %d (OK)",
116638aa2bcSschwarze charset, fmt, (unsigned int)wc, buf, irc);
117638aa2bcSschwarze }
118638aa2bcSschwarze
119638aa2bcSschwarze /*
120638aa2bcSschwarze * Try to print the invalid wide character wc with the format fmt,
121638aa2bcSschwarze * check that it fails as it should, and report and count if it doesn't.
122638aa2bcSschwarze */
123638aa2bcSschwarze void
tlc_expect_fail(const char * fmt,wint_t wc)124638aa2bcSschwarze tlc_expect_fail(const char *fmt, wint_t wc)
125638aa2bcSschwarze {
126638aa2bcSschwarze char buf[32];
127638aa2bcSschwarze const char *charset;
128638aa2bcSschwarze int irc;
129638aa2bcSschwarze
130638aa2bcSschwarze errno = 0;
131638aa2bcSschwarze charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII";
132638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, wc);
133638aa2bcSschwarze if (irc != -1) {
134638aa2bcSschwarze warn("%s printf(\"%s\", U+%.4X) returned %d",
135638aa2bcSschwarze charset, fmt, (unsigned int)wc, irc);
136638aa2bcSschwarze badret++;
137638aa2bcSschwarze } else if (errno != EILSEQ) {
138638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X) errno %d (expected %d)",
139638aa2bcSschwarze charset, fmt, (unsigned int)wc, errno, EILSEQ);
140638aa2bcSschwarze badret++;
141638aa2bcSschwarze } else if (verbose)
142638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X) returned %d errno %d (OK)",
143638aa2bcSschwarze charset, fmt, (unsigned int)wc, irc, errno);
144638aa2bcSschwarze }
145638aa2bcSschwarze
146638aa2bcSschwarze /*
147638aa2bcSschwarze * Print the string s with the format fmt,
148638aa2bcSschwarze * check that the result matches what we want,
149638aa2bcSschwarze * and report and count the error on failure.
150638aa2bcSschwarze */
151638aa2bcSschwarze void
ts(const char * fmt,const char * s,const char * want)152638aa2bcSschwarze ts(const char *fmt, const char *s, const char *want)
153638aa2bcSschwarze {
154638aa2bcSschwarze char buf[32];
155638aa2bcSschwarze size_t len;
156638aa2bcSschwarze int irc, happy;
157638aa2bcSschwarze
158638aa2bcSschwarze happy = 1;
159638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, s);
160638aa2bcSschwarze len = strlen(want);
161638aa2bcSschwarze if (irc < 0) {
162638aa2bcSschwarze warn("printf(\"%s\", \"%s\") returned %d", fmt, s, irc);
163638aa2bcSschwarze badret++;
164638aa2bcSschwarze return;
165638aa2bcSschwarze }
166638aa2bcSschwarze if ((unsigned long long)irc != len) {
167638aa2bcSschwarze warnx("printf(\"%s\", \"%s\") returned %d (expected %zu)",
168638aa2bcSschwarze fmt, s, irc, len);
169638aa2bcSschwarze badlen++;
170638aa2bcSschwarze happy = 0;
171638aa2bcSschwarze }
172638aa2bcSschwarze if (strcmp(buf, want) != 0) {
173638aa2bcSschwarze warnx("printf(\"%s\", \"%s\") wrote \"%s\" (expected \"%s\")",
174638aa2bcSschwarze fmt, s, buf, want);
175638aa2bcSschwarze badout++;
176638aa2bcSschwarze happy = 0;
177638aa2bcSschwarze }
178638aa2bcSschwarze if (verbose && happy)
179638aa2bcSschwarze warnx("printf(\"%s\", \"%s\") wrote \"%s\" length %d (OK)",
180638aa2bcSschwarze fmt, s, buf, irc);
181638aa2bcSschwarze }
182638aa2bcSschwarze
183638aa2bcSschwarze /*
184638aa2bcSschwarze * Print the wide character string ws with the format fmt,
185638aa2bcSschwarze * check that the result matches what we want,
186638aa2bcSschwarze * and report and count the error on failure.
187638aa2bcSschwarze */
188638aa2bcSschwarze void
tls(const char * fmt,const wchar_t * ws,const char * want)189638aa2bcSschwarze tls(const char *fmt, const wchar_t *ws, const char *want)
190638aa2bcSschwarze {
191638aa2bcSschwarze char buf[32];
192638aa2bcSschwarze const char *charset;
193638aa2bcSschwarze size_t len;
194638aa2bcSschwarze int irc, happy;
195638aa2bcSschwarze
196638aa2bcSschwarze happy = 1;
197638aa2bcSschwarze charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII";
198638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, ws);
199638aa2bcSschwarze len = strlen(want);
200638aa2bcSschwarze if (irc < 0) {
201638aa2bcSschwarze warn("%s printf(\"%s\", \"%ls\") returned %d",
202638aa2bcSschwarze charset, fmt, ws, irc);
203638aa2bcSschwarze badret++;
204638aa2bcSschwarze return;
205638aa2bcSschwarze }
206638aa2bcSschwarze if ((unsigned long long)irc != len) {
207638aa2bcSschwarze warnx("%s printf(\"%s\", \"%ls\") returned %d (expected %zu)",
208638aa2bcSschwarze charset, fmt, ws, irc, len);
209638aa2bcSschwarze badlen++;
210638aa2bcSschwarze happy = 0;
211638aa2bcSschwarze }
212638aa2bcSschwarze if (strcmp(buf, want) != 0) {
213638aa2bcSschwarze warnx("%s printf(\"%s\", \"%ls\") "
214638aa2bcSschwarze "wrote \"%s\" (expected \"%s\")",
215638aa2bcSschwarze charset, fmt, ws, buf, want);
216638aa2bcSschwarze badout++;
217638aa2bcSschwarze happy = 0;
218638aa2bcSschwarze }
219638aa2bcSschwarze if (verbose && happy)
220638aa2bcSschwarze warnx("%s printf(\"%s\", \"%ls\") wrote \"%s\" length %d (OK)",
221638aa2bcSschwarze charset, fmt, ws, buf, irc);
222638aa2bcSschwarze }
223638aa2bcSschwarze
224638aa2bcSschwarze /*
225638aa2bcSschwarze * Try to print the invalid wide character string ws with the format fmt,
226638aa2bcSschwarze * check that it fails as it should, and report and count if it doesn't.
227638aa2bcSschwarze */
228638aa2bcSschwarze void
tls_expect_fail(const char * fmt,const wchar_t * ws)229638aa2bcSschwarze tls_expect_fail(const char *fmt, const wchar_t *ws)
230638aa2bcSschwarze {
231638aa2bcSschwarze char buf[32];
232638aa2bcSschwarze const char *charset;
233638aa2bcSschwarze int irc;
234638aa2bcSschwarze
235638aa2bcSschwarze errno = 0;
236638aa2bcSschwarze charset = MB_CUR_MAX > 1 ? "UTF-8" : "ASCII";
237638aa2bcSschwarze irc = snprintf(buf, sizeof(buf), fmt, ws);
238638aa2bcSschwarze if (irc != -1) {
239638aa2bcSschwarze warn("%s printf(\"%s\", U+%.4X, ...) returned %d",
240638aa2bcSschwarze charset, fmt, (unsigned int)*ws, irc);
241638aa2bcSschwarze badret++;
242638aa2bcSschwarze } else if (errno != EILSEQ) {
243638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X, ...) errno %d (expected %d)",
244638aa2bcSschwarze charset, fmt, (unsigned int)*ws, errno, EILSEQ);
245638aa2bcSschwarze badret++;
246638aa2bcSschwarze } else if (verbose)
247638aa2bcSschwarze warnx("%s printf(\"%s\", U+%.4X, ...) "
248638aa2bcSschwarze "returned %d errno %d (OK)",
249638aa2bcSschwarze charset, fmt, (unsigned int)*ws, irc, errno);
250638aa2bcSschwarze }
251638aa2bcSschwarze
252638aa2bcSschwarze int
main(int argc,char * argv[])253638aa2bcSschwarze main(int argc, char *argv[])
254638aa2bcSschwarze {
255638aa2bcSschwarze const wchar_t ws[] = { 0x0421, 0x043e, 0x0444, 0x044f, 0 };
256638aa2bcSschwarze const wchar_t wsbad[] = { 0x0391, 0xdeef, 0x3c9, 0 };
257638aa2bcSschwarze int badarg, picky;
258*c39afc64Skettenis int ch;
259638aa2bcSschwarze
260638aa2bcSschwarze badarg = picky = 0;
261638aa2bcSschwarze while ((ch = getopt(argc, argv, "pv")) != -1) {
262638aa2bcSschwarze switch (ch) {
263638aa2bcSschwarze case 'p':
264638aa2bcSschwarze picky = 1;
265638aa2bcSschwarze break;
266638aa2bcSschwarze case 'v':
267638aa2bcSschwarze verbose = 1;
268638aa2bcSschwarze break;
269638aa2bcSschwarze default:
270638aa2bcSschwarze badarg = 1;
271638aa2bcSschwarze break;
272638aa2bcSschwarze }
273638aa2bcSschwarze }
274638aa2bcSschwarze argc -= optind;
275638aa2bcSschwarze argv += optind;
276638aa2bcSschwarze if (argc > 0) {
277638aa2bcSschwarze warnx("unexpected argument \"%s\"", *argv);
278638aa2bcSschwarze badarg = 1;
279638aa2bcSschwarze }
280638aa2bcSschwarze if (badarg) {
281638aa2bcSschwarze fputs("usage: string [-pv]\n", stderr);
282638aa2bcSschwarze return 1;
283638aa2bcSschwarze }
284638aa2bcSschwarze
285638aa2bcSschwarze /*
286638aa2bcSschwarze * Valid use cases of %c and %s.
287638aa2bcSschwarze */
288638aa2bcSschwarze
289638aa2bcSschwarze tc("<%c>", '=', "<=>");
290638aa2bcSschwarze tc("<%c>", '\t', "<\t>");
291638aa2bcSschwarze tc("<%c>", 0xfe, "<\xfe>");
292638aa2bcSschwarze tc("<%-c>", '=', "<=>");
293638aa2bcSschwarze tc("<%2c>", '=', "< =>");
294638aa2bcSschwarze tc("<%-2c>", '=', "<= >");
295638aa2bcSschwarze
296638aa2bcSschwarze ts("<%s>", "text", "<text>");
297638aa2bcSschwarze ts("<%-s>", "text", "<text>");
298638aa2bcSschwarze ts("<%6s>", "text", "< text>");
299638aa2bcSschwarze ts("<%-6s>", "text", "<text >");
300638aa2bcSschwarze ts("<%.2s>", "text", "<te>");
301638aa2bcSschwarze ts("<%4.2s>", "text", "< te>");
302638aa2bcSschwarze ts("<%-4.2s>", "text", "<te >");
303638aa2bcSschwarze
304638aa2bcSschwarze /*
305638aa2bcSschwarze * Undefined behaviour of %c and %s.
306638aa2bcSschwarze * Do not test by default to avoid noise.
307638aa2bcSschwarze * But provide the tests anyway to help track down
308638aa2bcSschwarze * unintended changes of behaviour when needed.
309638aa2bcSschwarze */
310638aa2bcSschwarze
311638aa2bcSschwarze if (picky) {
312638aa2bcSschwarze tc("<%#c>", '=', "<=>");
313638aa2bcSschwarze tc("<% -3c>", '=', "<= >");
314638aa2bcSschwarze tc("<%+-3c>", '=', "<= >");
315638aa2bcSschwarze tc("<%03c>", '=', "<00=>");
316638aa2bcSschwarze tc("<%-03c>", '=', "<= >");
317638aa2bcSschwarze tc("<%3.2c>", '=', "< =>");
318638aa2bcSschwarze tc("<%hc>", '=', "<=>");
319638aa2bcSschwarze
320638aa2bcSschwarze ts("<%#s>", "text", "<text>");
321638aa2bcSschwarze ts("<% -6s>", "text", "<text >");
322638aa2bcSschwarze ts("<%+-6s>", "text", "<text >");
323638aa2bcSschwarze ts("<%06s>", "text", "<00text>");
324638aa2bcSschwarze ts("<%-06s>", "text", "<text >");
325638aa2bcSschwarze ts("<%hs>", "text", "<text>");
326638aa2bcSschwarze }
327638aa2bcSschwarze
328638aa2bcSschwarze /*
329638aa2bcSschwarze * Valid use cases of %lc and %ls in the POSIX locale.
330638aa2bcSschwarze */
331638aa2bcSschwarze
332638aa2bcSschwarze tlc("<%lc>", L'=', "<=>");
333638aa2bcSschwarze tlc("<%lc>", L'\t', "<\t>");
334638aa2bcSschwarze tlc_expect_fail("<%lc>", 0x03c0);
335638aa2bcSschwarze tlc("<%-lc>", L'=', "<=>");
336638aa2bcSschwarze tlc("<%2lc>", L'=', "< =>");
337638aa2bcSschwarze tlc("<%-2lc>", L'=', "<= >");
338638aa2bcSschwarze
339638aa2bcSschwarze tls("<%ls>", L"text", "<text>");
340638aa2bcSschwarze tls_expect_fail("<%ls>", ws);
341638aa2bcSschwarze tls_expect_fail("<%ls>", wsbad);
342638aa2bcSschwarze tls("<%-ls>", L"text", "<text>");
343638aa2bcSschwarze tls("<%6ls>", L"text", "< text>");
344638aa2bcSschwarze tls("<%-6ls>", L"text", "<text >");
345638aa2bcSschwarze tls("<%.2ls>", L"text", "<te>");
346638aa2bcSschwarze tls("<%4.2ls>", L"text", "< te>");
347638aa2bcSschwarze tls("<%-4.2ls>", L"text", "<te >");
348638aa2bcSschwarze
349638aa2bcSschwarze /*
350638aa2bcSschwarze * Undefined behaviour of %lc and %ls in the POSIX locale.
351638aa2bcSschwarze */
352638aa2bcSschwarze
353638aa2bcSschwarze if (picky) {
354638aa2bcSschwarze tlc("<%lc>", 0x00fe, "<\xfe>");
355638aa2bcSschwarze tlc("<%#lc>", L'=', "<=>");
356638aa2bcSschwarze tlc("<% -3lc>", L'=', "<= >");
357638aa2bcSschwarze tlc("<%+-3lc>", L'=', "<= >");
358638aa2bcSschwarze tlc("<%03lc>", L'=', "<00=>");
359638aa2bcSschwarze tlc("<%-03lc>", L'=', "<= >");
360638aa2bcSschwarze tlc("<%3.2lc>", L'=', "< =>");
361638aa2bcSschwarze tc("<%llc>", '=', "<=>");
362638aa2bcSschwarze
363638aa2bcSschwarze tls("<%#ls>", L"text", "<text>");
364638aa2bcSschwarze tls("<% -6ls>", L"text", "<text >");
365638aa2bcSschwarze tls("<%+-6ls>", L"text", "<text >");
366638aa2bcSschwarze tls("<%06ls>", L"text", "<00text>");
367638aa2bcSschwarze tls("<%-06ls>", L"text", "<text >");
368638aa2bcSschwarze ts("<%lls>", "text", "<text>");
369638aa2bcSschwarze }
370638aa2bcSschwarze
371638aa2bcSschwarze /*
372638aa2bcSschwarze * Valid use cases of %lc and %ls in a UTF-8 locale.
373638aa2bcSschwarze */
374638aa2bcSschwarze
375638aa2bcSschwarze if (setlocale(LC_CTYPE, "C.UTF-8") == NULL)
376638aa2bcSschwarze err(1, "setlocale");
377638aa2bcSschwarze
378638aa2bcSschwarze tlc("<%lc>", L'=', "<=>");
379638aa2bcSschwarze tlc("<%lc>", L'\t', "<\t>");
380638aa2bcSschwarze tlc("<%lc>", 0x00fe, "<\xc3\xbe>");
381638aa2bcSschwarze tlc("<%lc>", 0x03c0, "<\xcf\x80>");
382638aa2bcSschwarze tlc_expect_fail("<%lc>", 0x123456);
383638aa2bcSschwarze tlc("<%-lc>", L'=', "<=>");
384638aa2bcSschwarze tlc("<%-lc>", 0x03c0, "<\xcf\x80>");
385638aa2bcSschwarze tlc("<%2lc>", L'=', "< =>");
386638aa2bcSschwarze tlc("<%3lc>", 0x03c0, "< \xcf\x80>");
387638aa2bcSschwarze tlc("<%-2lc>", L'=', "<= >");
388638aa2bcSschwarze tlc("<%-3lc>", 0x03c0, "<\xcf\x80 >");
389638aa2bcSschwarze
390638aa2bcSschwarze tls("<%ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>");
391638aa2bcSschwarze tls_expect_fail("<%ls>", wsbad);
392638aa2bcSschwarze tls("<%-ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>");
393638aa2bcSschwarze tls("<%9ls>", ws, "< \xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f>");
394638aa2bcSschwarze tls("<%-9ls>", ws, "<\xd0\xa1\xd0\xbe\xd1\x84\xd1\x8f >");
395638aa2bcSschwarze tls("<%.4ls>", ws, "<\xd0\xa1\xd0\xbe>");
396638aa2bcSschwarze tls("<%.3ls>", ws, "<\xd0\xa1>");
397638aa2bcSschwarze tls("<%6.4ls>", ws, "< \xd0\xa1\xd0\xbe>");
398638aa2bcSschwarze tls("<%3.3ls>", ws, "< \xd0\xa1>");
399638aa2bcSschwarze tls("<%-6.4ls>", ws, "<\xd0\xa1\xd0\xbe >");
400638aa2bcSschwarze tls("<%-3.3ls>", ws, "<\xd0\xa1 >");
401638aa2bcSschwarze
402638aa2bcSschwarze /*
403638aa2bcSschwarze * Undefined behaviour of %lc and %ls in a UTF-8 locale.
404638aa2bcSschwarze */
405638aa2bcSschwarze
406638aa2bcSschwarze if (picky) {
407638aa2bcSschwarze tlc("<%#lc>", 0x03c0, "<\xcf\x80>");
408638aa2bcSschwarze tlc("<% -4lc>", 0x03c0, "<\xcf\x80 >");
409638aa2bcSschwarze tlc("<%+-4lc>", 0x03c0, "<\xcf\x80 >");
410638aa2bcSschwarze tlc("<%04lc>", 0x03c0, "<00\xcf\x80>");
411638aa2bcSschwarze tlc("<%-04lc>", 0x03c0, "<\xcf\x80 >");
412638aa2bcSschwarze tlc("<%4.5lc>", 0x03c0, "< \xcf\x80>");
413638aa2bcSschwarze tlc("<%4.3lc>", 0x03c0, "< \xcf\x80>");
414638aa2bcSschwarze tlc("<%4.1lc>", 0x03c0, "< \xcf\x80>");
415638aa2bcSschwarze tc("<%llc>", 0xfe, "<\xfe>");
416638aa2bcSschwarze
417638aa2bcSschwarze tls("<%#ls>", ws + 2, "<\xd1\x84\xd1\x8f>");
418638aa2bcSschwarze tls("<% -6ls>", ws + 2, "<\xd1\x84\xd1\x8f >");
419638aa2bcSschwarze tls("<%+-6ls>", ws + 2, "<\xd1\x84\xd1\x8f >");
420638aa2bcSschwarze tls("<%06ls>", ws + 2, "<00\xd1\x84\xd1\x8f>");
421638aa2bcSschwarze tls("<%-06ls>", ws + 2, "<\xd1\x84\xd1\x8f >");
422638aa2bcSschwarze ts("<%lls>", "text", "<text>");
423638aa2bcSschwarze }
424638aa2bcSschwarze
425638aa2bcSschwarze /*
426638aa2bcSschwarze * Summarize the results.
427638aa2bcSschwarze */
428638aa2bcSschwarze
429638aa2bcSschwarze if (badret + badlen + badout)
430638aa2bcSschwarze errx(1, "ERRORS: %d fail + %d mismatch (incl. %d bad length)",
431638aa2bcSschwarze badret, badout, badlen);
432638aa2bcSschwarze else if (verbose)
433638aa2bcSschwarze warnx("SUCCESS");
434638aa2bcSschwarze return 0;
435638aa2bcSschwarze }
436