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 |
39 | make_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 | |
57 | static void |
58 | make_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 | |
78 | static void |
79 | prepare (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. */ |
114 | static int |
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 |
133 | children (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. */ |
156 | static int dirs = 0; |
157 | |
158 | static int |
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 (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 | |