attribute: const/pure defaults to unsequenced/reproducible
[gnulib.git] / tests / test-fts.c
blobf5ab8355e116cf57caa9884c8d2d1dc26abaeb66
1 /* Test the fts function.
2 Copyright 2017-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 #include <config.h>
19 #include <fts_.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
29 #define BASE "t-fts.tmp"
30 static char base[] = BASE; /* Not const, since argv needs non-const. */
31 static char const base_d[] = BASE "/d";
32 static char *const argv[2] = { base, 0 };
34 static void
35 perror_exit (char const *message, int status)
37 perror (message);
38 exit (status);
41 /* alloc/dealloc to ensure structures initialized appropriately. */
42 static void
43 fts_dealloc (void)
45 static char dir[] = "./";
46 static char *const curr_dir[2] = { dir, 0 };
47 FTS *ftsp = fts_open (curr_dir, FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0);
48 if (ftsp)
50 if (fts_close (ftsp) != 0)
51 perror_exit ("fts_close", 9);
53 else
54 perror_exit (base, 10);
57 /* Remove BASE and all files under it. */
58 static void
59 remove_tree (void)
61 FTSENT *e;
62 FTS *ftsp = fts_open (argv, FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0);
63 if (ftsp)
65 while ((e = fts_read (ftsp)))
67 int status = 0;
68 switch (e->fts_info)
70 case FTS_DP:
71 status = unlinkat (ftsp->fts_cwd_fd, e->fts_accpath,
72 AT_REMOVEDIR);
73 break;
75 case FTS_F: case FTS_NSOK:
76 status = unlinkat (ftsp->fts_cwd_fd, e->fts_accpath, 0);
77 break;
79 if (status != 0)
80 perror_exit (e->fts_path, 1);
82 if (fts_close (ftsp) != 0)
83 perror_exit ("fts_close", 2);
85 else if (errno != ENOENT)
86 perror_exit (base, 3);
89 int
90 main (void)
92 FTS *ftsp;
93 FTSENT *e;
94 char buf[sizeof BASE + 100];
95 int i;
96 enum { needles = 4 };
97 int needles_seen = 0;
98 struct stat st;
100 remove_tree ();
102 /* Create directories BASE, BASE/d, BASE/d/1, BASE/d/2, ..., BASE/d/65536,
103 to stress-test fts. Stop if directory creation fails due to
104 EMFILE or EMLINK problems, or if BASE/d's link count no longer matches the
105 Unix tradition. See:
106 https://bugzilla.kernel.org/show_bug.cgi?id=196405
107 for more info. */
108 if (mkdir (BASE, 0777) != 0)
109 perror_exit (base, 4);
110 if (mkdir (base_d, 0777) != 0)
111 perror_exit (base_d, 5);
112 for (i = 1; i <= 65536; i++)
114 sprintf (buf, "%s/d/%i", base, i);
115 if (mkdir (buf, 0777) != 0)
117 if (errno == EMFILE || errno == EMLINK)
118 break;
119 if (i <= needles)
120 perror_exit (buf, 77);
121 break;
123 if (needles < i && stat (base_d, &st) == 0 && st.st_nlink != i + 2)
124 break;
127 /* Create empty files BASE/d/1/needle etc. */
128 for (i = 1; i <= needles; i++)
130 int fd;
131 sprintf (buf, "%s/d/%d/needle", base, i);
132 fd = open (buf, O_WRONLY | O_CREAT, 0666);
133 if (fd < 0 || close (fd) != 0)
134 perror_exit (buf, 77);
137 /* Use fts to look for the needles. */
138 ftsp = fts_open (argv, FTS_SEEDOT | FTS_NOSTAT | FTS_PHYSICAL | FTS_CWDFD, 0);
139 if (!ftsp)
140 perror_exit (base, 6);
141 while ((e = fts_read (ftsp)))
142 needles_seen += strcmp (e->fts_name, "needle") == 0;
143 int fts_read_errno = errno;
144 fflush (stdout);
145 if (fts_read_errno)
147 errno = fts_read_errno;
148 perror_exit ("fts_read", 7);
150 if (fts_close (ftsp) != 0)
151 perror_exit (base, 8);
153 /* Report an error if we did not find the needles. */
154 if (needles_seen != needles)
156 fprintf (stderr, "%d needles found (should be %d)\n",
157 needles_seen, needles);
158 return 1;
161 remove_tree ();
162 if (stat (base, &st) == 0)
164 fprintf (stderr, "fts could not remove directory\n");
165 return 1;
168 fts_dealloc ();
170 return 0;