findutils/lib/savedirinfo.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);
}