text: /* Simple test for some fts functions.
     Copyright (C) 2015-2016 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;
  }
  