1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-02 21:10:27 +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> # include <config.h>
#endif #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 <stdio.h>
#include <errno.h> #include <errno.h>
@ -35,6 +52,7 @@
#include "libguile/smob.h" #include "libguile/smob.h"
#include "libguile/feature.h" #include "libguile/feature.h"
#include "libguile/fports.h" #include "libguile/fports.h"
#include "libguile/private-gc.h" /* for SCM_MAX */
#include "libguile/iselect.h" #include "libguile/iselect.h"
#include "libguile/strings.h" #include "libguile/strings.h"
#include "libguile/vectors.h" #include "libguile/vectors.h"
@ -92,6 +110,7 @@
#if defined (__MINGW32__) || defined (_MSC_VER) || defined (__BORLANDC__) #if defined (__MINGW32__) || defined (_MSC_VER) || defined (__BORLANDC__)
# include "win32-dirent.h" # include "win32-dirent.h"
# define NAMLEN(dirent) strlen((dirent)->d_name) # define NAMLEN(dirent) strlen((dirent)->d_name)
/* The following bits are per AC_HEADER_DIRENT doco in the autoconf manual */
#elif HAVE_DIRENT_H #elif HAVE_DIRENT_H
# include <dirent.h> # include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name) # define NAMLEN(dirent) strlen((dirent)->d_name)
@ -178,6 +197,13 @@
# define fsync(fd) _commit (fd) # define fsync(fd) _commit (fd)
# define fchmod(fd, mode) (-1) # define fchmod(fd, mode) (-1)
#endif /* __MINGW32__ */ #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 */ /* 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 /* FIXME: The glibc manual has a portability note that readdir_r may not
null-terminate its return string. The circumstances outlined for this 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 are not clear, nor is it clear what should be done about it. Lets use
about this if/when someone can figure it out. */ 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_DEFINE (scm_readdir, "readdir", 1, 0, 0,
(SCM port), (SCM port),
@ -847,29 +874,67 @@ SCM_DEFINE (scm_readdir, "readdir", 1, 0, 0,
if (!SCM_DIR_OPEN_P (port)) if (!SCM_DIR_OPEN_P (port))
SCM_MISC_ERROR ("Directory ~S is not open.", scm_list_1 (port)); SCM_MISC_ERROR ("Directory ~S is not open.", scm_list_1 (port));
errno = 0;
{
#if HAVE_READDIR_R #if HAVE_READDIR_R
/* On Solaris 2.7, struct dirent only contains "char d_name[1]" and one is /* As noted in the glibc manual, on various systems (such as Solaris) the
expected to provide a buffer of "sizeof(struct dirent) + NAME_MAX" d_name[] field is only 1 char and you're expected to size the dirent
bytes. The glibc 2.3.2 manual notes this sort of thing too, and buffer for readdir_r based on NAME_MAX. The SCM_MAX expressions below
advises "offsetof(struct dirent,d_name) + NAME_MAX + 1". Either should effectively give either sizeof(d_name) or NAME_MAX+1, whichever is
suffice, we give both to be certain. */ bigger.
union {
struct dirent ent; On solaris 10 there's no NAME_MAX constant, it's necessary to use
char pad1 [sizeof(struct dirent) + NAME_MAX]; pathconf(). We prefer NAME_MAX though, since it should be a constant
char pad2 [offsetof (struct dirent, d_name) + NAME_MAX + 1]; and will therefore save a system call. We also prefer it since dirfd()
} u; is not available everywhere.
SCM_SYSCALL (readdir_r ((DIR *) SCM_CELL_WORD_1 (port), &u.ent, &rdent));
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 #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 #endif
errno = 0;
SCM_SYSCALL (readdir_r (ds, (struct dirent *) buf, &rdent));
if (errno != 0) if (errno != 0)
SCM_SYSERROR; SCM_SYSERROR;
if (! rdent)
return SCM_EOF_VAL;
namlen = NAMLEN (rdent);
return (rdent ? scm_from_locale_stringn (rdent->d_name, NAMLEN (rdent)) return (rdent ? scm_from_locale_stringn (rdent->d_name, NAMLEN (rdent))
: SCM_EOF_VAL); : 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 #undef FUNC_NAME