/* * 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. */ #define __PHYSICSFS_INTERNAL__ #include "physfs_platforms.h" #ifdef PHYSFS_PLATFORM_POSIX #if (defined __STRICT_ANSI__) #define __PHYSFS_DOING_STRICT_ANSI__ #endif /* * We cheat a little: I want the symlink version of stat() (lstat), and * GCC/Linux will not declare it if compiled with the -ansi flag. * If you are really lacking symlink support on your platform, * you should #define __PHYSFS_NO_SYMLINKS__ before compiling this * file. That will open a security hole, though, if you really DO have * symlinks on your platform; it renders PHYSFS_permitSymbolicLinks(0) * useless, since every symlink will be reported as a regular file/dir. */ #if (defined __PHYSFS_DOING_STRICT_ANSI__) #undef __STRICT_ANSI__ #endif #include #if (defined __PHYSFS_DOING_STRICT_ANSI__) #define __STRICT_ANSI__ #endif #include #include #include #include #include #include #include #include #include #include #ifdef PHYSFS_HAVE_LLSEEK #include #endif #include "physfs_internal.h" char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname) { const char *envr = getenv(varname); char *retval = NULL; if (envr != NULL) { retval = (char *) allocator.Malloc(strlen(envr) + 1); if (retval != NULL) strcpy(retval, envr); } /* if */ return(retval); } /* __PHYSFS_platformCopyEnvironmentVariable */ static char *getUserNameByUID(void) { uid_t uid = getuid(); struct passwd *pw; char *retval = NULL; pw = getpwuid(uid); if ((pw != NULL) && (pw->pw_name != NULL)) { retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1); if (retval != NULL) strcpy(retval, pw->pw_name); } /* if */ return(retval); } /* getUserNameByUID */ static char *getUserDirByUID(void) { uid_t uid = getuid(); struct passwd *pw; char *retval = NULL; pw = getpwuid(uid); if ((pw != NULL) && (pw->pw_dir != NULL)) { retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1); if (retval != NULL) strcpy(retval, pw->pw_dir); } /* if */ return(retval); } /* getUserDirByUID */ char *__PHYSFS_platformGetUserName(void) { char *retval = getUserNameByUID(); if (retval == NULL) retval = __PHYSFS_platformCopyEnvironmentVariable("USER"); return(retval); } /* __PHYSFS_platformGetUserName */ char *__PHYSFS_platformGetUserDir(void) { char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME"); if (retval == NULL) retval = getUserDirByUID(); return(retval); } /* __PHYSFS_platformGetUserDir */ /* -ansi and -pedantic flags prevent use of strcasecmp() on Linux. */ int __PHYSFS_platformStricmp(const char *x, const char *y) { int ux, uy; do { ux = toupper((int) *x); uy = toupper((int) *y); if (ux != uy) return((ux > uy) ? 1 : -1); x++; y++; } while ((ux) && (uy)); return(0); } /* __PHYSFS_platformStricmp */ int __PHYSFS_platformStrnicmp(const char *x, const char *y, PHYSFS_uint32 len) { int ux, uy; if (!len) return(0); do { ux = toupper((int) *x); uy = toupper((int) *y); if (ux != uy) return((ux > uy) ? 1 : -1); x++; y++; len--; } while ((ux) && (uy) && (len)); return(0); } /* __PHYSFS_platformStrnicmp */ #if (defined __PHYSFS_NO_SYMLINKS__) #define doStat stat #else #define doStat lstat #endif int __PHYSFS_platformExists(const char *fname) { struct stat statbuf; BAIL_IF_MACRO(doStat(fname, &statbuf) == -1, strerror(errno), 0); return(1); } /* __PHYSFS_platformExists */ int __PHYSFS_platformIsSymLink(const char *fname) { #if (defined __PHYSFS_NO_SYMLINKS__) return(0); #else struct stat statbuf; BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0); return( (S_ISLNK(statbuf.st_mode)) ? 1 : 0 ); #endif } /* __PHYSFS_platformIsSymlink */ int __PHYSFS_platformIsDirectory(const char *fname) { struct stat statbuf; BAIL_IF_MACRO(stat(fname, &statbuf) == -1, strerror(errno), 0); return( (S_ISDIR(statbuf.st_mode)) ? 1 : 0 ); } /* __PHYSFS_platformIsDirectory */ char *__PHYSFS_platformCvtToDependent(const char *prepend, const char *dirName, const char *append) { int len = ((prepend) ? strlen(prepend) : 0) + ((append) ? strlen(append) : 0) + strlen(dirName) + 1; char *retval = (char *) allocator.Malloc(len); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); /* platform-independent notation is Unix-style already. :) */ if (prepend) strcpy(retval, prepend); else retval[0] = '\0'; strcat(retval, dirName); if (append) strcat(retval, append); return(retval); } /* __PHYSFS_platformCvtToDependent */ 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) { if (buf != 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) { 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_platformIsSymLink(buf)) continue; } /* if */ callback(callbackdata, origdir, ent->d_name); } /* while */ if (buf != NULL) allocator.Free(buf); closedir(dir); } /* __PHYSFS_platformEnumerateFiles */ char *__PHYSFS_platformCurrentDir(void) { int allocSize = 0; char *retval = NULL; char *ptr; do { allocSize += 100; ptr = (char *) allocator.Realloc(retval, allocSize); if (ptr == NULL) { if (retval != NULL) allocator.Free(retval); BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); } /* if */ retval = ptr; ptr = getcwd(retval, allocSize); } while (ptr == NULL && errno == ERANGE); if (ptr == NULL && errno) { /* * getcwd() failed for some reason, for example current * directory not existing. */ if (retval != NULL) allocator.Free(retval); BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); } /* if */ return(retval); } /* __PHYSFS_platformCurrentDir */ int __PHYSFS_platformMkDir(const char *path) { int rc; errno = 0; rc = mkdir(path, S_IRWXU); BAIL_IF_MACRO(rc == -1, strerror(errno), 0); return(1); } /* __PHYSFS_platformMkDir */ static void *doOpen(const char *filename, int mode) { int fd; int *retval; errno = 0; fd = open(filename, mode, S_IRUSR | S_IWUSR); BAIL_IF_MACRO(fd < 0, strerror(errno), NULL); retval = (int *) allocator.Malloc(sizeof (int)); if (retval == NULL) { close(fd); BAIL_MACRO(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_uint32 size, PHYSFS_uint32 count) { int fd = *((int *) opaque); int max = size * count; int rc = read(fd, buffer, max); BAIL_IF_MACRO(rc == -1, strerror(errno), rc); assert(rc <= max); if ((rc < max) && (size > 1)) lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ return(rc / size); } /* __PHYSFS_platformRead */ PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, PHYSFS_uint32 size, PHYSFS_uint32 count) { int fd = *((int *) opaque); int max = size * count; int rc = write(fd, (void *) buffer, max); BAIL_IF_MACRO(rc == -1, strerror(errno), rc); assert(rc <= max); if ((rc < max) && (size > 1)) lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */ return(rc / size); } /* __PHYSFS_platformWrite */ int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) { int fd = *((int *) opaque); #ifdef PHYSFS_HAVE_LLSEEK unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF); unsigned long offset_low = (pos & 0xFFFFFFFF); loff_t retoffset; int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET); BAIL_IF_MACRO(rc == -1, strerror(errno), 0); #else BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0); #endif return(1); } /* __PHYSFS_platformSeek */ PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) { int fd = *((int *) opaque); PHYSFS_sint64 retval; #ifdef PHYSFS_HAVE_LLSEEK loff_t retoffset; int rc = llseek(fd, 0, &retoffset, SEEK_CUR); BAIL_IF_MACRO(rc == -1, strerror(errno), -1); retval = (PHYSFS_sint64) retoffset; #else retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); BAIL_IF_MACRO(retval == -1, strerror(errno), -1); #endif return(retval); } /* __PHYSFS_platformTell */ PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) { int fd = *((int *) opaque); struct stat statbuf; BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1); return((PHYSFS_sint64) statbuf.st_size); } /* __PHYSFS_platformFileLength */ int __PHYSFS_platformEOF(void *opaque) { PHYSFS_sint64 pos = __PHYSFS_platformTell(opaque); PHYSFS_sint64 len = __PHYSFS_platformFileLength(opaque); return(pos >= len); } /* __PHYSFS_platformEOF */ int __PHYSFS_platformFlush(void *opaque) { int fd = *((int *) opaque); BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0); return(1); } /* __PHYSFS_platformFlush */ int __PHYSFS_platformClose(void *opaque) { int fd = *((int *) opaque); BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0); allocator.Free(opaque); return(1); } /* __PHYSFS_platformClose */ int __PHYSFS_platformDelete(const char *path) { BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0); return(1); } /* __PHYSFS_platformDelete */ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) { struct stat statbuf; BAIL_IF_MACRO(stat(fname, &statbuf) < 0, strerror(errno), -1); return statbuf.st_mtime; } /* __PHYSFS_platformGetLastModTime */ int __PHYSFS_platformAllocatorInit(void) { return(1); /* always succeeds. */ } /* __PHYSFS_platformAllocatorInit */ void __PHYSFS_platformAllocatorDeinit(void) { /* no-op */ } /* __PHYSFS_platformAllocatorInit */ void *__PHYSFS_platformAllocatorMalloc(PHYSFS_uint64 s) { BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); #undef malloc return(malloc((size_t) s)); } /* __PHYSFS_platformMalloc */ void *__PHYSFS_platformAllocatorRealloc(void *ptr, PHYSFS_uint64 s) { BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL); #undef realloc return(realloc(ptr, (size_t) s)); } /* __PHYSFS_platformRealloc */ void __PHYSFS_platformAllocatorFree(void *ptr) { #undef free free(ptr); } /* __PHYSFS_platformAllocatorFree */ #endif /* PHYSFS_PLATFORM_POSIX */ /* end of posix.c ... */