xref: /openbsd-src/regress/lib/libc/printf/string.c (revision c39afc64ddd4057073776bbe8e725caa516723eb)
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