diff -r d1ea9b3f543e -r 13e2037ebc79 misc/physfs/src/platform_posix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/physfs/src/platform_posix.c Sat Oct 20 00:20:39 2012 +0400 @@ -0,0 +1,479 @@ +/* + * Posix-esque support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* !!! FIXME: check for EINTR? */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_POSIX + +#include +#include +#include +#include +#include +#include +#include +#include + +#if ((!defined PHYSFS_NO_THREAD_SUPPORT) && (!defined PHYSFS_PLATFORM_BEOS)) +#include +#endif + +#include "physfs_internal.h" + + +static PHYSFS_ErrorCode errcodeFromErrnoError(const int err) +{ + switch (err) + { + case 0: return PHYSFS_ERR_OK; + case EACCES: return PHYSFS_ERR_PERMISSION; + case EPERM: return PHYSFS_ERR_PERMISSION; + case EDQUOT: return PHYSFS_ERR_NO_SPACE; + case EIO: return PHYSFS_ERR_IO; + case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP; + case EMLINK: return PHYSFS_ERR_NO_SPACE; + case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME; + case ENOENT: return PHYSFS_ERR_NO_SUCH_PATH; + case ENOSPC: return PHYSFS_ERR_NO_SPACE; + case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH; + case EISDIR: return PHYSFS_ERR_NOT_A_FILE; + case EROFS: return PHYSFS_ERR_READ_ONLY; + case ETXTBSY: return PHYSFS_ERR_BUSY; + case EBUSY: return PHYSFS_ERR_BUSY; + case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY; + case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY; + default: return PHYSFS_ERR_OS_ERROR; + } /* switch */ +} /* errcodeFromErrnoError */ + + +static inline PHYSFS_ErrorCode errcodeFromErrno(void) +{ + return errcodeFromErrnoError(errno); +} /* errcodeFromErrno */ + + +static char *getUserDirByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0')) + { + const size_t dlen = strlen(pw->pw_dir); + const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0; + retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep); + if (retval != NULL) + { + strcpy(retval, pw->pw_dir); + if (add_dirsep) + { + retval[dlen] = '/'; + retval[dlen+1] = '\0'; + } /* if */ + } /* if */ + } /* if */ + + return retval; +} /* getUserDirByUID */ + + +char *__PHYSFS_platformCalcUserDir(void) +{ + char *retval = NULL; + char *envr = getenv("HOME"); + + /* if the environment variable was set, make sure it's really a dir. */ + if (envr != NULL) + { + struct stat statbuf; + if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode))) + { + const size_t envrlen = strlen(envr); + const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0; + retval = allocator.Malloc(envrlen + 1 + add_dirsep); + if (retval) + { + strcpy(retval, envr); + if (add_dirsep) + { + retval[envrlen] = '/'; + retval[envrlen+1] = '\0'; + } /* if */ + } /* if */ + } /* if */ + } /* if */ + + if (retval == NULL) + retval = getUserDirByUID(); + + return retval; +} /* __PHYSFS_platformCalcUserDir */ + + +void __PHYSFS_platformEnumerateFiles(const char *dirname, + int omitSymLinks, + PHYSFS_EnumFilesCallback callback, + const char *origdir, + void *callbackdata) +{ + DIR *dir; + struct dirent *ent; + int bufsize = 0; + char *buf = NULL; + int dlen = 0; + + if (omitSymLinks) /* !!! FIXME: this malloc sucks. */ + { + dlen = strlen(dirname); + bufsize = dlen + 256; + buf = (char *) allocator.Malloc(bufsize); + if (buf == NULL) + return; + strcpy(buf, dirname); + if (buf[dlen - 1] != '/') + { + buf[dlen++] = '/'; + buf[dlen] = '\0'; + } /* if */ + } /* if */ + + errno = 0; + dir = opendir(dirname); + if (dir == NULL) + { + allocator.Free(buf); + return; + } /* if */ + + while ((ent = readdir(dir)) != NULL) + { + if (strcmp(ent->d_name, ".") == 0) + continue; + + if (strcmp(ent->d_name, "..") == 0) + continue; + + if (omitSymLinks) + { + PHYSFS_Stat statbuf; + int exists = 0; + char *p; + int len = strlen(ent->d_name) + dlen + 1; + if (len > bufsize) + { + p = (char *) allocator.Realloc(buf, len); + if (p == NULL) + continue; + buf = p; + bufsize = len; + } /* if */ + + strcpy(buf + dlen, ent->d_name); + + if (!__PHYSFS_platformStat(buf, &exists, &statbuf)) + continue; + else if (!exists) + continue; /* probably can't happen, but just in case. */ + else if (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK) + continue; + } /* if */ + + callback(callbackdata, origdir, ent->d_name); + } /* while */ + + allocator.Free(buf); + closedir(dir); +} /* __PHYSFS_platformEnumerateFiles */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + const int rc = mkdir(path, S_IRWXU); + BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformMkDir */ + + +static void *doOpen(const char *filename, int mode) +{ + const int appending = (mode & O_APPEND); + int fd; + int *retval; + errno = 0; + + /* O_APPEND doesn't actually behave as we'd like. */ + mode &= ~O_APPEND; + + fd = open(filename, mode, S_IRUSR | S_IWUSR); + BAIL_IF_MACRO(fd < 0, errcodeFromErrno(), NULL); + + if (appending) + { + if (lseek(fd, 0, SEEK_END) < 0) + { + const int err = errno; + close(fd); + BAIL_MACRO(errcodeFromErrnoError(err), NULL); + } /* if */ + } /* if */ + + retval = (int *) allocator.Malloc(sizeof (int)); + if (!retval) + { + close(fd); + BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + *retval = fd; + return ((void *) retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return doOpen(filename, O_RDONLY); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint64 len) +{ + const int fd = *((int *) opaque); + ssize_t rc = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + rc = read(fd, buffer, (size_t) len); + BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), -1); + assert(rc >= 0); + assert(rc <= len); + return (PHYSFS_sint64) rc; +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint64 len) +{ + const int fd = *((int *) opaque); + ssize_t rc = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + rc = write(fd, (void *) buffer, (size_t) len); + BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), rc); + assert(rc >= 0); + assert(rc <= len); + return (PHYSFS_sint64) rc; +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + const int fd = *((int *) opaque); + const int rc = lseek(fd, (off_t) pos, SEEK_SET); + BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + const int fd = *((int *) opaque); + PHYSFS_sint64 retval; + retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); + BAIL_IF_MACRO(retval == -1, errcodeFromErrno(), -1); + return retval; +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + const int fd = *((int *) opaque); + struct stat statbuf; + BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1); + return ((PHYSFS_sint64) statbuf.st_size); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + const int fd = *((int *) opaque); + BAIL_IF_MACRO(fsync(fd) == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformFlush */ + + +void __PHYSFS_platformClose(void *opaque) +{ + const int fd = *((int *) opaque); + (void) close(fd); /* we don't check this. You should have used flush! */ + allocator.Free(opaque); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + BAIL_IF_MACRO(remove(path) == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformDelete */ + + +int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st) +{ + struct stat statbuf; + + if (lstat(filename, &statbuf) == -1) + { + *exists = (errno != ENOENT); + BAIL_MACRO(errcodeFromErrno(), 0); + } /* if */ + + *exists = 1; + + if (S_ISREG(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_REGULAR; + st->filesize = statbuf.st_size; + } /* if */ + + else if(S_ISDIR(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filesize = 0; + } /* else if */ + + else + { + st->filetype = PHYSFS_FILETYPE_OTHER; + st->filesize = statbuf.st_size; + } /* else */ + + st->modtime = statbuf.st_mtime; + st->createtime = statbuf.st_ctime; + st->accesstime = statbuf.st_atime; + + /* !!! FIXME: maybe we should just report full permissions? */ + st->readonly = access(filename, W_OK); + return 1; +} /* __PHYSFS_platformStat */ + + +#ifndef PHYSFS_PLATFORM_BEOS /* BeOS has its own code in platform_beos.cpp */ +#if (defined PHYSFS_NO_THREAD_SUPPORT) + +void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); } +void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); } +void __PHYSFS_platformDestroyMutex(void *mutex) {} +int __PHYSFS_platformGrabMutex(void *mutex) { return 1; } +void __PHYSFS_platformReleaseMutex(void *mutex) {} + +#else + +typedef struct +{ + pthread_mutex_t mutex; + pthread_t owner; + PHYSFS_uint32 count; +} PthreadMutex; + + +void *__PHYSFS_platformGetThreadID(void) +{ + return ( (void *) ((size_t) pthread_self()) ); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + int rc; + PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex)); + BAIL_IF_MACRO(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + rc = pthread_mutex_init(&m->mutex, NULL); + if (rc != 0) + { + allocator.Free(m); + BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL); + } /* if */ + + m->count = 0; + m->owner = (pthread_t) 0xDEADBEEF; + return ((void *) m); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + + /* Destroying a locked mutex is a bug, but we'll try to be helpful. */ + if ((m->owner == pthread_self()) && (m->count > 0)) + pthread_mutex_unlock(&m->mutex); + + pthread_mutex_destroy(&m->mutex); + allocator.Free(m); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + pthread_t tid = pthread_self(); + if (m->owner != tid) + { + if (pthread_mutex_lock(&m->mutex) != 0) + return 0; + m->owner = tid; + } /* if */ + + m->count++; + return 1; +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + assert(m->owner == pthread_self()); /* catch programming errors. */ + assert(m->count > 0); /* catch programming errors. */ + if (m->owner == pthread_self()) + { + if (--m->count == 0) + { + m->owner = (pthread_t) 0xDEADBEEF; + pthread_mutex_unlock(&m->mutex); + } /* if */ + } /* if */ +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* !PHYSFS_NO_THREAD_SUPPORT */ +#endif /* !PHYSFS_PLATFORM_BEOS */ + +#endif /* PHYSFS_PLATFORM_POSIX */ + +/* end of posix.c ... */ +