Bug Summary

File:.build-ci/../tests/unit/c/test-argconfig-diff.c
Warning:line 430, column 11
The 1st argument to 'fdopen' is < 0 but should be >= 0

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name test-argconfig-diff.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I tests/unit/c/test-argconfig-diff.p -I tests/unit/c -I ../tests/unit/c -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../tests/unit/c/test-argconfig-diff.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Compare argconfig_parse() and argconfig_parse_global() with a frozen copy
4 * of the getopt based parser (argconfig-getopt-ref.c) on generated command
5 * lines: the return value, all option values, the seen flags and the
6 * operands must be identical.
7 *
8 * Two kinds of input are deliberately not supported anymore and are
9 * filtered out: single-dash long options ("-flag", which getopt_long_only()
10 * accepted) and abbreviated long options ("--fla"). They are tested in
11 * test-argconfig-parse.
12 *
13 * getopt's permutation and error handling differ between C libraries, the
14 * reference is only meaningful with glibc.
15 */
16
17#include <errno(*__errno_location ()).h>
18#include <locale.h>
19#include <stdbool.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25#include <time.h>
26
27#include <ccan/array_size/array_size.h>
28#include <shared/fs-util.h>
29
30#include "../../../src/argconfig.h"
31#include "argconfig-getopt-ref.h"
32
33const char *libnvme_strerror(int errnum);
34
35#ifdef __GLIBC__2
36
37struct diff_cfg {
38 bool_Bool flag;
39 uint64_t suffix;
40 uint32_t uint;
41 int int_val;
42 uint64_t long_val;
43 double double_val;
44 uint8_t byte;
45 uint16_t shrt;
46 int incr;
47 char *string;
48 char *str;
49 char *optstr;
50 uint32_t longonly;
51 bool_Bool dup1;
52 bool_Bool dup2;
53 bool_Bool noval;
54};
55
56struct global_cfg {
57 int verbose;
58 bool_Bool dry_run;
59 char *format;
60 uint32_t timeout;
61 bool_Bool help;
62};
63
64/* The report; stdout gets the help argconfig prints */
65static FILE *report;
66
67#define MAX_OPTS32 32
68#define MAX_ARGS8 8
69
70struct result {
71 int ret;
72 struct diff_cfg cfg;
73 struct global_cfg gcfg;
74 bool_Bool seen[MAX_OPTS32];
75 char *operands[MAX_ARGS8];
76 int nr_operands;
77 int argind;
78};
79
80static struct argconfig_opt_val byte_vals[] = {
81 VAL_BYTE("one", 1){"one", CFG_BYTE, .val.byte = 1},
82 VAL_BYTE("two", 2){"two", CFG_BYTE, .val.byte = 2},
83 VAL_BYTE("three", 3){"three", CFG_BYTE, .val.byte = 3},
84 VAL_END(){ ((void*)0) }
85};
86
87static const char * const long_names[] = {
88 "flag", "suffix", "uint", "int", "long", "double", "byte", "shrt",
89 "incr", "string", "str", "optstr", "noval", "longonly", "dup1",
90 "dup2", "help", "verbose", "dry-run", "format", "timeout",
91};
92
93static const char * const vocabulary[] = {
94 "--flag", "-f", "--flag=1",
95 "--suffix=4Ki", "--suffix", "-s", "-s1", "4Ki",
96 "--uint=5", "--uint", "-u", "-u7", "--uint=", "5",
97 "--int=-3", "-i", "-3",
98 "--long=0x10", "--double=1.5",
99 "--byte=two", "--byte=tw", "--byte=t", "--byte=300", "-b", "one",
100 "--shrt=65535", "--shrt=65536",
101 "--incr", "-I", "-II", "-fI", "-fu9", "-fu",
102 "--string=x", "--string", "-t", "-tval", "--str=y", "--str",
103 "--optstr", "--optstr=z", "-O", "-Oval",
104 "--noval", "-n", "--longonly=4", "--dup1", "--dup2", "-D",
105 "--", "-", "dev", "", "=x",
106 "--bogus", "-z", "--help", "-h", "-?", "-fh",
107 "-v", "-vv", "--verbose", "--dry-run", "-o", "--format=json",
108 "--timeout", "--timeout=10", "list",
109};
110
111static void diff_opts(struct argconfig_commandline_options *opts,
112 struct diff_cfg *cfg)
113{
114 struct argconfig_commandline_options o[] = {
115 OPT_FLAG("flag", 'f', &cfg->flag, "flag"){"flag", 'f', ((void*)0), CFG_FLAG, _Generic(&cfg->flag
, _Bool * : (&cfg->flag)), ARGCONFIG_ARG_NONE, "flag",
0, }
,
116 OPT_SUFFIX("suffix", 's', &cfg->suffix, "suffix"){"suffix", 's', "IONUM", CFG_LONG_SUFFIX, _Generic(&cfg->
suffix, uint64_t * : (&cfg->suffix), default : _Generic
(&cfg->suffix, unsigned long long * : (&cfg->suffix
), default : (void)0)), ARGCONFIG_ARG_REQUIRED, "suffix", 0, }
,
117 OPT_UINT("uint", 'u', &cfg->uint, "uint"){"uint", 'u', "NUM", CFG_POSITIVE, _Generic(&cfg->uint
, uint32_t * : (&cfg->uint)), ARGCONFIG_ARG_REQUIRED, "uint"
, 0, }
,
118 OPT_INT("int", 'i', &cfg->int_val, "int"){"int", 'i', "NUM", CFG_INT, _Generic(&cfg->int_val, int
* : (&cfg->int_val)), ARGCONFIG_ARG_REQUIRED, "int", 0
, }
,
119 OPT_LONG("long", 'l', &cfg->long_val, "long"){"long", 'l', "NUM", CFG_LONG, _Generic(&cfg->long_val
, uint64_t * : (&cfg->long_val), default : _Generic(&
cfg->long_val, unsigned long long * : (&cfg->long_val
), default : (void)0)), ARGCONFIG_ARG_REQUIRED, "long", 0, }
,
120 OPT_DOUBLE("double", 'd', &cfg->double_val, "double"){"double", 'd', "NUM", CFG_DOUBLE, _Generic(&cfg->double_val
, double * : (&cfg->double_val)), ARGCONFIG_ARG_REQUIRED
, "double", 0, }
,
121 OPT_BYTE("byte", 'b', &cfg->byte, "byte", byte_vals){"byte", 'b', "NUM", CFG_BYTE, _Generic(&cfg->byte, uint8_t
* : (&cfg->byte)), ARGCONFIG_ARG_REQUIRED, "byte", 0,
byte_vals}
,
122 OPT_GROUP("More options"){"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), ARGCONFIG_ARG_NONE
, "More options", 0, ((void*)0)}
,
123 OPT_SHRT("shrt", 'S', &cfg->shrt, "shrt"){"shrt", 'S', "NUM", CFG_SHORT, _Generic(&cfg->shrt, uint16_t
* : (&cfg->shrt)), ARGCONFIG_ARG_REQUIRED, "shrt", 0,
}
,
124 OPT_INCR("incr", 'I', &cfg->incr, "incr"){"incr", 'I', "NUM", CFG_INCREMENT, _Generic(&cfg->incr
, int * : (&cfg->incr)), ARGCONFIG_ARG_NONE, "incr", 0
, }
,
125 OPT_STRING("string", 't', "STRING", &cfg->string, "string"){"string", 't', "STRING", CFG_STRING, _Generic(&cfg->string
, char ** : (&cfg->string), const char ** : (&cfg->
string)), ARGCONFIG_ARG_REQUIRED, "string", 0, }
,
126 OPT_STR("str", 'r', &cfg->str, "str"){"str", 'r', "STRING", CFG_STRING, _Generic(&cfg->str,
char ** : (&cfg->str), const char ** : (&cfg->
str)), ARGCONFIG_ARG_REQUIRED, "str", 0, }
,
127 OPT_STRING_OPTIONAL("optstr", 'O', "STR", &cfg->optstr,{"optstr", 'O', "STR", CFG_STRING, _Generic(&cfg->optstr
, char ** : (&cfg->optstr), const char ** : (&cfg->
optstr)), ARGCONFIG_ARG_OPTIONAL, "optstr", 0, }
128 "optstr"){"optstr", 'O', "STR", CFG_STRING, _Generic(&cfg->optstr
, char ** : (&cfg->optstr), const char ** : (&cfg->
optstr)), ARGCONFIG_ARG_OPTIONAL, "optstr", 0, }
,
129 OPT_FLAG("noval", 'n', &cfg->noval, "noval"){"noval", 'n', ((void*)0), CFG_FLAG, _Generic(&cfg->noval
, _Bool * : (&cfg->noval)), ARGCONFIG_ARG_NONE, "noval"
, 0, }
,
130 OPT_UINT("longonly", 0, &cfg->longonly, "longonly"){"longonly", 0, "NUM", CFG_POSITIVE, _Generic(&cfg->longonly
, uint32_t * : (&cfg->longonly)), ARGCONFIG_ARG_REQUIRED
, "longonly", 0, }
,
131 OPT_FLAG("dup1", 'D', &cfg->dup1, "dup1"){"dup1", 'D', ((void*)0), CFG_FLAG, _Generic(&cfg->dup1
, _Bool * : (&cfg->dup1)), ARGCONFIG_ARG_NONE, "dup1",
0, }
,
132 OPT_FLAG("dup2", 'D', &cfg->dup2, "dup2"){"dup2", 'D', ((void*)0), CFG_FLAG, _Generic(&cfg->dup2
, _Bool * : (&cfg->dup2)), ARGCONFIG_ARG_NONE, "dup2",
0, }
,
133 OPT_END(){ ((void*)0) }
134 };
135
136 memcpy(opts, o, sizeof(o));
137}
138
139static void global_opts(struct argconfig_commandline_options *opts,
140 struct global_cfg *cfg)
141{
142 struct argconfig_commandline_options o[] = {
143 OPT_INCR("verbose", 'v', &cfg->verbose, "verbose"){"verbose", 'v', "NUM", CFG_INCREMENT, _Generic(&cfg->
verbose, int * : (&cfg->verbose)), ARGCONFIG_ARG_NONE,
"verbose", 0, }
,
144 OPT_FLAG("dry-run", 0, &cfg->dry_run, "dry-run"){"dry-run", 0, ((void*)0), CFG_FLAG, _Generic(&cfg->dry_run
, _Bool * : (&cfg->dry_run)), ARGCONFIG_ARG_NONE, "dry-run"
, 0, }
,
145 OPT_FMT("format", 'o', &cfg->format, "format"){"format", 'o', "FMT", CFG_STRING, _Generic(&cfg->format
, char ** : (&cfg->format), const char ** : (&cfg->
format)), ARGCONFIG_ARG_REQUIRED, "format", 0, }
,
146 OPT_UINT("timeout", 0, &cfg->timeout, "timeout"){"timeout", 0, "NUM", CFG_POSITIVE, _Generic(&cfg->timeout
, uint32_t * : (&cfg->timeout)), ARGCONFIG_ARG_REQUIRED
, "timeout", 0, }
,
147 OPT_FLAG("help", 'h', &cfg->help, "help"){"help", 'h', ((void*)0), CFG_FLAG, _Generic(&cfg->help
, _Bool * : (&cfg->help)), ARGCONFIG_ARG_NONE, "help",
0, }
,
148 OPT_END(){ ((void*)0) }
149 };
150
151 memcpy(opts, o, sizeof(o));
152}
153
154/* The short options of diff_opts() and the implicit help options */
155static const char command_shorts[] = "fsuildbSItrnODh?";
156
157/*
158 * True if getopt would treat the argument as a single-dash long option or
159 * as an abbreviated long option, which are not supported anymore. For the
160 * command options getopt_long_only() also took "-x" for an abbreviated
161 * long option if x is not a short option.
162 */
163static bool_Bool unsupported(const char *arg, bool_Bool global)
164{
165 const char *name, *eq;
166 size_t len;
167 unsigned int i;
168
169 if (arg[0] != '-' || !arg[1])
170 return false0;
171 if (arg[1] == '-') {
172 name = arg + 2;
173 if (!*name)
174 return false0;
175 } else {
176 name = arg + 1;
177 if (global)
178 return false0;
179 if (!name[1] && strchr(command_shorts, name[0])_Generic (0 ? (command_shorts) : (void *) 1, const void *: (const
char *) (strchr (command_shorts, name[0])), default: strchr (
command_shorts, name[0]))
)
180 return false0;
181 }
182
183 eq = strchr(name, '=')_Generic (0 ? (name) : (void *) 1, const void *: (const char *
) (strchr (name, '=')), default: strchr (name, '='))
;
184 len = eq ? (size_t)(eq - name) : strlen(name);
185
186 /* "--name" is fine even if a longer name starts with it */
187 if (arg[1] == '-') {
188 for (i = 0; i < ARRAY_SIZE(long_names)(sizeof(long_names) / sizeof((long_names)[0]) + 0); i++) {
189 if (strlen(long_names[i]) == len &&
190 !strncmp(long_names[i], name, len))
191 return false0;
192 }
193 }
194
195 /* "-name", "-nam" and "--nam" are not */
196 for (i = 0; i < ARRAY_SIZE(long_names)(sizeof(long_names) / sizeof((long_names)[0]) + 0); i++) {
197 if (!strncmp(long_names[i], name, len))
198 return true1;
199 }
200
201 return false0;
202}
203
204/* The filter must not skip the supported forms of the comparison */
205static bool_Bool filter_test(void)
206{
207 static const struct {
208 const char *arg;
209 bool_Bool global;
210 bool_Bool unsupported;
211 } tests[] = {
212 /* exact names, also when a longer name starts with them */
213 { "--str", false0, false0 },
214 { "--str=y", false0, false0 },
215 { "--long=0x10", false0, false0 },
216 { "--string=x", false0, false0 },
217 { "-f", false0, false0 },
218 { "-fu9", false0, false0 },
219 { "dev", false0, false0 },
220 { "-verbose", true1, false0 },
221 /* abbreviations and single-dash long options */
222 { "--stri", false0, true1 },
223 { "--lon", false0, true1 },
224 { "--verb", true1, true1 },
225 { "-str", false0, true1 },
226 { "-o", false0, true1 },
227 { "-flag", false0, true1 },
228 };
229 bool_Bool ok = true1;
230 unsigned int i;
231
232 for (i = 0; i < ARRAY_SIZE(tests)(sizeof(tests) / sizeof((tests)[0]) + 0); i++) {
233 if (unsupported(tests[i].arg, tests[i].global) ==
234 tests[i].unsupported)
235 continue;
236 fprintf(report, "ERROR: filter: '%s' %s taken as unsupported\n",
237 tests[i].arg, tests[i].unsupported ? "not" : "wrongly");
238 ok = false0;
239 }
240
241 return ok;
242}
243
244static void collect_seen(struct argconfig_commandline_options *opts,
245 bool_Bool *seen)
246{
247 int i;
248
249 for (i = 0; opts[i].option && i < MAX_OPTS32; i++)
250 seen[i] = opts[i].seen;
251}
252
253static void run(char **args, int nargs, bool_Bool ref, bool_Bool global,
254 struct result *r)
255{
256 struct argconfig_commandline_options opts[MAX_OPTS32];
257 char *argv[MAX_ARGS8 + 2];
258 char **operands;
259 int i;
260
261 memset(r, 0, sizeof(*r));
262 argv[0] = "test-argconfig-diff";
263 for (i = 0; i < nargs; i++)
264 argv[i + 1] = args[i];
265 argv[nargs + 1] = NULL((void*)0);
266
267 if (global) {
268 global_opts(opts, &r->gcfg);
269 if (ref)
270 r->ret = ref_argconfig_parse_global(nargs + 1, argv,
271 opts, &r->argind);
272 else
273 r->ret = argconfig_parse_global(nargs + 1, argv, opts,
274 &r->argind);
275 if (r->ret)
276 r->argind = 0;
277 collect_seen(opts, r->seen);
278 return;
279 }
280
281 diff_opts(opts, &r->cfg);
282 if (ref) {
283 r->ret = ref_argconfig_parse(nargs + 1, argv, opts,
284 &operands, &r->nr_operands);
285 for (i = 0; !r->ret && i < r->nr_operands; i++)
286 r->operands[i] = operands[i];
287 } else {
288 r->ret = argconfig_parse(nargs + 1, argv, "diff", opts);
289 /* the getopt parser reported the help as an error */
290 if (r->ret == ARGCONFIG_HELP1)
291 r->ret = -EINVAL22;
292 r->nr_operands = argconfig_nr_operands();
293 for (i = 0; !r->ret && i < r->nr_operands; i++)
294 r->operands[i] = argconfig_operand(i);
295 }
296 if (r->ret)
297 r->nr_operands = 0;
298 collect_seen(opts, r->seen);
299}
300
301static unsigned long nr_cases, nr_skipped;
302
303static bool_Bool compare(char **args, int nargs, bool_Bool global)
304{
305 struct result ref, cur;
306 int i;
307
308 for (i = 0; i < nargs; i++) {
309 if (unsupported(args[i], global)) {
310 nr_skipped++;
311 return true1;
312 }
313 }
314
315 nr_cases++;
316 run(args, nargs, true1, global, &ref);
317 run(args, nargs, false0, global, &cur);
318
319 if (!memcmp(&ref, &cur, sizeof(ref)))
320 return true1;
321
322 fprintf(report, "ERROR: %s parse differs for:",
323 global ? "global" : "command");
324 for (i = 0; i < nargs; i++)
325 fprintf(report, " '%s'", args[i]);
326 fprintf(report, "\n ret %d/%d argind %d/%d operands %d/%d\n",
327 ref.ret, cur.ret, ref.argind, cur.argind,
328 ref.nr_operands, cur.nr_operands);
329
330 return false0;
331}
332
333/*
334 * Time budgets in seconds. The tests run in an emulator for cross builds
335 * and under valgrind, so the larger parts are limited by time, not by a
336 * number of cases.
337 */
338#define BUDGET_THREE_WORDS2.0 2.0
339#define BUDGET_RANDOM1.0 1.0
340
341static double now(void)
342{
343 struct timespec ts;
344
345 clock_gettime(CLOCK_MONOTONIC1, &ts);
346 return ts.tv_sec + ts.tv_nsec / 1e9;
347}
348
349static bool_Bool expired(double end, unsigned long i)
350{
351 /* reading the clock for every case would dominate the runtime */
352 return !(i % 256) && now() > end;
353}
354
355/* All sequences of up to two words from the vocabulary */
356static bool_Bool two_words(bool_Bool global)
357{
358 unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0), i, j;
359 char *args[2];
360 bool_Bool ok = compare(args, 0, global);
361
362 for (i = 0; i < n; i++) {
363 args[0] = (char *)vocabulary[i];
364 ok &= compare(args, 1, global);
365 for (j = 0; j < n; j++) {
366 args[1] = (char *)vocabulary[j];
367 ok &= compare(args, 2, global);
368 }
369 }
370
371 return ok;
372}
373
374/* Sequences of three words, in a fixed order until the time is up */
375static bool_Bool three_words(bool_Bool global, unsigned long *count)
376{
377 unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0), i;
378 double end = now() + BUDGET_THREE_WORDS2.0;
379 char *args[3];
380 bool_Bool ok = true1;
381
382 for (i = 0; i < n * n * n && !expired(end, i); i++) {
383 args[0] = (char *)vocabulary[i / (n * n)];
384 args[1] = (char *)vocabulary[i / n % n];
385 args[2] = (char *)vocabulary[i % n];
386 ok &= compare(args, 3, global);
387 }
388 *count += i;
389
390 return ok;
391}
392
393/*
394 * Longer sequences until the time is up. The seed is fixed and a failure
395 * prints its arguments, so it can be reproduced.
396 */
397static bool_Bool random_cases(bool_Bool global, unsigned long *count)
398{
399 unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0);
400 double end = now() + BUDGET_RANDOM1.0;
401 unsigned int seed = 0x6e766d65;
402 char *args[MAX_ARGS8];
403 unsigned long i;
404 bool_Bool ok = true1;
405 int nargs, j;
406
407 for (i = 0; !expired(end, i); i++) {
408 seed = seed * 1103515245 + 12345;
409 nargs = 4 + (seed >> 16) % (MAX_ARGS8 - 3);
410 for (j = 0; j < nargs; j++) {
411 seed = seed * 1103515245 + 12345;
412 args[j] = (char *)vocabulary[(seed >> 16) % n];
413 }
414 ok &= compare(args, nargs, global);
415 }
416 *count += i;
417
418 return ok;
419}
420
421int main(void)
422{
423 unsigned int n = ARRAY_SIZE(vocabulary)(sizeof(vocabulary) / sizeof((vocabulary)[0]) + 0);
424 unsigned long nr_three = 0, nr_random = 0;
425 bool_Bool ok = true1;
426 FILE *f;
427
428 setlocale(LC_NUMERIC1, "C");
429 unsetenv("POSIXLY_CORRECT");
430 report = fdopen(dup(STDOUT_FILENO1), "w");
1
Assuming that 'dup' fails
2
The 1st argument to 'fdopen' is < 0 but should be >= 0
431 if (!report || !freopen(shr_dev_null(), "w", stdoutstdout)) {
432 printf("ERROR: redirecting stdout failed: %s\n",
433 libnvme_strerror(errno(*__errno_location ())));
434 return EXIT_FAILURE1;
435 }
436 f = freopen(shr_dev_null(), "w", stderrstderr);
437 if (!f) {
438 fprintf(report, "ERROR: reopening stderr failed: %s\n",
439 libnvme_strerror(errno(*__errno_location ())));
440 return EXIT_FAILURE1;
441 }
442
443 ok &= filter_test();
444 ok &= two_words(false0);
445 ok &= two_words(true1);
446 ok &= three_words(false0, &nr_three);
447 ok &= three_words(true1, &nr_three);
448 ok &= random_cases(false0, &nr_random);
449 ok &= random_cases(true1, &nr_random);
450
451 fprintf(report, "%lu cases compared, %lu skipped\n", nr_cases,
452 nr_skipped);
453 fprintf(report, "three words: %lu of %u, random: %lu\n",
454 nr_three, 2 * n * n * n, nr_random);
455 fclose(f);
456 fclose(report);
457
458 return ok ? EXIT_SUCCESS0 : EXIT_FAILURE1;
459}
460
461#else /* __GLIBC__ */
462
463int main(void)
464{
465 return 77;
466}
467
468#endif /* __GLIBC__ */