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:
parent
1ec2078756
commit
d34a52e744
1 changed files with 81 additions and 16 deletions
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue