1 /*
2  * Copyright (c) 2009 Philip Guenther
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * Test whether the various stdio functions set the stream orientation
32  * ("width") as they should
33  */
34 
35 #include <sys/types.h>
36 #include <err.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <wchar.h>
43 
44 char filename[] = "/tmp/fwide.XXXXXXXXXX";
45 
46 FILE *dup_stdout = NULL;
47 int failures = 0;
48 
49 void
fail(int line,int r,char const * expect,char const * test)50 fail(int line, int r, char const *expect, char const *test)
51 {
52 	failures++;
53 	fprintf(dup_stdout,
54 		"FAIL: %d: fwide returned %d, expected %s 0 after %s\n",
55 		line, r, expect, test);
56 }
57 
58 FILE *
setup(int line)59 setup(int line)
60 {
61 	FILE	*f;
62 	int	r;
63 
64 	if ((f = fopen(filename, "r+")) == NULL)
65 		err(2, "fopen");
66 	if ((r = fwide(f, 0)) != 0)
67 		fail(line, r, "==", "fopen");
68 	return (f);
69 }
70 
71 FILE *
setup_std(FILE * std,int line)72 setup_std(FILE *std, int line)
73 {
74 	int	r;
75 
76 	if (freopen(filename, "r+", std) == NULL)
77 		err(2, "freopen");
78 	if ((r = fwide(std, 0)) != 0)
79 		fail(line, r, "==", "freopen");
80 	return (std);
81 }
82 
83 #define TEST_(x, op)						\
84 	do {							\
85 		f = setup(__LINE__);				\
86 		x;						\
87 		if (!((r = fwide(f, 0)) op 0))			\
88 			fail(__LINE__, r, #op, #x);		\
89 		fclose(f);					\
90 	} while (0)
91 
92 #define TEST_STD_(std, x, op)					\
93 	do {							\
94 		f = setup_std(std, __LINE__);			\
95 		x;						\
96 		if (!((r = fwide(f, 0)) op 0))			\
97 			fail(__LINE__, r, #op, #x);		\
98 	} while (0)
99 
100 #define TEST_UNCHANGED(x)		TEST_(x, ==)
101 #define TEST_NARROW(x)			TEST_(x, <)
102 #define TEST_WIDE(x)			TEST_(x, >)
103 #define TEST_UNCHANGED_STD(std, x)	TEST_STD_(std, x, ==)
104 #define TEST_NARROW_STD(std, x)		TEST_STD_(std, x, <)
105 #define TEST_WIDE_STD(std, x)		TEST_STD_(std, x, >)
106 
107 int
main(int argc,char * argv[])108 main(int argc, char *argv[])
109 {
110 	char	buffer[BUFSIZ];
111 	wchar_t	wbuffer[BUFSIZ];
112 	char	*buf;
113 	wchar_t	*wbuf;
114 	FILE	*f;
115 	fpos_t	pos;
116 	size_t	size;
117 	int	fd, r;
118 	char	c;
119 	wchar_t	wc;
120 
121 	if ((fd = dup(1)) == -1)
122 		err(2, "dup");
123 	if ((dup_stdout = fdopen(fd, "w")) == NULL)
124 		err(2, "fdopen");
125 	if ((fd = mkstemp(filename)) == -1)
126 		err(2, "mkstemp");
127 	if (write(fd, "0123456789\n\n", 12) != 12 || close(fd))
128 		err(2, "write + close");
129 
130 	/* status */
131 	TEST_UNCHANGED(fwide(f, 0));
132 	TEST_NARROW(fwide(f, -1));
133 	TEST_WIDE(fwide(f, 1));
134 	TEST_UNCHANGED(feof(f));
135 	TEST_UNCHANGED(ferror(f));
136 	TEST_UNCHANGED(fileno(f));
137 	TEST_UNCHANGED(clearerr(f));
138 
139 	/* flush and purge */
140 	TEST_UNCHANGED(fflush(f));
141 
142 	/* positioning */
143 	TEST_UNCHANGED(fgetpos(f, &pos));
144 	TEST_UNCHANGED(fgetpos(f, &pos); fsetpos(f, &pos));
145 	TEST_UNCHANGED(ftell(f));
146 	TEST_UNCHANGED(ftello(f));
147 	TEST_UNCHANGED(fseek(f, 1, SEEK_CUR));
148 	TEST_UNCHANGED(fseek(f, 1, SEEK_SET));
149 	TEST_UNCHANGED(fseek(f, 1, SEEK_END));
150 	TEST_UNCHANGED(fseeko(f, 1, SEEK_CUR));
151 	TEST_UNCHANGED(fseeko(f, 1, SEEK_SET));
152 	TEST_UNCHANGED(fseeko(f, 1, SEEK_END));
153 	TEST_UNCHANGED(rewind(f));
154 
155 	/* buffering */
156 	TEST_UNCHANGED(setbuf(f, NULL));
157 	TEST_UNCHANGED(setbuf(f, buffer));
158 	TEST_UNCHANGED(setvbuf(f, buffer, _IONBF, BUFSIZ));
159 	TEST_UNCHANGED(setvbuf(f, buffer, _IOLBF, BUFSIZ));
160 	TEST_UNCHANGED(setvbuf(f, buffer, _IOFBF, BUFSIZ));
161 	TEST_UNCHANGED(setvbuf(f, NULL, _IONBF, 0));
162 	TEST_UNCHANGED(setvbuf(f, NULL, _IOLBF, 0));
163 	TEST_UNCHANGED(setvbuf(f, NULL, _IOFBF, 0));
164 	TEST_UNCHANGED(setbuffer(f, NULL, 0));
165 	TEST_UNCHANGED(setbuffer(f, buffer, BUFSIZ));
166 	TEST_UNCHANGED(setlinebuf(f));
167 
168 	/* locking */
169 	TEST_UNCHANGED(flockfile(f);funlockfile(f));
170 	TEST_UNCHANGED(ftrylockfile(f);funlockfile(f));
171 
172 	/* input */
173 	TEST_NARROW(getc(f));
174 	TEST_NARROW(getc_unlocked(f));
175 	TEST_NARROW(fgetc(f));
176 	TEST_NARROW(c = fgetc(f); ungetc(c, f));
177 	TEST_NARROW(fgets(buffer, BUFSIZ, f));
178 	TEST_NARROW(fscanf(f, "%s\n", buffer));
179 
180 	/* output */
181 	TEST_NARROW(putc('c', f));
182 	TEST_NARROW(putc_unlocked('c', f));
183 	TEST_NARROW(fputc('c', f));
184 	TEST_NARROW(fputs("foo", f));
185 	TEST_NARROW(fprintf(f, "%s\n", "foo"));
186 
187 	/* input from stdin */
188 	TEST_NARROW_STD(stdin, getchar());
189 	TEST_NARROW_STD(stdin, getchar_unlocked());
190 	TEST_NARROW_STD(stdin, scanf("%s\n", buffer));
191 
192 	/* output to stdout */
193 	TEST_NARROW_STD(stdout, putchar('c'));
194 	TEST_NARROW_STD(stdout, putchar_unlocked('c'));
195 	TEST_NARROW_STD(stdout, puts("foo"));
196 	TEST_NARROW_STD(stdout, printf("foo"));
197 
198 	/* word-size ops */
199 	/*
200 	 * fread and fwrite are specified as being implemented in
201 	 * terms of fgetc() and fputc() and therefore must set the
202 	 * stream orientation to narrow.
203 	 */
204 	TEST_NARROW(fread(buffer, 4, BUFSIZ / 4, f));
205 	TEST_NARROW(fwrite(buffer, 4, BUFSIZ / 4, f));
206 
207 	/*
208 	 * getw() and putw() aren't specified anywhere but logically
209 	 * should behave the same as fread/fwrite.  Not all OSes agree:
210 	 * Solaris 10 has them not changing the orientation.
211 	 */
212 	TEST_NARROW(getw(f));
213 	TEST_NARROW(putw(1234, f));
214 
215 
216 	/* WIDE CHAR TIME! */
217 
218 	/* input */
219 	TEST_WIDE(getwc(f));
220 	TEST_WIDE(fgetwc(f));
221 	TEST_WIDE(wc = fgetwc(f); ungetwc(wc, f));
222 	TEST_WIDE(fgetws(wbuffer, BUFSIZ, f));
223 	TEST_WIDE(fwscanf(f, L"%s\n", wbuffer));
224 
225 	/* output */
226 	TEST_WIDE(putwc(L'c', f));
227 	TEST_WIDE(fputwc(L'c', f));
228 	TEST_WIDE(fputws(L"foo", f));
229 	TEST_WIDE(fwprintf(f, L"%s\n", L"foo"));
230 
231 	/* input from stdin */
232 	TEST_WIDE_STD(stdin, getwchar());
233 	TEST_WIDE_STD(stdin, wscanf(L"%s\n", wbuffer));
234 
235 	/* output to stdout */
236 	TEST_WIDE_STD(stdout, putwchar(L'c'));
237 	TEST_WIDE_STD(stdout, wprintf(L"foo"));
238 
239 
240 	/* memory streams */
241 	f = open_memstream(&buf, &size);
242 	if (!((r = fwide(f, 0)) < 0))
243 		fail(__LINE__, r, "<", "open_memstream()");
244 	fclose(f);
245 	f = open_wmemstream(&wbuf, &size);
246 	if (f != NULL && !((r = fwide(f, 0)) > 0))
247 		fail(__LINE__, r, ">", "open_wmemstream()");
248 	fclose(f);
249 
250 
251 	/* random stuff? */
252 	TEST_UNCHANGED_STD(stderr, perror("foo"));
253 
254 	remove(filename);
255 	if (failures)
256 		exit(1);
257 	exit(0);
258 }
259