1 /* Simple test for some fts functions.
2 Copyright (C) 2015-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fts.h>
22
23 #include <errno.h>
24 #include <error.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29
30 static void prepare (void);
31 static int do_test (void);
32 #define PREPARE(argc, argv) prepare ()
33 #define TEST_FUNCTION do_test ()
34 #include "../test-skeleton.c"
35
36 static char *fts_test_dir;
37
38 static void
make_dir(const char * dirname)39 make_dir (const char *dirname)
40 {
41 char *name;
42 if (asprintf (&name, "%s/%s", fts_test_dir, dirname) < 0)
43 {
44 puts ("out of memory");
45 exit (1);
46 }
47
48 if (mkdir (name, 0700) < 0)
49 {
50 printf ("cannot create dir \"%s\": %m\n", name);
51 exit (1);
52 }
53
54 add_temp_file (name);
55 }
56
57 static void
make_file(const char * filename)58 make_file (const char *filename)
59 {
60 char *name;
61 if (asprintf (&name, "%s/%s", fts_test_dir, filename) < 0)
62 {
63 puts ("out of memory");
64 exit (1);
65 }
66
67 int fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0600);
68 if (fd < 0)
69 {
70 printf ("cannot create file \"%s\": %m\n", name);
71 exit (1);
72 }
73 close (fd);
74
75 add_temp_file (name);
76 }
77
78 static void
prepare(void)79 prepare (void)
80 {
81 char *dirbuf;
82 char dir_name[] = "/tst-fts.XXXXXX";
83
84 if (asprintf (&dirbuf, "%s%s", test_dir, dir_name) < 0)
85 {
86 puts ("out of memory");
87 exit (1);
88 }
89
90 if (mkdtemp (dirbuf) == NULL)
91 {
92 puts ("cannot create temporary directory");
93 exit (1);
94 }
95
96 add_temp_file (dirbuf);
97 fts_test_dir = dirbuf;
98
99 make_file ("12");
100 make_file ("345");
101 make_file ("6789");
102
103 make_dir ("aaa");
104 make_file ("aaa/1234");
105 make_file ("aaa/5678");
106
107 make_dir ("bbb");
108 make_file ("bbb/1234");
109 make_file ("bbb/5678");
110 make_file ("bbb/90ab");
111 }
112
113 /* Largest name wins, otherwise strcmp. */
114 static int
compare_ents(const FTSENT ** ent1,const FTSENT ** ent2)115 compare_ents (const FTSENT **ent1, const FTSENT **ent2)
116 {
117 short len1 = (*ent1)->fts_namelen;
118 short len2 = (*ent2)->fts_namelen;
119 if (len1 != len2)
120 return len1 - len2;
121 else
122 {
123 const char *name1 = (*ent1)->fts_name;
124 const char *name2 = (*ent2)->fts_name;
125 return strcmp (name1, name2);
126 }
127 }
128
129 /* Count the number of files seen as children. */
130 static int files = 0;
131
132 static void
children(FTS * fts)133 children (FTS *fts)
134 {
135 FTSENT *child = fts_children (fts, 0);
136 if (child == NULL && errno != 0)
137 {
138 printf ("FAIL: fts_children: %m\n");
139 exit (1);
140 }
141
142 while (child != NULL)
143 {
144 short level = child->fts_level;
145 const char *name = child->fts_name;
146 if (child->fts_info == FTS_F || child->fts_info == FTS_NSOK)
147 {
148 files++;
149 printf ("%*s%s\n", 2 * level, "", name);
150 }
151 child = child->fts_link;
152 }
153 }
154
155 /* Count the number of dirs seen in the test. */
156 static int dirs = 0;
157
158 static int
do_test(void)159 do_test (void)
160 {
161 char *paths[2] = { fts_test_dir, NULL };
162 FTS *fts;
163 fts = fts_open (paths, FTS_LOGICAL, &compare_ents);
164 if (fts == NULL)
165 {
166 printf ("FAIL: fts_open: %m\n");
167 exit (1);
168 }
169
170 FTSENT *ent;
171 while ((ent = fts_read (fts)) != NULL)
172 {
173 const char *name = ent->fts_name;
174 short level = ent->fts_level;
175 switch (ent->fts_info)
176 {
177 case FTS_F:
178 /* Don't show anything, children will have on parent dir. */
179 break;
180
181 case FTS_D:
182 printf ("%*s%s =>\n", 2 * level, "", name);
183 children (fts);
184 break;
185
186 case FTS_DP:
187 dirs++;
188 printf ("%*s<= %s\n", 2 * level, "", name);
189 break;
190
191 case FTS_NS:
192 case FTS_ERR:
193 printf ("FAIL: fts_read ent: %s\n", strerror (ent->fts_errno));
194 exit (1);
195 break;
196
197 default:
198 printf ("FAIL: unexpected fts_read ent %s\n", name);
199 exit (1);
200 break;
201 }
202 }
203 /* fts_read returns NULL when done (and clears errno)
204 or when an error occured (with errno set). */
205 if (errno != 0)
206 {
207 printf ("FAIL: fts_read: %m\n");
208 exit (1);
209 }
210
211 if (fts_close (fts) != 0)
212 {
213 printf ("FAIL: fts_close: %m\n");
214 exit (1);
215 }
216
217 if (files != 8)
218 {
219 printf ("FAIL: Unexpected number of files: %d\n", files);
220 return 1;
221 }
222
223 if (dirs != 3)
224 {
225 printf ("FAIL: Unexpected number of dirs: %d\n", dirs);
226 return 1;
227 }
228
229 puts ("PASS");
230 return 0;
231 }
232