mirror of
https://https.git.savannah.gnu.org/git/findutils.git
synced 2026-01-27 01:44:23 +00:00
387 lines
9.1 KiB
C
387 lines
9.1 KiB
C
/* savedirinfo.c -- Save the list of files in a directory, with additional information.
|
|
|
|
Copyright 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free
|
|
Software Foundation, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
/* Written by James Youngman, <jay@gnu.org>. */
|
|
/* Derived from savedir.c, written by David MacKenzie <djm@gnu.org>. */
|
|
|
|
#if HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#if HAVE_SYS_STAT_H
|
|
# include <sys/stat.h>
|
|
#endif
|
|
|
|
#if HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
|
|
/* The presence of unistd.h is assumed by gnulib these days, so we
|
|
* might as well assume it too.
|
|
*/
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#if HAVE_DIRENT_H
|
|
# include <dirent.h>
|
|
#else
|
|
# define dirent direct
|
|
# if HAVE_SYS_NDIR_H
|
|
# include <sys/ndir.h>
|
|
# endif
|
|
# if HAVE_SYS_DIR_H
|
|
# include <sys/dir.h>
|
|
# endif
|
|
# if HAVE_NDIR_H
|
|
# include <ndir.h>
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef CLOSEDIR_VOID
|
|
/* Fake a return value. */
|
|
# define CLOSEDIR(d) (closedir (d), 0)
|
|
#else
|
|
# define CLOSEDIR(d) closedir (d)
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "xalloc.h"
|
|
#include "extendbuf.h"
|
|
#include "savedirinfo.h"
|
|
|
|
/* In order to use struct dirent.d_type, it has to be enabled on the
|
|
* configure command line, and we have to have a d_type member in
|
|
* 'struct dirent'.
|
|
*/
|
|
#if !defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
/* Not enabled, hence pretend it is absent. */
|
|
#undef HAVE_STRUCT_DIRENT_D_TYPE
|
|
#endif
|
|
#if !defined(HAVE_STRUCT_DIRENT_D_TYPE)
|
|
/* Not present, so cannot use it. */
|
|
#undef USE_STRUCT_DIRENT_D_TYPE
|
|
#endif
|
|
|
|
|
|
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
/* Convert the value of struct dirent.d_type into a value for
|
|
* struct stat.st_mode (at least the file type bits), or zero
|
|
* if the type is DT_UNKNOWN or is a value we don't know about.
|
|
*/
|
|
static mode_t
|
|
type_to_mode(unsigned type)
|
|
{
|
|
switch (type)
|
|
{
|
|
#ifdef DT_FIFO
|
|
case DT_FIFO: return S_IFIFO;
|
|
#endif
|
|
#ifdef DT_CHR
|
|
case DT_CHR: return S_IFCHR;
|
|
#endif
|
|
#ifdef DT_DIR
|
|
case DT_DIR: return S_IFDIR;
|
|
#endif
|
|
#ifdef DT_BLK
|
|
case DT_BLK: return S_IFBLK;
|
|
#endif
|
|
#ifdef DT_REG
|
|
case DT_REG: return S_IFREG;
|
|
#endif
|
|
#ifdef DT_LNK
|
|
case DT_LNK: return S_IFLNK;
|
|
#endif
|
|
#ifdef DT_SOCK
|
|
case DT_SOCK: return S_IFSOCK;
|
|
#endif
|
|
default:
|
|
return 0; /* Unknown. */
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
struct new_savedir_direntry_internal
|
|
{
|
|
int flags; /* from SaveDirDataFlags */
|
|
mode_t type_info;
|
|
size_t buffer_offset;
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
savedir_cmp(const void *p1, const void *p2)
|
|
{
|
|
const struct savedir_direntry *de1, *de2;
|
|
de1 = p1;
|
|
de2 = p2;
|
|
return strcmp(de1->name, de2->name); /* POSIX order, not locale order. */
|
|
}
|
|
|
|
|
|
static struct savedir_direntry*
|
|
convertentries(const struct savedir_dirinfo *info,
|
|
struct new_savedir_direntry_internal *internal)
|
|
{
|
|
char *p = info->buffer;
|
|
struct savedir_direntry *result;
|
|
int n =info->size;
|
|
int i;
|
|
|
|
|
|
result = xmalloc(sizeof(*result) * info->size);
|
|
|
|
for (i=0; i<n; ++i)
|
|
{
|
|
result[i].flags = internal[i].flags;
|
|
result[i].type_info = internal[i].type_info;
|
|
result[i].name = &p[internal[i].buffer_offset];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
struct savedir_dirinfo *
|
|
xsavedir(const char *dir, int flags)
|
|
{
|
|
DIR *dirp;
|
|
struct dirent *dp;
|
|
struct savedir_dirinfo *result = NULL;
|
|
struct new_savedir_direntry_internal *internal;
|
|
|
|
size_t namebuf_allocated = 0u, namebuf_used = 0u;
|
|
size_t entrybuf_allocated = 0u;
|
|
int save_errno;
|
|
|
|
dirp = opendir (dir);
|
|
if (dirp == NULL)
|
|
return NULL;
|
|
|
|
errno = 0;
|
|
result = xmalloc(sizeof(*result));
|
|
result->buffer = NULL;
|
|
result->size = 0u;
|
|
result->entries = NULL;
|
|
internal = NULL;
|
|
|
|
while ((dp = readdir (dirp)) != NULL)
|
|
{
|
|
/* Skip "", ".", and "..". "" is returned by at least one buggy
|
|
implementation: Solaris 2.4 readdir on NFS file systems. */
|
|
char const *entry = dp->d_name;
|
|
if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
|
|
{
|
|
/* Remember the name. */
|
|
size_t entry_size = strlen (entry) + 1;
|
|
result->buffer = extendbuf(result->buffer, namebuf_used+entry_size, &namebuf_allocated);
|
|
memcpy ((result->buffer) + namebuf_used, entry, entry_size);
|
|
|
|
/* Remember the other stuff. */
|
|
internal = extendbuf(internal, (1+result->size)*sizeof(*internal), &entrybuf_allocated);
|
|
internal[result->size].flags = 0;
|
|
|
|
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
internal[result->size].type_info = type_to_mode(dp->d_type);
|
|
if (dp->d_type != DT_UNKNOWN)
|
|
internal[result->size].flags |= SavedirHaveFileType;
|
|
#else
|
|
internal[result->size].type_info = 0;
|
|
#endif
|
|
internal[result->size].buffer_offset = namebuf_used;
|
|
|
|
/* Prepare for the next iteration */
|
|
++(result->size);
|
|
namebuf_used += entry_size;
|
|
}
|
|
}
|
|
|
|
result->buffer = extendbuf(result->buffer, namebuf_used+1, &namebuf_allocated);
|
|
result->buffer[namebuf_used] = '\0';
|
|
|
|
/* convert the result to its externally-usable form. */
|
|
result->entries = convertentries(result, internal);
|
|
free(internal);
|
|
internal = NULL;
|
|
|
|
|
|
if (flags & SavedirSort)
|
|
{
|
|
qsort(result->entries,
|
|
result->size, sizeof(*result->entries),
|
|
savedir_cmp);
|
|
}
|
|
|
|
|
|
save_errno = errno;
|
|
if (CLOSEDIR (dirp) != 0)
|
|
save_errno = errno;
|
|
if (save_errno != 0)
|
|
{
|
|
free (result->buffer);
|
|
free (result);
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void free_dirinfo(struct savedir_dirinfo *p)
|
|
{
|
|
free(p->entries);
|
|
p->entries = NULL;
|
|
free(p->buffer);
|
|
p->buffer = NULL;
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
new_savedirinfo (const char *dir, struct savedir_extrainfo **extra)
|
|
{
|
|
struct savedir_dirinfo *p = xsavedir(dir, SavedirSort);
|
|
char *buf, *s;
|
|
size_t bufbytes = 0;
|
|
int i;
|
|
|
|
if (p)
|
|
{
|
|
struct savedir_extrainfo *pex = xmalloc(p->size * sizeof(*extra));
|
|
for (i=0; i<p->size; ++i)
|
|
{
|
|
bufbytes += strlen(p->entries[i].name);
|
|
++bufbytes; /* the \0 */
|
|
|
|
pex[i].type_info = p->entries[i].type_info;
|
|
}
|
|
|
|
s = buf = xmalloc(bufbytes+1);
|
|
for (i=0; i<p->size; ++i)
|
|
{
|
|
size_t len = strlen(p->entries[i].name);
|
|
memcpy(s, p->entries[i].name, len);
|
|
s += len;
|
|
*s = 0; /* Place a NUL */
|
|
++s; /* Skip the NUL. */
|
|
}
|
|
*s = 0; /* final (doubled) terminating NUL */
|
|
|
|
if (extra)
|
|
*extra = pex;
|
|
else
|
|
free (pex);
|
|
return buf;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
/* Return a freshly allocated string containing the filenames
|
|
in directory DIR, separated by '\0' characters;
|
|
the end is marked by two '\0' characters in a row.
|
|
Return NULL (setting errno) if DIR cannot be opened, read, or closed. */
|
|
|
|
static char *
|
|
old_savedirinfo (const char *dir, struct savedir_extrainfo **extra)
|
|
{
|
|
DIR *dirp;
|
|
struct dirent *dp;
|
|
char *name_space;
|
|
size_t namebuf_allocated = 0u, namebuf_used = 0u;
|
|
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
size_t extra_allocated = 0u, extra_used = 0u;
|
|
struct savedir_extrainfo *info = NULL;
|
|
#endif
|
|
int save_errno;
|
|
|
|
if (extra)
|
|
*extra = NULL;
|
|
|
|
dirp = opendir (dir);
|
|
if (dirp == NULL)
|
|
return NULL;
|
|
|
|
errno = 0;
|
|
name_space = NULL;
|
|
while ((dp = readdir (dirp)) != NULL)
|
|
{
|
|
/* Skip "", ".", and "..". "" is returned by at least one buggy
|
|
implementation: Solaris 2.4 readdir on NFS file systems. */
|
|
char const *entry = dp->d_name;
|
|
if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
|
|
{
|
|
/* Remember the name. */
|
|
size_t entry_size = strlen (entry) + 1;
|
|
name_space = extendbuf(name_space, namebuf_used+entry_size, &namebuf_allocated);
|
|
memcpy (name_space + namebuf_used, entry, entry_size);
|
|
namebuf_used += entry_size;
|
|
|
|
|
|
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
/* Remember the type. */
|
|
if (extra)
|
|
{
|
|
info = extendbuf(info,
|
|
(extra_used+1) * sizeof(struct savedir_dirinfo),
|
|
&extra_allocated);
|
|
info[extra_used].type_info = type_to_mode(dp->d_type);
|
|
++extra_used;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
name_space = extendbuf(name_space, namebuf_used+1, &namebuf_allocated);
|
|
name_space[namebuf_used] = '\0';
|
|
|
|
save_errno = errno;
|
|
if (CLOSEDIR (dirp) != 0)
|
|
save_errno = errno;
|
|
if (save_errno != 0)
|
|
{
|
|
free (name_space);
|
|
errno = save_errno;
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(USE_STRUCT_DIRENT_D_TYPE)
|
|
if (extra && info)
|
|
*extra = info;
|
|
#endif
|
|
|
|
return name_space;
|
|
}
|
|
#endif
|
|
|
|
|
|
char *
|
|
savedirinfo (const char *dir, struct savedir_extrainfo **extra)
|
|
{
|
|
return new_savedirinfo(dir, extra);
|
|
}
|