(root)/
tar-1.35/
gnu/
savedir.c
       1  /* savedir.c -- save the list of files in a directory in a string
       2  
       3     Copyright (C) 1990, 1997-2001, 2003-2006, 2009-2023 Free Software
       4     Foundation, Inc.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation, either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
      20  
      21  #include <config.h>
      22  
      23  #include "savedir.h"
      24  
      25  #include <sys/types.h>
      26  
      27  #include <errno.h>
      28  
      29  #include "dirent--.h"
      30  #ifndef _D_EXACT_NAMLEN
      31  # define _D_EXACT_NAMLEN(dp)    strlen ((dp)->d_name)
      32  #endif
      33  
      34  #include <stddef.h>
      35  #include <stdlib.h>
      36  #include <string.h>
      37  
      38  #include "xalloc.h"
      39  
      40  typedef struct
      41  {
      42    char *name;
      43  #if D_INO_IN_DIRENT
      44    ino_t ino;
      45  #endif
      46  } direntry_t;
      47  
      48  /* Compare the names of two directory entries */
      49  
      50  static int
      51  direntry_cmp_name (void const *a, void const *b)
      52  {
      53    direntry_t const *dea = a;
      54    direntry_t const *deb = b;
      55  
      56    return strcmp (dea->name, deb->name);
      57  }
      58  
      59  #if D_INO_IN_DIRENT
      60  /* Compare the inode numbers of two directory entries */
      61  
      62  static int
      63  direntry_cmp_inode (void const *a, void const *b)
      64  {
      65    direntry_t const *dea = a;
      66    direntry_t const *deb = b;
      67  
      68    return _GL_CMP (dea->ino, deb->ino);
      69  }
      70  #endif
      71  
      72  typedef int (*comparison_function) (void const *, void const *);
      73  
      74  static comparison_function const comparison_function_table[] =
      75    {
      76      0,
      77      direntry_cmp_name
      78  #if D_INO_IN_DIRENT
      79      , direntry_cmp_inode
      80  #endif
      81    };
      82  
      83  /* Return a freshly allocated string containing the file names
      84     in directory DIRP, separated by '\0' characters;
      85     the end is marked by two '\0' characters in a row.
      86     Returned values are sorted according to OPTION.
      87     Return NULL (setting errno) if DIRP cannot be read.
      88     If DIRP is NULL, return NULL without affecting errno.  */
      89  
      90  char *
      91  streamsavedir (DIR *dirp, enum savedir_option option)
      92  {
      93    char *name_space = NULL;
      94    idx_t allocated = 0;
      95    direntry_t *entries = NULL;
      96    idx_t entries_allocated = 0;
      97    idx_t entries_used = 0;
      98    idx_t used = 0;
      99    comparison_function cmp = comparison_function_table[option];
     100  
     101    if (dirp == NULL)
     102      return NULL;
     103  
     104    for (;;)
     105      {
     106        struct dirent const *dp;
     107        char const *entry;
     108  
     109        errno = 0;
     110        dp = readdir (dirp);
     111        if (! dp)
     112          break;
     113  
     114        /* Skip "", ".", and "..".  "" is returned by at least one buggy
     115           implementation: Solaris 2.4 readdir on NFS file systems.  */
     116        entry = dp->d_name;
     117        if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
     118          {
     119            idx_t entry_size = _D_EXACT_NAMLEN (dp) + 1;
     120            if (cmp)
     121              {
     122                if (entries_allocated == entries_used)
     123                  entries = xpalloc (entries, &entries_allocated, 1, -1,
     124                                     sizeof *entries);
     125                entries[entries_used].name = xstrdup (entry);
     126  #if D_INO_IN_DIRENT
     127                entries[entries_used].ino = dp->d_ino;
     128  #endif
     129                entries_used++;
     130              }
     131            else
     132              {
     133                if (allocated - used <= entry_size)
     134                  name_space = xpalloc (name_space, &allocated,
     135                                        entry_size - (allocated - used),
     136                                        IDX_MAX - 1, sizeof *name_space);
     137                memcpy (name_space + used, entry, entry_size);
     138              }
     139            used += entry_size;
     140          }
     141      }
     142  
     143    if (errno != 0)
     144      {
     145        free (entries);
     146        free (name_space);
     147        return NULL;
     148      }
     149  
     150    if (cmp)
     151      {
     152        if (entries_used)
     153          qsort (entries, entries_used, sizeof *entries, cmp);
     154        name_space = ximalloc (used + 1);
     155        used = 0;
     156        for (idx_t i = 0; i < entries_used; i++)
     157          {
     158            char *dest = name_space + used;
     159            used += stpcpy (dest, entries[i].name) - dest + 1;
     160            free (entries[i].name);
     161          }
     162        free (entries);
     163      }
     164    else if (used == allocated)
     165      name_space = xirealloc (name_space, used + 1);
     166  
     167    name_space[used] = '\0';
     168    return name_space;
     169  }
     170  
     171  /* Return a freshly allocated string containing the file names
     172     in directory DIR, separated by '\0' characters;
     173     the end is marked by two '\0' characters in a row.
     174     Return NULL (setting errno) if DIR cannot be opened, read, or closed.  */
     175  
     176  char *
     177  savedir (char const *dir, enum savedir_option option)
     178  {
     179    DIR *dirp = opendir (dir);
     180    if (! dirp)
     181      return NULL;
     182    else
     183      {
     184        char *name_space = streamsavedir (dirp, option);
     185        if (closedir (dirp) != 0)
     186          {
     187            free (name_space);
     188            return NULL;
     189          }
     190        return name_space;
     191      }
     192  }