| /* Simple test for some fts functions. |
| Copyright (C) 2015-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fts.h> |
| |
| #include <errno.h> |
| #include <error.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| static void prepare (void); |
| static int do_test (void); |
| #define PREPARE(argc, argv) prepare () |
| #define TEST_FUNCTION do_test () |
| #include "../test-skeleton.c" |
| |
| static char *fts_test_dir; |
| |
| static void |
| make_dir (const char *dirname) |
| { |
| char *name; |
| if (asprintf (&name, "%s/%s", fts_test_dir, dirname) < 0) |
| { |
| puts ("out of memory"); |
| exit (1); |
| } |
| |
| if (mkdir (name, 0700) < 0) |
| { |
| printf ("cannot create dir \"%s\": %m\n", name); |
| exit (1); |
| } |
| |
| add_temp_file (name); |
| } |
| |
| static void |
| make_file (const char *filename) |
| { |
| char *name; |
| if (asprintf (&name, "%s/%s", fts_test_dir, filename) < 0) |
| { |
| puts ("out of memory"); |
| exit (1); |
| } |
| |
| int fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0600); |
| if (fd < 0) |
| { |
| printf ("cannot create file \"%s\": %m\n", name); |
| exit (1); |
| } |
| close (fd); |
| |
| add_temp_file (name); |
| } |
| |
| static void |
| prepare (void) |
| { |
| char *dirbuf; |
| char dir_name[] = "/tst-fts.XXXXXX"; |
| |
| if (asprintf (&dirbuf, "%s%s", test_dir, dir_name) < 0) |
| { |
| puts ("out of memory"); |
| exit (1); |
| } |
| |
| if (mkdtemp (dirbuf) == NULL) |
| { |
| puts ("cannot create temporary directory"); |
| exit (1); |
| } |
| |
| add_temp_file (dirbuf); |
| fts_test_dir = dirbuf; |
| |
| make_file ("12"); |
| make_file ("345"); |
| make_file ("6789"); |
| |
| make_dir ("aaa"); |
| make_file ("aaa/1234"); |
| make_file ("aaa/5678"); |
| |
| make_dir ("bbb"); |
| make_file ("bbb/1234"); |
| make_file ("bbb/5678"); |
| make_file ("bbb/90ab"); |
| } |
| |
| /* Largest name wins, otherwise strcmp. */ |
| static int |
| compare_ents (const FTSENT **ent1, const FTSENT **ent2) |
| { |
| short len1 = (*ent1)->fts_namelen; |
| short len2 = (*ent2)->fts_namelen; |
| if (len1 != len2) |
| return len1 - len2; |
| else |
| { |
| const char *name1 = (*ent1)->fts_name; |
| const char *name2 = (*ent2)->fts_name; |
| return strcmp (name1, name2); |
| } |
| } |
| |
| /* Count the number of files seen as children. */ |
| static int files = 0; |
| |
| static void |
| children (FTS *fts) |
| { |
| FTSENT *child = fts_children (fts, 0); |
| if (child == NULL && errno != 0) |
| { |
| printf ("FAIL: fts_children: %m\n"); |
| exit (1); |
| } |
| |
| while (child != NULL) |
| { |
| short level = child->fts_level; |
| const char *name = child->fts_name; |
| if (child->fts_info == FTS_F || child->fts_info == FTS_NSOK) |
| { |
| files++; |
| printf ("%*s%s\n", 2 * level, "", name); |
| } |
| child = child->fts_link; |
| } |
| } |
| |
| /* Count the number of dirs seen in the test. */ |
| static int dirs = 0; |
| |
| static int |
| do_test (void) |
| { |
| char *paths[2] = { fts_test_dir, NULL }; |
| FTS *fts; |
| fts = fts_open (paths, FTS_LOGICAL, &compare_ents); |
| if (fts == NULL) |
| { |
| printf ("FAIL: fts_open: %m\n"); |
| exit (1); |
| } |
| |
| FTSENT *ent; |
| while ((ent = fts_read (fts)) != NULL) |
| { |
| const char *name = ent->fts_name; |
| short level = ent->fts_level; |
| switch (ent->fts_info) |
| { |
| case FTS_F: |
| /* Don't show anything, children will have on parent dir. */ |
| break; |
| |
| case FTS_D: |
| printf ("%*s%s =>\n", 2 * level, "", name); |
| children (fts); |
| break; |
| |
| case FTS_DP: |
| dirs++; |
| printf ("%*s<= %s\n", 2 * level, "", name); |
| break; |
| |
| case FTS_NS: |
| case FTS_ERR: |
| printf ("FAIL: fts_read ent: %s\n", strerror (ent->fts_errno)); |
| exit (1); |
| break; |
| |
| default: |
| printf ("FAIL: unexpected fts_read ent %s\n", name); |
| exit (1); |
| break; |
| } |
| } |
| /* fts_read returns NULL when done (and clears errno) |
| or when an error occured (with errno set). */ |
| if (errno != 0) |
| { |
| printf ("FAIL: fts_read: %m\n"); |
| exit (1); |
| } |
| |
| if (fts_close (fts) != 0) |
| { |
| printf ("FAIL: fts_close: %m\n"); |
| exit (1); |
| } |
| |
| if (files != 8) |
| { |
| printf ("FAIL: Unexpected number of files: %d\n", files); |
| return 1; |
| } |
| |
| if (dirs != 3) |
| { |
| printf ("FAIL: Unexpected number of dirs: %d\n", dirs); |
| return 1; |
| } |
| |
| puts ("PASS"); |
| return 0; |
| } |