1 /* savedir.c -- save the list of files in a directory in a string
3 Copyright (C) 1990, 1997-2001, 2003-2006, 2009-2025 Free Software
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.
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.
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/>. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
25 #include <sys/types.h>
29 #if GNULIB_DIRENT_SAFER
30 # include "dirent--.h"
33 #ifndef _D_EXACT_NAMLEN
34 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
41 #include "attribute.h"
44 /* Pacify GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113963>. */
45 #if _GL_GNUC_PREREQ (13, 0)
46 # pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
47 # pragma GCC diagnostic ignored "-Wanalyzer-null-dereference"
52 /* Offset of file name in name_space. */
56 /* File inode number. */
61 /* Compare the names of two directory entries */
64 direntry_cmp_name (void const *a
, void const *b
, void *arg
)
66 direntry_t
const *dea
= a
;
67 direntry_t
const *deb
= b
;
68 char const *name_space
= arg
;
70 return strcmp (name_space
+ dea
->name
, name_space
+ deb
->name
);
74 /* Compare the inode numbers of two directory entries */
77 direntry_cmp_inode (void const *a
, void const *b
, MAYBE_UNUSED
void *arg
)
79 direntry_t
const *dea
= a
;
80 direntry_t
const *deb
= b
;
82 return _GL_CMP (dea
->ino
, deb
->ino
);
86 typedef int (*comparison_function
) (void const *, void const *, void *);
88 static comparison_function
const comparison_function_table
[] =
97 /* Return a freshly allocated string containing the file names
98 in directory DIRP, separated by '\0' characters;
99 the end is marked by two '\0' characters in a row.
100 Returned values are sorted according to OPTION.
101 Return NULL (setting errno) if DIRP cannot be read.
102 If DIRP is NULL, return NULL without affecting errno. */
105 streamsavedir (DIR *dirp
, enum savedir_option option
)
107 char *name_space
= NULL
;
109 direntry_t
*entries
= NULL
;
110 idx_t entries_allocated
= 0;
111 idx_t entries_used
= 0;
113 comparison_function cmp
= comparison_function_table
[option
];
120 struct dirent
const *dp
;
127 /* Some readdir()s do not absorb ENOENT (dir deleted but open).
128 This bug was fixed in glibc 2.3 (2002). */
129 #if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))
136 /* Skip "", ".", and "..". "" is returned by at least one buggy
137 implementation: Solaris 2.4 readdir on NFS file systems. */
139 if (entry
[entry
[0] != '.' ? 0 : entry
[1] != '.' ? 1 : 2] != '\0')
141 idx_t entry_size
= _D_EXACT_NAMLEN (dp
) + 1;
142 if (allocated
- used
<= entry_size
)
143 name_space
= xpalloc (name_space
, &allocated
,
144 entry_size
- (allocated
- used
),
145 IDX_MAX
- 1, sizeof *name_space
);
146 memcpy (name_space
+ used
, entry
, entry_size
);
149 if (entries_allocated
== entries_used
)
150 entries
= xpalloc (entries
, &entries_allocated
, 1, -1,
152 entries
[entries_used
].name
= used
;
154 entries
[entries_used
].ino
= dp
->d_ino
;
170 qsort_r (entries
, entries_used
, sizeof *entries
, cmp
, name_space
);
171 char *sorted_name_space
= ximalloc (used
+ 1);
172 char *p
= sorted_name_space
;
173 for (idx_t i
= 0; i
< entries_used
; i
++)
174 p
= stpcpy (p
, name_space
+ entries
[i
].name
) + 1;
177 name_space
= sorted_name_space
;
181 if (used
== allocated
)
182 name_space
= xirealloc (name_space
, used
+ 1);
183 name_space
[used
] = '\0';
190 /* Return a freshly allocated string containing the file names
191 in directory DIR, separated by '\0' characters;
192 the end is marked by two '\0' characters in a row.
193 Return NULL (setting errno) if DIR cannot be opened, read, or closed. */
196 savedir (char const *dir
, enum savedir_option option
)
198 DIR *dirp
= opendir (dir
);
203 char *name_space
= streamsavedir (dirp
, option
);
204 if (closedir (dirp
) != 0)