posix: Remove alloca usage for internal fnmatch implementation
[platform/upstream/glibc.git] / posix / tst-getopt-cancel.c
1 /* Copyright (C) 2017-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17
18 /* fprintf is a cancellation point, but getopt is not supposed to be a
19    cancellation point, even when it prints error messages.  */
20
21 /* Note: getopt.h must be included first in this file, so we get the
22    GNU getopt rather than the POSIX one.  */
23 #include <getopt.h>
24
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <fcntl.h>
30 #include <pthread.h>
31 #include <unistd.h>
32
33 #include <support/support.h>
34 #include <support/temp_file.h>
35 #include <support/xthread.h>
36
37 static bool
38 check_stderr (bool expect_errmsg, FILE *stderr_trapped)
39 {
40   static char *lineptr = 0;
41   static size_t linesz = 0;
42
43   bool got_errmsg = false;
44   rewind (stderr_trapped);
45   while (getline (&lineptr, &linesz, stderr_trapped) > 0)
46     {
47       got_errmsg = true;
48       fputs (lineptr, stdout);
49     }
50   rewind (stderr_trapped);
51   ftruncate (fileno (stderr_trapped), 0);
52   return got_errmsg == expect_errmsg;
53 }
54
55 struct test_short
56 {
57   const char *label;
58   const char *opts;
59   const char *const argv[8];
60   int argc;
61   bool expect_errmsg;
62 };
63
64 struct test_long
65 {
66   const char *label;
67   const char *opts;
68   const struct option longopts[4];
69   const char *const argv[8];
70   int argc;
71   bool expect_errmsg;
72 };
73
74 #define DEFINE_TEST_DRIVER(test_type, getopt_call)                      \
75   struct test_type##_tdata                                              \
76   {                                                                     \
77     pthread_mutex_t *sync;                                              \
78     const struct test_type *tcase;                                      \
79     bool ok;                                                            \
80   };                                                                    \
81                                                                         \
82   static void *                                                         \
83   test_type##_threadproc (void *data)                                   \
84   {                                                                     \
85     struct test_type##_tdata *tdata = data;                             \
86     const struct test_type *tc = tdata->tcase;                          \
87                                                                         \
88     xpthread_mutex_lock (tdata->sync);                                  \
89     xpthread_mutex_unlock (tdata->sync);                                \
90                                                                         \
91     /* At this point, this thread has a cancellation pending.           \
92        We should still be able to get all the way through a getopt      \
93        loop without being cancelled.                                    \
94        Setting optind to 0 forces getopt to reinitialize itself.  */    \
95     optind = 0;                                                         \
96     opterr = 1;                                                         \
97     optopt = 0;                                                         \
98     while (getopt_call != -1)                                           \
99       ;                                                                 \
100     tdata->ok = true;                                                   \
101                                                                         \
102     pthread_testcancel();                                               \
103     return 0;                                                           \
104   }                                                                     \
105                                                                         \
106   static bool                                                           \
107   do_##test_type (const struct test_type *tcase, FILE *stderr_trapped)  \
108   {                                                                     \
109     pthread_mutex_t sync;                                               \
110     struct test_type##_tdata tdata;                                     \
111                                                                         \
112     printf("begin: %s\n", tcase->label);                                \
113                                                                         \
114     xpthread_mutex_init (&sync, 0);                                     \
115     xpthread_mutex_lock (&sync);                                        \
116                                                                         \
117     tdata.sync = &sync;                                                 \
118     tdata.tcase = tcase;                                                \
119     tdata.ok = false;                                                   \
120                                                                         \
121     pthread_t thr = xpthread_create (0, test_type##_threadproc,         \
122                                      (void *)&tdata);                   \
123     xpthread_cancel (thr);                                              \
124     xpthread_mutex_unlock (&sync);                                      \
125     void *rv = xpthread_join (thr);                                     \
126                                                                         \
127     xpthread_mutex_destroy (&sync);                                     \
128                                                                         \
129     bool ok = true;                                                     \
130     if (!check_stderr (tcase->expect_errmsg, stderr_trapped))           \
131       {                                                                 \
132         ok = false;                                                     \
133         printf("FAIL: %s: stderr not as expected\n", tcase->label);     \
134       }                                                                 \
135     if (!tdata.ok)                                                      \
136       {                                                                 \
137         ok = false;                                                     \
138         printf("FAIL: %s: did not complete loop\n", tcase->label);      \
139       }                                                                 \
140     if (rv != PTHREAD_CANCELED)                                         \
141       {                                                                 \
142         ok = false;                                                     \
143         printf("FAIL: %s: thread was not cancelled\n", tcase->label);   \
144       }                                                                 \
145     if (ok)                                                             \
146       printf ("pass: %s\n", tcase->label);                              \
147     return ok;                                                          \
148   }
149
150 DEFINE_TEST_DRIVER (test_short,
151                     getopt (tc->argc, (char *const *)tc->argv, tc->opts))
152 DEFINE_TEST_DRIVER (test_long,
153                     getopt_long (tc->argc, (char *const *)tc->argv,
154                                  tc->opts, tc->longopts, 0))
155
156 /* Caution: all option strings must begin with a '+' or '-' so that
157    getopt does not attempt to permute the argument vector (which is in
158    read-only memory).  */
159 const struct test_short tests_short[] = {
160   { "no errors",
161     "+ab:c", { "program", "-ac", "-b", "x", 0 }, 4, false },
162   { "invalid option",
163     "+ab:c", { "program", "-d", 0 },             2, true },
164   { "missing argument",
165     "+ab:c", { "program", "-b", 0 },             2, true },
166   { 0 }
167 };
168
169 const struct test_long tests_long[] = {
170   { "no errors (long)",
171     "+ab:c", { { "alpha",   no_argument,       0, 'a' },
172                { "bravo",   required_argument, 0, 'b' },
173                { "charlie", no_argument,       0, 'c' },
174                { 0 } },
175     { "program", "-a", "--charlie", "--bravo=x", 0 }, 4, false },
176
177   { "invalid option (long)",
178     "+ab:c", { { "alpha",   no_argument,       0, 'a' },
179                { "bravo",   required_argument, 0, 'b' },
180                { "charlie", no_argument,       0, 'c' },
181                { 0 } },
182     { "program", "-a", "--charlie", "--dingo", 0 }, 4, true },
183
184   { "unwanted argument",
185     "+ab:c", { { "alpha",   no_argument,       0, 'a' },
186                { "bravo",   required_argument, 0, 'b' },
187                { "charlie", no_argument,       0, 'c' },
188                { 0 } },
189     { "program", "-a", "--charlie=dingo", "--bravo=x", 0 }, 4, true },
190
191   { "missing argument",
192     "+ab:c", { { "alpha",   no_argument,       0, 'a' },
193                { "bravo",   required_argument, 0, 'b' },
194                { "charlie", no_argument,       0, 'c' },
195                { 0 } },
196     { "program", "-a", "--charlie", "--bravo", 0 }, 4, true },
197
198   { "ambiguous options",
199     "+uvw", { { "veni", no_argument, 0, 'u' },
200               { "vedi", no_argument, 0, 'v' },
201               { "veci", no_argument, 0, 'w' } },
202     { "program", "--ve", 0 }, 2, true },
203
204   { "no errors (long W)",
205     "+ab:cW;", { { "alpha",   no_argument,       0, 'a' },
206                  { "bravo",   required_argument, 0, 'b' },
207                  { "charlie", no_argument,       0, 'c' },
208                  { 0 } },
209     { "program", "-a", "-W", "charlie", "-W", "bravo=x", 0 }, 6, false },
210
211   { "missing argument (W itself)",
212     "+ab:cW;", { { "alpha",   no_argument,       0, 'a' },
213                  { "bravo",   required_argument, 0, 'b' },
214                  { "charlie", no_argument,       0, 'c' },
215                  { 0 } },
216     { "program", "-a", "-W", "charlie", "-W", 0 }, 5, true },
217
218   { "missing argument (W longopt)",
219     "+ab:cW;", { { "alpha",   no_argument,       0, 'a' },
220                  { "bravo",   required_argument, 0, 'b' },
221                  { "charlie", no_argument,       0, 'c' },
222                  { 0 } },
223     { "program", "-a", "-W", "charlie", "-W", "bravo", 0 }, 6, true },
224
225   { "unwanted argument (W longopt)",
226     "+ab:cW;", { { "alpha",   no_argument,       0, 'a' },
227                  { "bravo",   required_argument, 0, 'b' },
228                  { "charlie", no_argument,       0, 'c' },
229                  { 0 } },
230     { "program", "-a", "-W", "charlie=dingo", "-W", "bravo=x", 0 }, 6, true },
231
232   { "ambiguous options (W)",
233     "+uvwW;", { { "veni", no_argument, 0, 'u' },
234                 { "vedi", no_argument, 0, 'v' },
235                 { "veci", no_argument, 0, 'w' } },
236     { "program", "-W", "ve", 0 }, 3, true },
237
238   { 0 }
239 };
240
241 static int
242 do_test (void)
243 {
244   int stderr_trap = create_temp_file ("stderr", 0);
245   if (stderr_trap < 0)
246     {
247       perror ("create_temp_file");
248       return 1;
249     }
250   FILE *stderr_trapped = fdopen(stderr_trap, "r+");
251   if (!stderr_trapped)
252     {
253       perror ("fdopen");
254       return 1;
255     }
256   int old_stderr = dup (fileno (stderr));
257   if (old_stderr < 0)
258     {
259       perror ("dup");
260       return 1;
261     }
262   if (dup2 (stderr_trap, 2) < 0)
263     {
264       perror ("dup2");
265       return 1;
266     }
267   rewind (stderr);
268
269   bool success = true;
270
271   for (const struct test_short *tcase = tests_short; tcase->label; tcase++)
272     success = do_test_short (tcase, stderr_trapped) && success;
273
274   for (const struct test_long *tcase = tests_long; tcase->label; tcase++)
275     success = do_test_long (tcase, stderr_trapped) && success;
276
277   dup2 (old_stderr, 2);
278   close (old_stderr);
279   fclose (stderr_trapped);
280
281   return success ? 0 : 1;
282 }
283
284 #include <support/test-driver.c>