summaryrefslogtreecommitdiff
path: root/src/libs/uio/stdio/stdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio/stdio/stdio.c')
-rw-r--r--src/libs/uio/stdio/stdio.c854
1 files changed, 854 insertions, 0 deletions
diff --git a/src/libs/uio/stdio/stdio.c b/src/libs/uio/stdio/stdio.c
new file mode 100644
index 0000000..a4421d1
--- /dev/null
+++ b/src/libs/uio/stdio/stdio.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The GPDir structures and functions are used for caching only.
+
+#ifdef __svr4__
+# define _POSIX_PTHREAD_SEMANTICS
+ // For the POSIX variant of readdir_r()
+#endif
+
+#include "./stdio.h"
+
+#ifdef WIN32
+# include <io.h>
+#else
+# include <sys/stat.h>
+# include <unistd.h>
+# include <dirent.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "../uioport.h"
+#include "../paths.h"
+#include "../mem.h"
+#include "../physical.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+static inline uio_GPFile *stdio_addFile(uio_GPDir *gPDir,
+ const char *fileName);
+static inline uio_GPDir *stdio_addDir(uio_GPDir *gPDir, const char *dirName);
+static char *stdio_getPath(uio_GPDir *gPDir);
+static stdio_GPDirData *stdio_GPDirData_new(char *name, char *cachedPath,
+ uio_GPDir *upDir);
+static void stdio_GPDirData_delete(stdio_GPDirData *gPDirData);
+static inline stdio_GPDirData *stdio_GPDirData_alloc(void);
+static inline void stdio_GPDirData_free(stdio_GPDirData *gPDirData);
+static inline stdio_EntriesIterator *stdio_EntriesIterator_alloc(void);
+static inline void stdio_EntriesIterator_free(
+ stdio_EntriesIterator *iterator);
+
+uio_FileSystemHandler stdio_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ stdio_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ stdio_access,
+ /* .close = */ stdio_close,
+ /* .fstat = */ stdio_fstat,
+ /* .stat = */ stdio_stat,
+ /* .mkdir = */ stdio_mkdir,
+ /* .open = */ stdio_open,
+ /* .read = */ stdio_read,
+ /* .rename = */ stdio_rename,
+ /* .rmdir = */ stdio_rmdir,
+ /* .seek = */ stdio_seek,
+ /* .write = */ stdio_write,
+ /* .unlink = */ stdio_unlink,
+
+ /* .openEntries = */ stdio_openEntries,
+ /* .readEntries = */ stdio_readEntries,
+ /* .closeEntries = */ stdio_closeEntries,
+
+ /* .getPDirEntryHandle = */ stdio_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations stdio_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ stdio_GPDirData_delete,
+ /* .deleteGPFileExtra = */ NULL,
+};
+
+
+void
+stdio_close(uio_Handle *handle) {
+ int fd;
+ int result;
+
+ fd = handle->native->fd;
+ uio_free(handle->native);
+
+ while (1) {
+ result = close(fd);
+ if (result == 0)
+ break;
+ if (errno != EINTR) {
+ fprintf(stderr, "Warning: Error while closing socket: %s\n",
+ strerror(errno));
+ break;
+ }
+ }
+}
+
+int
+stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = access(path, mode);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+int
+stdio_fstat(uio_Handle *handle, struct stat *statBuf) {
+ return fstat(handle->native->fd, statBuf);
+}
+
+int
+stdio_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = stat(path, statBuf);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+uio_PDirHandle *
+stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name, mode_t mode) {
+ char *path;
+ uio_GPDir *newGPDir;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (MKDIR(path, mode) == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ uio_free(path);
+
+ newGPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(newGPDir);
+ return uio_PDirHandle_new(pDirHandle->pRoot, newGPDir);
+}
+
+/*
+ * Function name: stdio_open
+ * Description: open a file from a normal stdio environment
+ * Arguments: gPDir - the dir where to open the file
+ * file - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, for use in functions accessing the opened file.
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode) {
+ stdio_Handle *handle;
+ char *path;
+ int fd;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), file);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ fd = open(path, flags, mode);
+ if (fd == -1) {
+ int save_errno;
+
+ save_errno = errno;
+ uio_free(path);
+ errno = save_errno;
+ return NULL;
+ }
+ uio_free(path);
+
+#if 0
+ if (flags & O_CREAT) {
+ if (uio_GPDir_getGPDirEntry(pDirHandle->extra, file) == NULL)
+ stdio_addFile(pDirHandle->extra, file);
+ }
+#endif
+
+ handle = uio_malloc(sizeof (stdio_Handle));
+ handle->fd = fd;
+
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+stdio_read(uio_Handle *handle, void *buf, size_t count) {
+ return read(handle->native->fd, buf, count);
+}
+
+int
+stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName,
+ uio_PDirHandle *newPDirHandle, const char *newName) {
+ char *newPath, *oldPath;
+ int result;
+
+ oldPath = joinPaths(stdio_getPath(oldPDirHandle->extra), oldName);
+ if (oldPath == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ newPath = joinPaths(stdio_getPath(newPDirHandle->extra), newName);
+ if (newPath == NULL) {
+ // errno is set
+ uio_free(oldPath);
+ return -1;
+ }
+
+ result = rename(oldPath, newPath);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(oldPath);
+ uio_free(newPath);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(oldPath);
+ uio_free(newPath);
+
+ {
+ // update the GPDir structure
+ uio_GPDirEntry *entry;
+
+ // TODO: add locking
+ entry = uio_GPDir_getGPDirEntry(oldPDirHandle->extra, oldName);
+ if (entry != NULL) {
+ uio_GPDirEntries_remove(oldPDirHandle->extra->entries, oldName);
+ uio_GPDirEntries_add(newPDirHandle->extra->entries, newName,
+ entry);
+ }
+ }
+
+ return result;
+}
+
+int
+stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = rmdir(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeSubDir(pDirHandle->extra, name);
+
+ return result;
+}
+
+off_t
+stdio_seek(uio_Handle *handle, off_t offset, int whence) {
+ return lseek(handle->native->fd, offset, whence);
+}
+
+ssize_t
+stdio_write(uio_Handle *handle, const void *buf, size_t count) {
+ return write(handle->native->fd, buf, count);
+}
+
+int
+stdio_unlink(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = unlink(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeFile(pDirHandle->extra, name);
+
+ return result;
+}
+
+uio_PDirEntryHandle *
+stdio_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, const char *name) {
+ uio_PDirEntryHandle *result;
+ const char *pathUpTo;
+ char *path;
+ struct stat statBuf;
+#ifdef HAVE_DRIVE_LETTERS
+ char driveName[3];
+#endif /* HAVE_DRIVE_LETTERS */
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Top dir. Contains only drive letters and UNC \\server\share
+ // parts.
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(name[0]) && name[1] == ':' && name[2] == '\0') {
+ driveName[0] = tolower(name[0]);
+ driveName[1] = ':';
+ driveName[2] = '\0';
+ name = driveName;
+ } else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ {
+ size_t uncLen;
+
+ uncLen = uio_skipUNCServerShare(name);
+ if (name[uncLen] != '\0') {
+ // 'name' contains neither a drive letter, nor the
+ // first part of a UNC path.
+ return NULL;
+ }
+ }
+#else /* !defined(HAVE_UNC_PATHS) */
+ {
+ // Make sure that there is an 'else' case if HAVE_DRIVE_LETTERS
+ // is defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ result = uio_GPDir_getPDirEntryHandle(pDirHandle, name);
+ if (result != NULL)
+ return result;
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Need to create a 'directory' for the drive letter or UNC
+ // "\\server\share" part.
+ // It's no problem if we happen to create a dir for a non-existing
+ // drive. It should just produce an empty dir.
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ pathUpTo = stdio_getPath(pDirHandle->extra);
+ if (pathUpTo == NULL) {
+ // errno is set
+ return NULL;
+ }
+ path = joinPaths(pathUpTo, name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (stat(path, &statBuf) == -1) {
+#ifdef __SYMBIAN32__
+ // XXX: HACK: If we don't have access to a directory, we can still
+ // have access to the underlying entries. We don't actually know
+ // whether the entry is a directory, but I know of no way to find
+ // out. We just pretend that it is; worst case, a file which we can't
+ // access shows up as a directory which we can't access.
+ if (errno == EACCES) {
+ statBuf.st_mode = S_IFDIR;
+ // Fake a directory; the other fields of the stat
+ // structure are unused.
+ } else
+#endif
+ {
+ // errno is set.
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+ uio_free(path);
+
+ if (S_ISREG(statBuf.st_mode)) {
+ uio_GPFile *gPFile;
+
+ gPFile = stdio_addFile(pDirHandle->extra, name);
+ uio_GPFile_ref(gPFile);
+ return (uio_PDirEntryHandle *) uio_PFileHandle_new(
+ pDirHandle->pRoot, gPFile);
+ } else if (S_ISDIR(statBuf.st_mode)) {
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Attempt to access '%s' from '%s', "
+ "which is not a regular file, nor a directory.\n", name,
+ pathUpTo);
+#endif
+ return NULL;
+ }
+}
+
+uio_PRoot *
+stdio_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ stdio_GPDirData *extra;
+
+ assert (handle == NULL);
+ extra = stdio_GPDirData_new(
+ uio_strdup("") /* name */,
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Full paths start with a drive letter or \\server\share
+ uio_strdup("") /* cached path */,
+#else
+ uio_strdup("/") /* cached path */,
+#endif /* HAVE_DRIVE_LETTERS */
+ NULL /* parent dir */);
+
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_STDIO), flags,
+ &stdio_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, extra, 0);
+
+ uio_GPDir_setComplete(result->rootDir->extra, true);
+
+ return result;
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ char path[PATH_MAX];
+ char *pathEnd;
+ size_t dirPathLen;
+ stdio_EntriesIterator *iterator;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirPathLen = strlen(dirPath);
+ if (dirPathLen > PATH_MAX - 3) {
+ // dirPath ++ '/' ++ '*' ++ '\0'
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ memcpy(path, dirPath, dirPathLen);
+ pathEnd = path + dirPathLen;
+ pathEnd[0] = '/';
+ pathEnd[1] = '*';
+ pathEnd[2] = '\0';
+ iterator = stdio_EntriesIterator_new(0);
+ iterator->dirHandle = _findfirst(path, &iterator->findData);
+ if (iterator->dirHandle == 1) {
+ if (errno != ENOENT) {
+ stdio_EntriesIterator_delete(iterator);
+ return NULL;
+ }
+ iterator->status = 1;
+ } else
+ iterator->status = 0;
+ return iterator;
+}
+#endif
+
+#ifndef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ DIR *dirHandle;
+ stdio_EntriesIterator *result;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirHandle = opendir(dirPath);
+ if (dirHandle == NULL) {
+ // errno is set;
+ return NULL;
+ }
+
+ result = stdio_EntriesIterator_new(dirHandle);
+ result->status = readdir_r(dirHandle, result->direntBuffer,
+ &result->entry);
+#ifndef WIN32
+# ifdef DEBUG
+ if (result->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(result->status));
+ }
+# endif
+#endif
+ return result;
+}
+#endif
+
+// the start of 'buf' will be filled with pointers to strings
+// those strings are stored elsewhere in buf.
+// The function returns the number of strings passed along, or -1 for error.
+// If there are no more entries, the last pointer will be NULL.
+// (this pointer counts towards the return value)
+int
+stdio_readEntries(stdio_EntriesIterator **iteratorPtr,
+ char *buf, size_t len) {
+ char *end;
+ char **start;
+ int num;
+ const char *name;
+ size_t nameLen;
+ stdio_EntriesIterator *iterator;
+
+ iterator = *iteratorPtr;
+
+ // buf will be filled like this:
+ // The start of buf will contain pointers to char *,
+ // the end will contain the actual char[] that those pointers point to.
+ // The start and the end will grow towards eachother.
+ start = (char **) buf;
+ end = buf + len;
+ num = 0;
+#ifdef WIN32
+ for (; iterator->status == 0;
+ iterator->status = _findnext(iterator->dirHandle,
+ &iterator->findData))
+#else
+ for (; iterator->status == 0 && iterator->entry != NULL;
+ iterator->status = readdir_r(iterator->dirHandle,
+ iterator->direntBuffer, &iterator->entry))
+#endif
+ {
+#ifdef WIN32
+ name = iterator->findData.name;
+#else
+ name = iterator->entry->d_name;
+#endif
+ if (name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0'))) {
+ // skip directories "." and ".."
+ continue;
+ }
+ nameLen = strlen(name) + 1;
+
+ // Does this work with systems that need memory access to be
+ // aligned on a certain number of bytes?
+ if ((size_t) (sizeof (char *) + nameLen) >
+ (size_t) (end - (char *) start)) {
+ // Not enough room to fit the pointer (at the beginning) and
+ // the string (at the end).
+ return num;
+ }
+ end -= nameLen;
+ memcpy(end, name, nameLen);
+ *start = end;
+ start++;
+ num++;
+ }
+#ifndef WIN32
+# ifdef DEBUG
+ if (iterator->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(iterator->status));
+ }
+# endif
+#endif
+ if (sizeof (char *) > (size_t) (end - (char *) start)) {
+ // not enough room to fit the NULL pointer.
+ // It will have to be reported seperately the next time.
+ return num;
+ }
+ *start = NULL;
+ num++;
+ return num;
+}
+
+void
+stdio_closeEntries(stdio_EntriesIterator *iterator) {
+#ifdef WIN32
+ _findclose(iterator->dirHandle);
+#else
+ closedir(iterator->dirHandle);
+#endif
+ stdio_EntriesIterator_delete(iterator);
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(long dirHandle) {
+ stdio_EntriesIterator *result;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+ return result;
+}
+#else
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(DIR *dirHandle) {
+ stdio_EntriesIterator *result;
+ size_t bufferSize;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+
+ // Linux's and FreeBSD's struct dirent are defined with a
+ // maximum d_name field (NAME_MAX).
+ // However, POSIX doesn't require this, and in fact
+ // at least QNX defines struct dirent with an empty d_name field.
+ // Solaris defineds it with a d_name field of length 1.
+ // This should take care of it:
+ bufferSize = sizeof (struct dirent)
+ - sizeof (((struct dirent *) 0)->d_name) + (NAME_MAX + 1);
+ // Take the length of the dirent structure as it is defined,
+ // subtract the length of the d_name field, and add the length
+ // of the maximum length d_name field (NAME_MAX plus 1 for
+ // the '\0').
+ // XXX: Could this give problems with weird alignments?
+ result->direntBuffer = uio_malloc(bufferSize);
+ return result;
+}
+#endif
+
+void
+stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator) {
+#ifndef WIN32
+ uio_free(iterator->direntBuffer);
+#endif
+ stdio_EntriesIterator_free(iterator);
+}
+
+static inline stdio_EntriesIterator *
+stdio_EntriesIterator_alloc(void) {
+ return uio_malloc(sizeof (stdio_EntriesIterator));
+}
+
+static inline void
+stdio_EntriesIterator_free(stdio_EntriesIterator *iterator) {
+ uio_free(iterator);
+}
+
+static inline uio_GPFile *
+stdio_addFile(uio_GPDir *gPDir, const char *fileName) {
+ uio_GPFile *file;
+
+ file = uio_GPFile_new(gPDir->pRoot, NULL,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, fileName, file);
+ return file;
+}
+
+// called by fillGPDir when a subdir is found
+static inline uio_GPDir *
+stdio_addDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDir *subDir;
+
+ subDir = uio_GPDir_prepareSubDir(gPDir, dirName);
+ if (subDir->extra == NULL) {
+ // It's a new dir, we'll need to add our own data.
+ uio_GPDir_ref(gPDir);
+ subDir->extra = stdio_GPDirData_new(uio_strdup(dirName),
+ NULL, gPDir);
+ uio_GPDir_setComplete(subDir, true);
+ // fillPDir should not be called.
+ }
+ uio_GPDir_commitSubDir(gPDir, dirName, subDir);
+ return subDir;
+}
+
+// returns a pointer to gPDir->extra->cachedPath
+// pointer should not be stored, the memory it points to can be freed
+// lateron. TODO: not threadsafe.
+static char *
+stdio_getPath(uio_GPDir *gPDir) {
+ if (gPDir->extra->cachedPath == NULL) {
+ char *upPath;
+ size_t upPathLen, nameLen;
+
+ if (gPDir->extra->upDir == NULL) {
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Drive letter or UNC \\server\share still needs to follow.
+ gPDir->extra->cachedPath = uio_malloc(1);
+ gPDir->extra->cachedPath[0] = '\0';
+#else
+ gPDir->extra->cachedPath = uio_malloc(2);
+ gPDir->extra->cachedPath[0] = '/';
+ gPDir->extra->cachedPath[1] = '\0';
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ return gPDir->extra->cachedPath;
+ }
+
+ upPath = stdio_getPath(gPDir->extra->upDir);
+ if (upPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (upPath[0] == '\0') {
+ // The up dir is the root dir. Directly below the root dir are
+ // only dirs for drive letters and UNC \\share\server parts.
+ // No '/' needs to be attached.
+ gPDir->extra->cachedPath = uio_strdup(gPDir->extra->name);
+ return gPDir->extra->cachedPath;
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ upPathLen = strlen(upPath);
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS)
+ if (upPath[upPathLen - 1] == '/') {
+ // should only happen for "/"
+ upPathLen--;
+ }
+#endif /* !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS) */
+ nameLen = strlen(gPDir->extra->name);
+ if (upPathLen + nameLen + 1 >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ gPDir->extra->cachedPath = uio_malloc(upPathLen + nameLen + 2);
+ memcpy(gPDir->extra->cachedPath, upPath, upPathLen);
+ gPDir->extra->cachedPath[upPathLen] = '/';
+ memcpy(gPDir->extra->cachedPath + upPathLen + 1,
+ gPDir->extra->name, nameLen);
+ gPDir->extra->cachedPath[upPathLen + nameLen + 1] = '\0';
+ }
+ return gPDir->extra->cachedPath;
+}
+
+static stdio_GPDirData *
+stdio_GPDirData_new(char *name, char *cachedPath, uio_GPDir *upDir) {
+ stdio_GPDirData *result;
+
+ result = stdio_GPDirData_alloc();
+ result->name = name;
+ result->cachedPath = cachedPath;
+ result->upDir = upDir;
+ return result;
+}
+
+static void
+stdio_GPDirData_delete(stdio_GPDirData *gPDirData) {
+ if (gPDirData->upDir != NULL)
+ uio_GPDir_unref(gPDirData->upDir);
+ stdio_GPDirData_free(gPDirData);
+}
+
+static inline stdio_GPDirData *
+stdio_GPDirData_alloc(void) {
+ stdio_GPDirData *result;
+
+ result = uio_malloc(sizeof (stdio_GPDirData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(stdio_GPDirData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+stdio_GPDirData_free(stdio_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(stdio_GPDirData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData->name);
+ if (gPDirData->cachedPath != NULL)
+ uio_free(gPDirData->cachedPath);
+ uio_free(gPDirData);
+}
+
+