diff --git a/libmaxsi/Makefile b/libmaxsi/Makefile
index 23f0d3d3..cd60537a 100644
--- a/libmaxsi/Makefile
+++ b/libmaxsi/Makefile
@@ -33,6 +33,8 @@ c/ctype.o \
c/file.o \
c/fdio.o \
c/stdio.o \
+c/dir.o \
+c/fddir-sortix.o \
CHEADERS=\
c/h/ctype.h \
@@ -47,6 +49,7 @@ c/h/features.h \
c/h/string.h \
c/h/errno.h \
c/h/error.h \
+c/h/dirent.h \
c/h/sys/readdirents.h \
c/h/sys/stat.h \
c/h/sys/types.h \
diff --git a/libmaxsi/c/decl/DIR.h b/libmaxsi/c/decl/DIR.h
new file mode 100644
index 00000000..f33630ec
--- /dev/null
+++ b/libmaxsi/c/decl/DIR.h
@@ -0,0 +1,23 @@
+#ifndef _DIR_DECL
+#define _DIR_DECL
+struct dirent;
+
+#define _DIR_REGISTERED (1<<0)
+#define _DIR_ERROR (1<<1)
+#define _DIR_EOF (1<<2)
+typedef struct _DIR
+{
+ void* user;
+ int (*read_func)(void* user, struct dirent* dirent, size_t* size);
+ int (*rewind_func)(void* user);
+ int (*fd_func)(void* user);
+ int (*close_func)(void* user);
+ void (*free_func)(struct _DIR* dir);
+ /* Application writers shouldn't use anything beyond this point. */
+ struct _DIR* prev;
+ struct _DIR* next;
+ struct dirent* entry;
+ size_t entrysize;
+ int flags;
+} DIR;
+#endif
diff --git a/libmaxsi/c/dir.c b/libmaxsi/c/dir.c
new file mode 100644
index 00000000..3e3d0027
--- /dev/null
+++ b/libmaxsi/c/dir.c
@@ -0,0 +1,150 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of LibMaxsi.
+
+ LibMaxsi is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ LibMaxsi 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 Lesser General Public License for
+ more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with LibMaxsi. If not, see .
+
+ dir.c
+ DIR* is an interface allowing various directory backends.
+
+******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+DIR* firstdir = NULL;
+
+void dregister(DIR* dir)
+{
+ dir->flags |= _DIR_REGISTERED;
+ if ( !firstdir ) { firstdir = dir; return; }
+ dir->next = firstdir;
+ firstdir->prev = dir;
+ firstdir = dir;
+}
+
+void dunregister(DIR* dir)
+{
+ if ( !(dir->flags & _DIR_REGISTERED) ) { return; }
+ if ( !dir->prev ) { firstdir = dir->next; }
+ if ( dir->prev ) { dir->prev->next = dir->next; }
+ if ( dir->next ) { dir->next->prev = dir->prev; }
+ dir->flags &= ~_DIR_REGISTERED;
+}
+
+struct dirent* readdir(DIR* dir)
+{
+ if ( !dir->read_func )
+ {
+ dir->flags |= _DIR_ERROR;
+ errno = EBADF;
+ return 0;
+ }
+
+ size_t size = dir->entrysize;
+ int status = dir->read_func(dir->user, dir->entry, &size);
+ if ( status < 0 )
+ {
+ dir->flags |= _DIR_ERROR;
+ return NULL;
+ }
+ if ( 0 < status )
+ {
+ struct dirent* biggerdir = malloc(size);
+ if ( !biggerdir )
+ {
+ dir->flags |= _DIR_ERROR;
+ return NULL;
+ }
+ free(dir->entry);
+ dir->entry = biggerdir;
+ dir->entrysize = size;
+ return readdir(dir);
+ }
+
+ dir->flags &= ~_DIR_ERROR;
+
+ if ( !dir->entry->d_name[0] )
+ {
+ dir->flags |= _DIR_EOF;
+ return NULL;
+ }
+
+ return dir->entry;
+}
+
+int closedir(DIR* dir)
+{
+ int result = (dir->close_func) ? dir->close_func(dir->user) : 0;
+ dunregister(dir);
+ free(dir->entry);
+ if ( dir->free_func ) { dir->free_func(dir); }
+ return result;
+}
+
+void rewinddir(DIR* dir)
+{
+ if ( dir->rewind_func ) { dir->rewind_func(dir->user); }
+ dir->flags &= ~_DIR_EOF;
+}
+
+int dirfd(DIR* dir)
+{
+ if ( !dir->fd_func ) { errno = EBADF; return 0; }
+
+ return dir->fd_func(dir->user);
+}
+
+void dclearerr(DIR* dir)
+{
+ dir->flags &= ~_DIR_ERROR;
+}
+
+int derror(DIR* dir)
+{
+ return dir->flags & _DIR_ERROR;
+}
+
+int deof(DIR* dir)
+{
+ return dir->flags & _DIR_EOF;
+}
+
+static void dfreedir(DIR* dir)
+{
+ free(dir);
+}
+
+DIR* dnewdir(void)
+{
+ DIR* dir = (DIR*) calloc(sizeof(DIR), 1);
+ if ( !dir ) { return NULL; }
+ dir->flags = 0;
+ dir->free_func = dfreedir;
+ dregister(dir);
+ return dir;
+}
+
+int dcloseall(void)
+{
+ int result = 0;
+ while ( firstdir ) { result |= closedir(firstdir); }
+ return (result) ? EOF : 0;
+}
+
diff --git a/libmaxsi/c/fddir-sortix.c b/libmaxsi/c/fddir-sortix.c
new file mode 100644
index 00000000..a20b3bcc
--- /dev/null
+++ b/libmaxsi/c/fddir-sortix.c
@@ -0,0 +1,139 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of LibMaxsi.
+
+ LibMaxsi is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ LibMaxsi 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 Lesser General Public License for
+ more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with LibMaxsi. If not, see .
+
+ fddir-sortix.c
+ Handles the file descriptor backend for the DIR* API on Sortix.
+
+******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef struct fddir_sortix_struct
+{
+ struct sortix_dirent* dirent;
+ struct sortix_dirent* current;
+ size_t direntsize;
+ int fd;
+} fddir_sortix_t;
+
+int fddir_sortix_readents(fddir_sortix_t* info)
+{
+ if ( !info->dirent )
+ {
+ // Allocate a buffer of at least sizeof(sortix_dirent).
+ info->direntsize = sizeof(struct sortix_dirent) + 4UL;
+ info->dirent = malloc(info->direntsize);
+ if ( !info->dirent ) { return -1; }
+ }
+
+ if ( readdirents(info->fd, info->dirent, info->direntsize) )
+ {
+ if ( errno != ERANGE ) { return -1; }
+ size_t newdirentsize = info->dirent->d_namelen;
+ struct sortix_dirent* newdirent = malloc(newdirentsize);
+ if ( !newdirent ) { return -1; }
+ free(info->dirent);
+ info->dirent = newdirent;
+ info->direntsize = newdirentsize;
+ return fddir_sortix_readents(info);
+ }
+
+ return 0;
+}
+
+int fddir_sortix_read(void* user, struct dirent* dirent, size_t* size)
+{
+ fddir_sortix_t* info = (fddir_sortix_t*) user;
+ if ( !info->current )
+ {
+ if ( fddir_sortix_readents(info) ) { return -1; }
+ info->current = info->dirent;
+ }
+
+ size_t provided = (user) ? *size : 0;
+ size_t needed = sizeof(struct dirent) + info->current->d_namelen + 1;
+ *size = needed;
+ if ( provided < needed ) { return 1; }
+
+ strcpy(dirent->d_name, info->current->d_name);
+
+ info->current = info->current->d_next;
+
+ return 0;
+}
+
+int fddir_sortix_rewind(void* user)
+{
+ fddir_sortix_t* info = (fddir_sortix_t*) user;
+ return lseek(info->fd, SEEK_SET, 0);
+}
+
+int fddir_sortix_fd(void* user)
+{
+ fddir_sortix_t* info = (fddir_sortix_t*) user;
+ return info->fd;
+}
+
+int fddir_sortix_close(void* user)
+{
+ fddir_sortix_t* info = (fddir_sortix_t*) user;
+ close(info->fd);
+ free(info->dirent);
+ free(info);
+}
+
+DIR* fdopendir(int fd)
+{
+ fddir_sortix_t* info = calloc(sizeof(fddir_sortix_t), 1);
+ if ( !info ) { return NULL; }
+
+ DIR* dir = dnewdir();
+ if ( !dir ) { free(info); return NULL; }
+
+ // TODO: Possibly set O_CLOEXEC on fd, as that's what GNU/Linux does.
+
+ info->fd = fd;
+
+ dir->read_func = fddir_sortix_read;
+ dir->rewind_func = fddir_sortix_rewind;
+ dir->fd_func = fddir_sortix_fd;
+ dir->close_func = fddir_sortix_close;
+ dir->user = (void*) info;
+
+ return dir;
+}
+
+DIR* opendir(const char* path)
+{
+ // TODO: POSIX says we should use O_CLOEXEC here. That seems quite hacky to
+ // me. Is that stupid? If so, I'll leave it out.
+ int fd = open(path, O_SEARCH | O_DIRECTORY);
+ if ( fd < 0 ) { return NULL; }
+ DIR* dir = fdopendir(fd);
+ if ( !dir ) { close(fd); return NULL; }
+ return dir;
+}
+
diff --git a/libmaxsi/c/hsrc/dirent.h b/libmaxsi/c/hsrc/dirent.h
new file mode 100644
index 00000000..12acf7f2
--- /dev/null
+++ b/libmaxsi/c/hsrc/dirent.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+
+ COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
+
+ This file is part of LibMaxsi.
+
+ LibMaxsi is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ LibMaxsi 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 Lesser General Public License for
+ more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with LibMaxsi. If not, see .
+
+ dirent.h
+ Format of directory entries.
+
+******************************************************************************/
+
+#ifndef _DIRENT_H
+#define _DIRENT_H 1
+
+#include
+
+__BEGIN_DECLS
+
+@include(DIR.h)
+
+struct dirent
+{
+ char d_name[0];
+};
+
+int closedir(DIR* dir);
+int dirfd(DIR* dir);
+DIR* fdopendir(int fd);
+DIR* opendir(const char* path);
+struct dirent* readdir(DIR* dir);
+void rewinddir(DIR* dir);
+
+#ifdef SORTIX_EXTENSIONS
+void dregister(DIR* dir);
+void dunregister(DIR* dir);
+DIR* dnewdir(void);
+int dcloseall(void);
+void dclearerr(DIR* dir);
+int derror(DIR* dir);
+int deof(DIR* dif);
+#endif
+
+__END_DECLS
+
+#endif
diff --git a/libmaxsi/process.cpp b/libmaxsi/process.cpp
index a95bda45..53b49a32 100644
--- a/libmaxsi/process.cpp
+++ b/libmaxsi/process.cpp
@@ -26,6 +26,7 @@
#include "syscall.h"
#include "process.h"
#include
+#include
namespace Maxsi
{
@@ -58,6 +59,7 @@ namespace Maxsi
DUAL_FUNCTION(void, exit, Exit, (int status))
{
+ dcloseall();
fcloseall();
_exit(status);
}