Migrated from GPL version 2 to GPL version 3
[findutils.git] / lib / savedirinfo.c
blobbe62b7fc753bd610670d548915e76b6cc13151f6
1 /* savedirinfo.c -- Save the list of files in a directory, with additional information.
3 Copyright 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free
4 Software Foundation, Inc.
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 <http://www.gnu.org/licenses/>.
19 /* Written by James Youngman, <jay@gnu.org>. */
20 /* Derived from savedir.c, written by David MacKenzie <djm@gnu.org>. */
22 #include <config.h>
24 #if HAVE_SYS_STAT_H
25 # include <sys/stat.h>
26 #endif
28 #if HAVE_SYS_TYPES_H
29 # include <sys/types.h>
30 #endif
32 /* The presence of unistd.h is assumed by gnulib these days, so we
33 * might as well assume it too.
35 #include <unistd.h>
37 #include <errno.h>
39 #if HAVE_DIRENT_H
40 # include <dirent.h>
41 #else
42 # define dirent direct
43 # if HAVE_SYS_NDIR_H
44 # include <sys/ndir.h>
45 # endif
46 # if HAVE_SYS_DIR_H
47 # include <sys/dir.h>
48 # endif
49 # if HAVE_NDIR_H
50 # include <ndir.h>
51 # endif
52 #endif
54 #ifdef CLOSEDIR_VOID
55 /* Fake a return value. */
56 # define CLOSEDIR(d) (closedir (d), 0)
57 #else
58 # define CLOSEDIR(d) closedir (d)
59 #endif
61 #include <stddef.h>
62 #include <stdlib.h>
63 #include <string.h>
65 #include "xalloc.h"
66 #include "extendbuf.h"
67 #include "savedirinfo.h"
69 /* In order to use struct dirent.d_type, it has to be enabled on the
70 * configure command line, and we have to have a d_type member in
71 * 'struct dirent'.
73 #if !defined(USE_STRUCT_DIRENT_D_TYPE)
74 /* Not enabled, hence pretend it is absent. */
75 #undef HAVE_STRUCT_DIRENT_D_TYPE
76 #endif
77 #if !defined(HAVE_STRUCT_DIRENT_D_TYPE)
78 /* Not present, so cannot use it. */
79 #undef USE_STRUCT_DIRENT_D_TYPE
80 #endif
83 #if defined HAVE_STRUCT_DIRENT_D_TYPE && defined USE_STRUCT_DIRENT_D_TYPE
84 /* Convert the value of struct dirent.d_type into a value for
85 * struct stat.st_mode (at least the file type bits), or zero
86 * if the type is DT_UNKNOWN or is a value we don't know about.
88 static mode_t
89 type_to_mode(unsigned type)
91 switch (type)
93 #ifdef DT_FIFO
94 case DT_FIFO: return S_IFIFO;
95 #endif
96 #ifdef DT_CHR
97 case DT_CHR: return S_IFCHR;
98 #endif
99 #ifdef DT_DIR
100 case DT_DIR: return S_IFDIR;
101 #endif
102 #ifdef DT_BLK
103 case DT_BLK: return S_IFBLK;
104 #endif
105 #ifdef DT_REG
106 case DT_REG: return S_IFREG;
107 #endif
108 #ifdef DT_LNK
109 case DT_LNK: return S_IFLNK;
110 #endif
111 #ifdef DT_SOCK
112 case DT_SOCK: return S_IFSOCK;
113 #endif
114 default:
115 return 0; /* Unknown. */
119 #endif
121 struct new_savedir_direntry_internal
123 int flags; /* from SaveDirDataFlags */
124 mode_t type_info;
125 size_t buffer_offset;
130 static int
131 savedir_cmp(const void *p1, const void *p2)
133 const struct savedir_direntry *de1, *de2;
134 de1 = p1;
135 de2 = p2;
136 return strcmp(de1->name, de2->name); /* POSIX order, not locale order. */
140 static struct savedir_direntry*
141 convertentries(const struct savedir_dirinfo *info,
142 struct new_savedir_direntry_internal *internal)
144 char *p = info->buffer;
145 struct savedir_direntry *result;
146 int n =info->size;
147 int i;
150 result = xmalloc(sizeof(*result) * info->size);
152 for (i=0; i<n; ++i)
154 result[i].flags = internal[i].flags;
155 result[i].type_info = internal[i].type_info;
156 result[i].name = &p[internal[i].buffer_offset];
158 return result;
162 struct savedir_dirinfo *
163 xsavedir(const char *dir, int flags)
165 DIR *dirp;
166 struct dirent *dp;
167 struct savedir_dirinfo *result = NULL;
168 struct new_savedir_direntry_internal *internal;
170 size_t namebuf_allocated = 0u, namebuf_used = 0u;
171 size_t entrybuf_allocated = 0u;
172 int save_errno;
174 dirp = opendir (dir);
175 if (dirp == NULL)
176 return NULL;
178 errno = 0;
179 result = xmalloc(sizeof(*result));
180 result->buffer = NULL;
181 result->size = 0u;
182 result->entries = NULL;
183 internal = NULL;
185 while ((dp = readdir (dirp)) != NULL)
187 /* Skip "", ".", and "..". "" is returned by at least one buggy
188 implementation: Solaris 2.4 readdir on NFS file systems. */
189 char const *entry = dp->d_name;
190 if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
192 /* Remember the name. */
193 size_t entry_size = strlen (entry) + 1;
194 result->buffer = extendbuf(result->buffer, namebuf_used+entry_size, &namebuf_allocated);
195 memcpy ((result->buffer) + namebuf_used, entry, entry_size);
197 /* Remember the other stuff. */
198 internal = extendbuf(internal, (1+result->size)*sizeof(*internal), &entrybuf_allocated);
199 internal[result->size].flags = 0;
201 #if defined HAVE_STRUCT_DIRENT_D_TYPE && defined USE_STRUCT_DIRENT_D_TYPE
202 internal[result->size].type_info = type_to_mode(dp->d_type);
203 if (dp->d_type != DT_UNKNOWN)
204 internal[result->size].flags |= SavedirHaveFileType;
205 #else
206 internal[result->size].type_info = 0;
207 #endif
208 internal[result->size].buffer_offset = namebuf_used;
210 /* Prepare for the next iteration */
211 ++(result->size);
212 namebuf_used += entry_size;
216 result->buffer = extendbuf(result->buffer, namebuf_used+1, &namebuf_allocated);
217 result->buffer[namebuf_used] = '\0';
219 /* convert the result to its externally-usable form. */
220 result->entries = convertentries(result, internal);
221 free(internal);
222 internal = NULL;
225 if (flags & SavedirSort)
227 qsort(result->entries,
228 result->size, sizeof(*result->entries),
229 savedir_cmp);
233 save_errno = errno;
234 if (CLOSEDIR (dirp) != 0)
235 save_errno = errno;
236 if (save_errno != 0)
238 free (result->buffer);
239 free (result);
240 errno = save_errno;
241 return NULL;
244 return result;
247 void free_dirinfo(struct savedir_dirinfo *p)
249 free(p->entries);
250 p->entries = NULL;
251 free(p->buffer);
252 p->buffer = NULL;
253 free(p);
258 static char *
259 new_savedirinfo (const char *dir, struct savedir_extrainfo **extra)
261 struct savedir_dirinfo *p = xsavedir(dir, SavedirSort);
262 char *buf, *s;
263 size_t bufbytes = 0;
264 unsigned int i;
266 if (p)
268 struct savedir_extrainfo *pex = xmalloc(p->size * sizeof(*extra));
269 for (i=0; i<p->size; ++i)
271 bufbytes += strlen(p->entries[i].name);
272 ++bufbytes; /* the \0 */
274 pex[i].type_info = p->entries[i].type_info;
277 s = buf = xmalloc(bufbytes+1);
278 for (i=0; i<p->size; ++i)
280 size_t len = strlen(p->entries[i].name);
281 memcpy(s, p->entries[i].name, len);
282 s += len;
283 *s = 0; /* Place a NUL */
284 ++s; /* Skip the NUL. */
286 *s = 0; /* final (doubled) terminating NUL */
288 if (extra)
289 *extra = pex;
290 else
291 free (pex);
292 return buf;
294 else
296 return NULL;
301 #if 0
302 /* Return a freshly allocated string containing the filenames
303 in directory DIR, separated by '\0' characters;
304 the end is marked by two '\0' characters in a row.
305 Return NULL (setting errno) if DIR cannot be opened, read, or closed. */
307 static char *
308 old_savedirinfo (const char *dir, struct savedir_extrainfo **extra)
310 DIR *dirp;
311 struct dirent *dp;
312 char *name_space;
313 size_t namebuf_allocated = 0u, namebuf_used = 0u;
314 #if defined HAVE_STRUCT_DIRENT_D_TYPE && defined USE_STRUCT_DIRENT_D_TYPE
315 size_t extra_allocated = 0u, extra_used = 0u;
316 struct savedir_extrainfo *info = NULL;
317 #endif
318 int save_errno;
320 if (extra)
321 *extra = NULL;
323 dirp = opendir (dir);
324 if (dirp == NULL)
325 return NULL;
327 errno = 0;
328 name_space = NULL;
329 while ((dp = readdir (dirp)) != NULL)
331 /* Skip "", ".", and "..". "" is returned by at least one buggy
332 implementation: Solaris 2.4 readdir on NFS file systems. */
333 char const *entry = dp->d_name;
334 if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
336 /* Remember the name. */
337 size_t entry_size = strlen (entry) + 1;
338 name_space = extendbuf(name_space, namebuf_used+entry_size, &namebuf_allocated);
339 memcpy (name_space + namebuf_used, entry, entry_size);
340 namebuf_used += entry_size;
343 #if defined HAVE_STRUCT_DIRENT_D_TYPE && defined USE_STRUCT_DIRENT_D_TYPE
344 /* Remember the type. */
345 if (extra)
347 info = extendbuf(info,
348 (extra_used+1) * sizeof(struct savedir_dirinfo),
349 &extra_allocated);
350 info[extra_used].type_info = type_to_mode(dp->d_type);
351 ++extra_used;
353 #endif
357 name_space = extendbuf(name_space, namebuf_used+1, &namebuf_allocated);
358 name_space[namebuf_used] = '\0';
360 save_errno = errno;
361 if (CLOSEDIR (dirp) != 0)
362 save_errno = errno;
363 if (save_errno != 0)
365 free (name_space);
366 errno = save_errno;
367 return NULL;
370 #if defined HAVE_STRUCT_DIRENT_D_TYPE && defined USE_STRUCT_DIRENT_D_TYPE
371 if (extra && info)
372 *extra = info;
373 #endif
375 return name_space;
377 #endif
380 char *
381 savedirinfo (const char *dir, struct savedir_extrainfo **extra)
383 return new_savedirinfo(dir, extra);