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); }