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
30static void prepare (void);
31static int do_test (void);
32#define PREPARE(argc, argv) prepare ()
33#define TEST_FUNCTION do_test ()
34#include "../test-skeleton.c"
35
36static char *fts_test_dir;
37
38static void
39make_dir (const char *dirname)
40{
41 char *name;
42 if (asprintf (ptr: &name, fmt: "%s/%s", fts_test_dir, dirname) < 0)
43 {
44 puts (s: "out of memory");
45 exit (1);
46 }
47
48 if (mkdir (path: name, mode: 0700) < 0)
49 {
50 printf (format: "cannot create dir \"%s\": %m\n", name);
51 exit (1);
52 }
53
54 add_temp_file (name);
55}
56
57static void
58make_file (const char *filename)
59{
60 char *name;
61 if (asprintf (ptr: &name, fmt: "%s/%s", fts_test_dir, filename) < 0)
62 {
63 puts (s: "out of memory");
64 exit (1);
65 }
66
67 int fd = open (file: name, O_WRONLY | O_CREAT | O_EXCL, 0600);
68 if (fd < 0)
69 {
70 printf (format: "cannot create file \"%s\": %m\n", name);
71 exit (1);
72 }
73 close (fd: fd);
74
75 add_temp_file (name);
76}
77
78static void
79prepare (void)
80{
81 char *dirbuf;
82 char dir_name[] = "/tst-fts.XXXXXX";
83
84 if (asprintf (ptr: &dirbuf, fmt: "%s%s", test_dir, dir_name) < 0)
85 {
86 puts (s: "out of memory");
87 exit (1);
88 }
89
90 if (mkdtemp (template: dirbuf) == NULL)
91 {
92 puts (s: "cannot create temporary directory");
93 exit (1);
94 }
95
96 add_temp_file (name: dirbuf);
97 fts_test_dir = dirbuf;
98
99 make_file (filename: "12");
100 make_file (filename: "345");
101 make_file (filename: "6789");
102
103 make_dir (dirname: "aaa");
104 make_file (filename: "aaa/1234");
105 make_file (filename: "aaa/5678");
106
107 make_dir (dirname: "bbb");
108 make_file (filename: "bbb/1234");
109 make_file (filename: "bbb/5678");
110 make_file (filename: "bbb/90ab");
111}
112
113/* Largest name wins, otherwise strcmp. */
114static int
115compare_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. */
130static int files = 0;
131
132static void
133children (FTS *fts)
134{
135 FTSENT *child = fts_children (fts, 0);
136 if (child == NULL && errno != 0)
137 {
138 printf (format: "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 (format: "%*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. */
156static int dirs = 0;
157
158static int
159do_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 (format: "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 (format: "%*s%s =>\n", 2 * level, "", name);
183 children (fts);
184 break;
185
186 case FTS_DP:
187 dirs++;
188 printf (format: "%*s<= %s\n", 2 * level, "", name);
189 break;
190
191 case FTS_NS:
192 case FTS_ERR:
193 printf (format: "FAIL: fts_read ent: %s\n", strerror (errnum: ent->fts_errno));
194 exit (1);
195 break;
196
197 default:
198 printf (format: "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 (format: "FAIL: fts_read: %m\n");
208 exit (1);
209 }
210
211 if (fts_close (fts) != 0)
212 {
213 printf (format: "FAIL: fts_close: %m\n");
214 exit (1);
215 }
216
217 if (files != 8)
218 {
219 printf (format: "FAIL: Unexpected number of files: %d\n", files);
220 return 1;
221 }
222
223 if (dirs != 3)
224 {
225 printf (format: "FAIL: Unexpected number of dirs: %d\n", dirs);
226 return 1;
227 }
228
229 puts (s: "PASS");
230 return 0;
231}
232

source code of glibc/io/tst-fts.c