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