1 /* Test program for the wide character stream functions handling larger
2    amounts of text.
3    Copyright (C) 2000-2022 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19 
20 #include <assert.h>
21 #include <iconv.h>
22 #include <locale.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <wchar.h>
28 
29 /* Approximate size of the file (must be larger).  */
30 #define SIZE 210000
31 
32 
33 static int
do_test(void)34 do_test (void)
35 {
36   char name[] = "/tmp/widetext.out.XXXXXX";
37   char mbbuf[SIZE];
38   char mb2buf[SIZE];
39   wchar_t wcbuf[SIZE];
40   wchar_t wc2buf[SIZE];
41   size_t mbsize;
42   size_t wcsize;
43   int fd;
44   FILE *fp;
45   size_t n;
46   int res;
47   int status = 0;
48   wchar_t *wcp;
49 
50   setlocale (LC_ALL, "de_DE.UTF-8");
51   printf ("locale used: %s\n\n", setlocale (LC_ALL, NULL));
52 
53   /* Read the file into memory.  */
54   mbsize = fread (mbbuf, 1, SIZE, stdin);
55   if (mbsize == 0)
56     {
57       printf ("%u: cannot read input file from standard input: %m\n",
58 	      __LINE__);
59       exit (1);
60     }
61 
62    printf ("INFO: input file has %Zd bytes\n", mbsize);
63 
64   /* First convert the text to wide characters.  We use iconv here.  */
65   {
66     iconv_t cd;
67     char *inbuf = mbbuf;
68     size_t inleft = mbsize;
69     char *outbuf = (char *) wcbuf;
70     size_t outleft = sizeof (wcbuf);
71     size_t nonr;
72 
73     cd = iconv_open ("WCHAR_T", "UTF-8");
74     if (cd == (iconv_t) -1)
75       {
76 	printf ("%u: cannot get iconv descriptor for conversion to UCS4\n",
77 		__LINE__);
78 	exit (1);
79       }
80 
81     /* We must need only one call and there must be no losses.  */
82     nonr = iconv (cd, &inbuf, &inleft, &outbuf, &outleft);
83     if (nonr != 0 && nonr != (size_t) -1)
84       {
85 	printf ("%u: iconv performed %Zd nonreversible conversions\n",
86 		__LINE__, nonr);
87 	exit (1);
88       }
89 
90     if  (nonr == (size_t) -1)
91       {
92 	printf ("\
93 %u: iconv returned with %Zd and errno = %m (inleft: %Zd, outleft: %Zd)\n",
94 		__LINE__, nonr, inleft, outleft);
95 	exit (1);
96       }
97 
98     if (inleft != 0)
99       {
100 	printf ("%u: iconv didn't convert all input\n", __LINE__);
101 	exit (1);
102       }
103 
104     iconv_close (cd);
105 
106     if ((sizeof (wcbuf) - outleft) % sizeof (wchar_t) != 0)
107       {
108 	printf ("%u: iconv converted not complete wchar_t\n", __LINE__);
109 	exit (1);
110       }
111 
112     wcsize = (sizeof (wcbuf) - outleft) / sizeof (wchar_t);
113     assert (wcsize + 1 <= SIZE);
114   }
115 
116   /* Now that we finished the preparations, run the first test.  We
117      are writing the wide char data out and read it back in.  We write
118      and read single characters.  */
119 
120   fd = mkstemp (name);
121   if (fd == -1)
122     {
123       printf ("%u: cannot open temporary file: %m\n", __LINE__);
124       exit (1);
125     }
126 
127   unlink (name);
128 
129   fp = fdopen (dup (fd), "w");
130   if (fp == NULL)
131     {
132       printf ("%u: fdopen of temp file for writing failed: %m\n", __LINE__);
133       exit (1);
134     }
135 
136   for (n = 0; n < wcsize; ++n)
137     {
138       if (fputwc (wcbuf[n], fp) == WEOF)
139 	{
140 	  printf ("%u: fputwc failed: %m\n", __LINE__);
141 	  exit (1);
142 	}
143     }
144 
145   res = fclose (fp);
146   if (res != 0)
147     {
148       printf ("%u: fclose after single-character writing failed (%d): %m\n",
149 	      __LINE__, res);
150       exit (1);
151     }
152 
153   lseek (fd, SEEK_SET, 0);
154   fp = fdopen (dup (fd), "r");
155   if (fp == NULL)
156     {
157       printf ("%u: fdopen of temp file for reading failed: %m\n", __LINE__);
158       exit (1);
159     }
160 
161   for (n = 0; n < wcsize; ++n)
162     {
163       wint_t wch = fgetwc (fp);
164       if (wch == WEOF)
165 	{
166 	  printf ("%u: fgetwc failed (idx %Zd): %m\n", __LINE__, n);
167 	  exit (1);
168 	}
169       wc2buf[n] = wch;
170     }
171 
172   /* There should be nothing else.  */
173   if (fgetwc (fp) != WEOF)
174     {
175       printf ("%u: too many characters available with fgetwc\n", __LINE__);
176       status = 1;
177     }
178   else if (wmemcmp (wcbuf, wc2buf, wcsize) != 0)
179     {
180       printf ("%u: buffer read with fgetwc differs\n", __LINE__);
181       status = 1;
182     }
183 
184   res = fclose (fp);
185   if (res != 0)
186     {
187       printf ("%u: fclose after single-character reading failed (%d): %m\n",
188 	      __LINE__, res);
189       exit (1);
190     }
191 
192   /* Just make sure there are no two errors which hide each other, read the
193      file using the `char' functions.  */
194 
195   lseek (fd, SEEK_SET, 0);
196   fp = fdopen (fd, "r");
197   if (fp == NULL)
198     {
199       printf ("%u: fdopen of temp file for reading failed: %m\n", __LINE__);
200       exit (1);
201     }
202 
203   if (fread (mb2buf, 1, mbsize, fp) != mbsize)
204     {
205       printf ("%u: cannot read all of the temp file\n", __LINE__);
206       status = 1;
207     }
208   else
209     {
210       /* Make sure there is nothing left.  */
211       if (fgetc (fp) != EOF)
212 	{
213 	  printf ("%u: more input available\n", __LINE__);
214 	  status = 1;
215 	}
216 
217       if (memcmp (mb2buf, mbbuf, mbsize) != 0)
218 	{
219 	  printf ("%u: buffer written with fputwc differs\n", __LINE__);
220 	  status = 1;
221 	}
222     }
223 
224   res = fclose (fp);
225   if (res != 0)
226     {
227       printf ("%u: fclose after single-character reading failed (%d): %m\n",
228 	      __LINE__, res);
229       exit (1);
230     }
231 
232   /* Now to reading and writing line-wise.  */
233 
234   fd = mkstemp (strcpy (name, "/tmp/widetext.out.XXXXXX"));
235   if (fd == -1)
236     {
237       printf ("%u: cannot open temporary file: %m\n", __LINE__);
238       exit (1);
239     }
240 
241   unlink (name);
242 
243   fp = fdopen (dup (fd), "w");
244   if (fp == NULL)
245     {
246       printf ("%u: fdopen of temp file for writing failed: %m\n", __LINE__);
247       exit (1);
248     }
249 
250   for (wcp = wcbuf; wcp < &wcbuf[wcsize]; )
251     {
252       wchar_t *wendp = wcschr (wcp, L'\n');
253 
254       if (wendp != NULL)
255 	{
256 	  /* Temporarily NUL terminate the line.  */
257 	  wchar_t save = wendp[1];
258 	  wendp[1] = L'\0';
259 
260 	  fputws (wcp, fp);
261 
262 	  wendp[1] = save;
263 	  wcp = &wendp[1];
264 	}
265       else
266 	{
267 	  fputws (wcp, fp);
268 	  wcp = wcschr (wcp, L'\0');
269 	  assert (wcp == &wcbuf[wcsize]);
270 	}
271     }
272 
273   res = fclose (fp);
274   if (res != 0)
275     {
276       printf ("%u: fclose after line-wise writing failed (%d): %m\n",
277 	      __LINE__, res);
278       exit (1);
279     }
280 
281   lseek (fd, SEEK_SET, 0);
282   fp = fdopen (dup (fd), "r");
283   if (fp == NULL)
284     {
285       printf ("%u: fdopen of temp file for reading failed: %m\n", __LINE__);
286       exit (1);
287     }
288 
289   for (wcp = wc2buf; wcp < &wc2buf[wcsize]; )
290     {
291       if (fgetws (wcp, &wc2buf[wcsize] - wcp + 1, fp) == NULL)
292 	{
293 	  printf ("%u: short read using fgetws (only %td of %Zd)\n",
294 		  __LINE__, wcp - wc2buf, wcsize);
295 	  status = 1;
296 	  break;
297 	}
298       wcp = wcschr (wcp, L'\0');
299     }
300 
301   if (wcp > &wc2buf[wcsize])
302     {
303       printf ("%u: fgetws read too much\n", __LINE__);
304       status = 1;
305     }
306   else if (fgetwc (fp) != WEOF)
307     {
308       /* There should be nothing else.  */
309       printf ("%u: too many characters available with fgetws\n", __LINE__);
310       status = 1;
311     }
312 
313   if (wcp >= &wc2buf[wcsize] && wmemcmp (wcbuf, wc2buf, wcsize) != 0)
314     {
315       printf ("%u: buffer read with fgetws differs\n", __LINE__);
316       status = 1;
317     }
318 
319   res = fclose (fp);
320   if (res != 0)
321     {
322       printf ("%u: fclose after single-character reading failed (%d): %m\n",
323 	      __LINE__, res);
324       exit (1);
325     }
326 
327   /* Just make sure there are no two errors which hide each other, read the
328      file using the `char' functions.  */
329 
330   lseek (fd, SEEK_SET, 0);
331   fp = fdopen (fd, "r");
332   if (fp == NULL)
333     {
334       printf ("%u: fdopen of temp file for reading failed: %m\n", __LINE__);
335       exit (1);
336     }
337 
338   if (fread (mb2buf, 1, mbsize, fp) != mbsize)
339     {
340       printf ("%u: cannot read all of the temp file\n", __LINE__);
341       status = 1;
342     }
343   else
344     {
345       /* Make sure there is nothing left.  */
346       if (fgetc (fp) != EOF)
347 	{
348 	  printf ("%u: more input available\n", __LINE__);
349 	  status = 1;
350 	}
351 
352       if (memcmp (mb2buf, mbbuf, mbsize) != 0)
353 	{
354 	  printf ("%u: buffer written with fputws differs\n", __LINE__);
355 	  status = 1;
356 	}
357     }
358 
359   res = fclose (fp);
360   if (res != 0)
361     {
362       printf ("%u: fclose after single-character reading failed (%d): %m\n",
363 	      __LINE__, res);
364       exit (1);
365     }
366 
367   return status;
368 }
369 
370 #define TEST_FUNCTION do_test ()
371 #include "../test-skeleton.c"
372