1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-02 04:40:29 +02:00

(scm_readdir): Use fpathconf for the dirent size when

NAME_MAX is not available, which is so on Solaris 10.  Report and help
by Bill Schottstaedt.
This commit is contained in:
Kevin Ryde 2006-02-25 21:22:30 +00:00
parent 1ec2078756
commit d34a52e744

View file

@ -28,6 +28,23 @@
# include <config.h>
#endif
/* AIX requires this to be the first thing in the file. The #pragma
directive is indented so pre-ANSI compilers will ignore it, rather
than choke on it. */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
# pragma alloca
# else
# ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
# endif
# endif
# endif
#endif
#include <stdio.h>
#include <errno.h>
@ -35,6 +52,7 @@
#include "libguile/smob.h"
#include "libguile/feature.h"
#include "libguile/fports.h"
#include "libguile/private-gc.h" /* for SCM_MAX */
#include "libguile/iselect.h"
#include "libguile/strings.h"
#include "libguile/vectors.h"
@ -92,6 +110,7 @@
#if defined (__MINGW32__) || defined (_MSC_VER) || defined (__BORLANDC__)
# include "win32-dirent.h"
# define NAMLEN(dirent) strlen((dirent)->d_name)
/* The following bits are per AC_HEADER_DIRENT doco in the autoconf manual */
#elif HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
@ -178,6 +197,13 @@
# define fsync(fd) _commit (fd)
# define fchmod(fd, mode) (-1)
#endif /* __MINGW32__ */
/* This definition is for Solaris 10, it's probably not right elsewhere, but
that's ok, it shouldn't be used elsewhere. */
#if ! HAVE_DIRFD
#define dirfd(dirstream) (dirstream->dd_fd)
#endif
/* Two helper macros for an often used pattern */
@ -831,8 +857,9 @@ SCM_DEFINE (scm_opendir, "opendir", 1, 0, 0,
/* FIXME: The glibc manual has a portability note that readdir_r may not
null-terminate its return string. The circumstances outlined for this
are not clear, nor is it clear what should be done about it. Lets worry
about this if/when someone can figure it out. */
are not clear, nor is it clear what should be done about it. Lets use
NAMLEN and worry about what else should be done if/when someone can
figure it out. */
SCM_DEFINE (scm_readdir, "readdir", 1, 0, 0,
(SCM port),
@ -847,29 +874,67 @@ SCM_DEFINE (scm_readdir, "readdir", 1, 0, 0,
if (!SCM_DIR_OPEN_P (port))
SCM_MISC_ERROR ("Directory ~S is not open.", scm_list_1 (port));
errno = 0;
{
#if HAVE_READDIR_R
/* On Solaris 2.7, struct dirent only contains "char d_name[1]" and one is
expected to provide a buffer of "sizeof(struct dirent) + NAME_MAX"
bytes. The glibc 2.3.2 manual notes this sort of thing too, and
advises "offsetof(struct dirent,d_name) + NAME_MAX + 1". Either should
suffice, we give both to be certain. */
union {
struct dirent ent;
char pad1 [sizeof(struct dirent) + NAME_MAX];
char pad2 [offsetof (struct dirent, d_name) + NAME_MAX + 1];
} u;
SCM_SYSCALL (readdir_r ((DIR *) SCM_CELL_WORD_1 (port), &u.ent, &rdent));
/* As noted in the glibc manual, on various systems (such as Solaris) the
d_name[] field is only 1 char and you're expected to size the dirent
buffer for readdir_r based on NAME_MAX. The SCM_MAX expressions below
effectively give either sizeof(d_name) or NAME_MAX+1, whichever is
bigger.
On solaris 10 there's no NAME_MAX constant, it's necessary to use
pathconf(). We prefer NAME_MAX though, since it should be a constant
and will therefore save a system call. We also prefer it since dirfd()
is not available everywhere.
An alternative to dirfd() would be to open() the directory and then use
fdopendir(), if the latter is available. That'd let us hold the fd
somewhere in the smob, or just the dirent size calculated once. */
{
struct dirent de; /* just for sizeof */
DIR *ds = (DIR *) SCM_CELL_WORD_1 (port);
size_t namlen;
#ifdef NAME_MAX
char buf [SCM_MAX (sizeof (de),
sizeof (de) - sizeof (de.d_name) + NAME_MAX + 1)];
#else
SCM_SYSCALL (rdent = readdir ((DIR *) SCM_CELL_WORD_1 (port)));
char *buf;
long name_max = fpathconf (dirfd (ds), _PC_NAME_MAX);
if (name_max == -1)
SCM_SYSERROR;
buf = alloca (SCM_MAX (sizeof (de),
sizeof (de) - sizeof (de.d_name) + name_max + 1));
#endif
errno = 0;
SCM_SYSCALL (readdir_r (ds, (struct dirent *) buf, &rdent));
if (errno != 0)
SCM_SYSERROR;
if (! rdent)
return SCM_EOF_VAL;
namlen = NAMLEN (rdent);
return (rdent ? scm_from_locale_stringn (rdent->d_name, NAMLEN (rdent))
: SCM_EOF_VAL);
}
#else
{
SCM ret;
scm_dynwind_begin (0);
scm_i_dynwind_pthread_mutex_lock (&scm_i_misc_mutex);
errno = 0;
SCM_SYSCALL (rdent = readdir ((DIR *) SCM_CELL_WORD_1 (port)));
if (errno != 0)
SCM_SYSERROR;
ret = (rdent ? scm_from_locale_stringn (rdent->d_name, NAMLEN (rdent))
: SCM_EOF_VAL);
scm_dynwind_end ();
return ret;
}
#endif
}
#undef FUNC_NAME