Skip to content

Commit

Permalink
Implement opendir()/readdir() etc.
Browse files Browse the repository at this point in the history
For now, this just falls back to Fsfirst()/Fsnext(), and does not attempt to
use Dopendir() from MiNT
  • Loading branch information
th-otto committed May 15, 2020
1 parent f109013 commit be8f0a6
Show file tree
Hide file tree
Showing 16 changed files with 601 additions and 0 deletions.
39 changes: 39 additions & 0 deletions include/bits/dirent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

#ifndef _SYS_DIRENT_H
# error "Never use <bits/dirent.h> directly; include <dirent.h> instead."
#endif

#ifndef NAME_MAX
# define NAME_MAX 255
#endif

struct dirent {
__ino_t d_fileno; /* garbage under TOS */
__off_t d_off; /* position in directory */
unsigned short d_namlen; /* for us, length of d_name */
char d_name[NAME_MAX+1];
};

#define __DIRENTSIZ(x) (sizeof(struct dirent))

#ifndef _OSTRUCT_H
# include <mint/ostruct.h>
#endif

struct __dirstream {
short status; /* status of the search so far: */
#define _INSEARCH 0 /* need to call Fsnext for a new entry */
#define _STARTSEARCH 1 /* Fsfirst called once, successfully */
#define _NMFILE 2 /* no more files in directory */
_DTA dta; /* TOS DTA for this directory */
char *dirname; /* directory of the search (used under
TOS for rewinddir) */
struct dirent buf; /* dirent struct for this directory */
long handle; /* Dreaddir handle */
};

#undef _DIRENT_HAVE_D_TYPE
#undef _DIRENT_HAVE_D_RECLEN

#define _DIRENT_HAVE_D_NAMLEN
#define _DIRENT_HAVE_D_OFF
1 change: 1 addition & 0 deletions include/dirent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <sys/dirent.h>
1 change: 1 addition & 0 deletions include/errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern int errno;
#define ELOOP 80
#define EISDIR 24
#define EACCES 36
#define ENMFILES 49
#define EEXIST 85
#define ERANGE 88
#define EDOM 89
126 changes: 126 additions & 0 deletions include/sys/dirent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* header file for POSIX directory access routines */

#ifndef _SYS_DIRENT_H
# define _SYS_DIRENT_H 1

#ifndef _FEATURES_H
# include <features.h>
#endif

__BEGIN_DECLS

#include <sys/types.h>

#include <bits/dirent.h>

/* This file defines `struct dirent'.
It defines the macro `_DIRENT_HAVE_D_NAMLEN' iff there is a `d_namlen'
member that gives the length of `d_name'.
It defines the macro `_DIRENT_HAVE_D_RECLEN' iff there is a `d_reclen'
member that gives the size of the entire directory entry.
It defines the macro `_DIRENT_HAVE_D_OFF' iff there is a `d_off'
member that gives the file offset of the next directory entry.
It defines the macro `_DIRENT_HAVE_D_TYPE' iff there is a `d_type'
member that gives the type of the file.
*/

#if (defined __USE_BSD || defined __USE_MISC) && !defined d_fileno
# define d_ino d_fileno /* Backward compatibility. */
#endif

/* These macros extract size information from a `struct dirent *'.
They may evaluate their argument multiple times, so it must not
have side effects. Each of these may involve a relatively costly
call to `strlen' on some systems, so these values should be cached.
_D_EXACT_NAMLEN (DP) returns the length of DP->d_name, not including
its terminating null character.
_D_ALLOC_NAMLEN (DP) returns a size at least (_D_EXACT_NAMLEN (DP) + 1);
that is, the allocation size needed to hold the DP->d_name string.
Use this macro when you don't need the exact length, just an upper bound.
This macro is less likely to require calling `strlen' than _D_EXACT_NAMLEN.
*/

#ifdef _DIRENT_HAVE_D_NAMLEN
# define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
#else
# define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name))
# ifdef _DIRENT_HAVE_D_RECLEN
# define _D_ALLOC_NAMLEN(d) (((char *) (d) + (d)->d_reclen) - &(d)->d_name[0])
# else
# define _D_ALLOC_NAMLEN(d) (sizeof (d)->d_name > 1 ? sizeof (d)->d_name : \
_D_EXACT_NAMLEN (d) + 1)
# endif
#endif


/* This is the data type of directory stream objects.
The actual structure is opaque to users. */
typedef struct __dirstream DIR;

/* Open a directory stream on NAME.
Return a DIR stream on the directory, or NULL if it could not be opened. */
extern DIR *opendir (__const char *__name) __THROW;

/* Close the directory stream DIRP.
Return 0 if successful, -1 if not. */
extern int closedir (DIR *__dirp) __THROW;

/* Read a directory entry from DIRP. Return a pointer to a `struct
dirent' describing the entry, or NULL for EOF or error. The
storage returned may be overwritten by a later readdir call on the
same DIR stream. */
extern struct dirent *readdir (DIR *__dirp) __THROW;

#if defined __USE_POSIX || defined __USE_MISC
/* Reentrant version of `readdir'. Return in RESULT a pointer to the
next entry. */
extern int readdir_r (DIR *__restrict __dirp,
struct dirent *__restrict __entry,
struct dirent **__restrict __result) __THROW;
#endif

/* Rewind DIRP to the beginning of the directory. */
extern void rewinddir (DIR *__dirp) __THROW;

#if defined __USE_BSD || defined __USE_MISC || defined __USE_XOPEN

/* Seek to position POS on DIRP. */
extern void seekdir (DIR *__dirp, long int __pos) __THROW;

/* Return the current position of DIRP. */
extern long int telldir (DIR *__dirp) __THROW;

#endif /* Use BSD or misc or xopen. */

#if defined __USE_BSD || defined __USE_MISC

/* Scan the directory DIR, calling SELECTOR on each directory entry.
Entries for which SELECT returns nonzero are individually malloc'd,
sorted using qsort with CMP, and collected in a malloc'd array in
*NAMELIST. Returns the number of entries selected, or -1 on error. */
extern int scandir (__const char *__restrict __dir,
struct dirent ***__restrict __namelist,
int (*__selector) (__const struct dirent *),
int (*__cmp) (const struct dirent **, const struct dirent **)) __THROW;

/* Function to compare two `struct dirent's alphabetically. */
extern int alphasort (const struct dirent **, const struct dirent **) __THROW;

# ifdef __USE_GNU
/* Function to compare two `struct dirent's by name & version. */
extern int versionsort (const struct dirent **, const struct dirent **) __THROW;
# endif

#endif /* Use BSD or misc. */


__END_DECLS

#endif /* _SYS_DIRENT_H */
25 changes: 25 additions & 0 deletions sources/alphasort.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright (C) 1992, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */

#include <dirent.h>
#include <string.h>

int alphasort(const struct dirent **a, const struct dirent **b)
{
return strcoll((*a)->d_name, (*b)->d_name);
}
40 changes: 40 additions & 0 deletions sources/closedir.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* closedir routine */

/* under MiNT (v0.9 or better) these use the appropriate system calls.
* under TOS or older versions of MiNT, they use Fsfirst/Fsnext
*
* Written by Eric R. Smith and placed in the public domain
*/

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <mint/osbind.h>
#include <mint/mintbind.h>
#include "lib.h"


/* Important note: the same comment for the status variable of
* opendir/readdir under Metados applies also here.
*/

int closedir(DIR *dirp)
{
long r;

/* The GNU libc closedir returns gracefully if a NULL pointer is
passed. We follow here. */
if (dirp == NULL)
{
__set_errno(EBADF);
return -1;
}

free(dirp->dirname);
free(dirp);
return r;
}
4 changes: 4 additions & 0 deletions sources/inode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include <sys/types.h>

/* used in readdir, __do_stat, fstat */
__ino_t __inode = 32;
4 changes: 4 additions & 0 deletions sources/lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>

/* definitions needed for stack stuff */

Expand Down Expand Up @@ -44,6 +45,9 @@ void _crtinit_noargs(void);

extern FILE *__stdio_head;

/* inode.c */
extern __ino_t __inode;

#undef __set_errno
#define __set_errno(e) (errno = (int)(e))

Expand Down
108 changes: 108 additions & 0 deletions sources/opendir.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* opendir routine */

/* under MiNT (v0.9 or better) these use the appropriate system calls.
* under TOS or older versions of MiNT, they use Fsfirst/Fsnext
*
* Written by Eric R. Smith and placed in the public domain
*/

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <mint/mintbind.h>
#include "lib.h"


/* Important note: Metados can have opendir for some device and do not
* have it for others, so there is no point in having a status variable
* saying there is an opendir call. Check this every time.
*/

/* a new value for DIR->status, to indicate that the file system is not
* case sensitive.
*/
#define _NO_CASE 8

DIR *opendir(const char *uname)
{
DIR *d;
long r;
_DTA *olddta;
char namebuf[PATH_MAX];
char dirpath[PATH_MAX];
char *p;
char *name;

strcpy(namebuf, uname);
name = namebuf;

d = malloc(sizeof(DIR));
if (!d)
{
__set_errno(ENOMEM);
return NULL;
}

d->handle = 0xff000000L; /* indicate that the handle is invalid */

/* TOS emulation routines */

p = name;
if (p)
{
/* find the end of the string */
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';

/* make sure the string ends in '\' */
if (p != name || *(p-1) != '\\')
{
*p++ = '\\';
}
}

strcpy(p, "*.*");
olddta = Fgetdta();
Fsetdta(&(d->dta));
r = Fsfirst(name, 0x17);
Fsetdta(olddta);

if (r == 0)
{
d->status = _STARTSEARCH;
} else if (r == -ENOENT)
{
d->status = _NMFILE;
} else
{
free(d);
__set_errno(-r);
return NULL;
}
d->buf.d_off = 0;

/* for rewinddir: if necessary, build a relative path */
if (name[1] == ':') /* absolute path, no problem */
{
dirpath[0] = 0;
} else
{
dirpath[0] = Dgetdrv() + 'A';
dirpath[1] = ':';
dirpath[2] = 0;
if (*name != '\\')
(void)Dgetpath(dirpath + 2, 0);
}
d->dirname = malloc(strlen(dirpath) + strlen(name) + 1);
if (d->dirname)
{
strcpy(d->dirname, dirpath);
strcat(d->dirname, name);
}
return d;
}
Loading

0 comments on commit be8f0a6

Please sign in to comment.