1 /* savedir.c -- save the list of files in a directory in a string
3 Copyright (C) 1990, 1997-2001, 2003-2006, 2009-2024 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>
30 #ifndef _D_EXACT_NAMLEN
31 # define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
38 #include "attribute.h"
41 /* Pacify GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113963>. */
43 # pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
44 # pragma GCC diagnostic ignored "-Wanalyzer-null-dereference"
49 /* Offset of file name in name_space. */
53 /* File inode number. */
58 /* Compare the names of two directory entries */
61 direntry_cmp_name (void const *a
, void const *b
, void *arg
)
63 direntry_t
const *dea
= a
;
64 direntry_t
const *deb
= b
;
65 char const *name_space
= arg
;
67 return strcmp (name_space
+ dea
->name
, name_space
+ deb
->name
);
71 /* Compare the inode numbers of two directory entries */
74 direntry_cmp_inode (void const *a
, void const *b
, MAYBE_UNUSED
void *arg
)
76 direntry_t
const *dea
= a
;
77 direntry_t
const *deb
= b
;
79 return _GL_CMP (dea
->ino
, deb
->ino
);
83 typedef int (*comparison_function
) (void const *, void const *, void *);
85 static comparison_function
const comparison_function_table
[] =
94 /* Return a freshly allocated string containing the file names
95 in directory DIRP, separated by '\0' characters;
96 the end is marked by two '\0' characters in a row.
97 Returned values are sorted according to OPTION.
98 Return NULL (setting errno) if DIRP cannot be read.
99 If DIRP is NULL, return NULL without affecting errno. */
102 streamsavedir (DIR *dirp
, enum savedir_option option
)
104 char *name_space
= NULL
;
106 direntry_t
*entries
= NULL
;
107 idx_t entries_allocated
= 0;
108 idx_t entries_used
= 0;
110 comparison_function cmp
= comparison_function_table
[option
];
117 struct dirent
const *dp
;
125 /* Skip "", ".", and "..". "" is returned by at least one buggy
126 implementation: Solaris 2.4 readdir on NFS file systems. */
128 if (entry
[entry
[0] != '.' ? 0 : entry
[1] != '.' ? 1 : 2] != '\0')
130 idx_t entry_size
= _D_EXACT_NAMLEN (dp
) + 1;
131 if (allocated
- used
<= entry_size
)
132 name_space
= xpalloc (name_space
, &allocated
,
133 entry_size
- (allocated
- used
),
134 IDX_MAX
- 1, sizeof *name_space
);
135 memcpy (name_space
+ used
, entry
, entry_size
);
138 if (entries_allocated
== entries_used
)
139 entries
= xpalloc (entries
, &entries_allocated
, 1, -1,
141 entries
[entries_used
].name
= used
;
143 entries
[entries_used
].ino
= dp
->d_ino
;
159 qsort_r (entries
, entries_used
, sizeof *entries
, cmp
, name_space
);
160 char *sorted_name_space
= ximalloc (used
+ 1);
161 char *p
= sorted_name_space
;
162 for (idx_t i
= 0; i
< entries_used
; i
++)
163 p
= stpcpy (p
, name_space
+ entries
[i
].name
) + 1;
166 name_space
= sorted_name_space
;
170 if (used
== allocated
)
171 name_space
= xirealloc (name_space
, used
+ 1);
172 name_space
[used
] = '\0';
179 /* Return a freshly allocated string containing the file names
180 in directory DIR, separated by '\0' characters;
181 the end is marked by two '\0' characters in a row.
182 Return NULL (setting errno) if DIR cannot be opened, read, or closed. */
185 savedir (char const *dir
, enum savedir_option option
)
187 DIR *dirp
= opendir (dir
);
192 char *name_space
= streamsavedir (dirp
, option
);
193 if (closedir (dirp
) != 0)