/* * Playing-field leveller for Build * by Jonathon Fowler * * A note about this: * 1. There is some kind of problem somewhere in the functions below because * compiling with __compat_h_macrodef__ disabled makes stupid things happen. * 2. The functions below, aside from the ones which aren't trivial, were never * really intended to be used for anything except tracking anr removing ties * to the standard C library from games. Using the Bxx versions of functions * means we can redefine those names to link up with different runtime library * names. */ #include "compat.h" #ifdef _WIN32 # include # include #elif __APPLE__ # include "osxbits.h" #endif #if defined(_MSC_VER) # include #else # include #endif #if defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # include // for dirname() #endif #if defined(__FreeBSD__) # include // for sysctl() to get path to executable #endif #include "baselayer.h" ////////// PANICKING ALLOCATION FUNCTIONS ////////// static void (*g_MemErrHandler)(int32_t line, const char *file, const char *func); #ifdef DEBUGGINGAIDS static const char *g_MemErrFunc = "???"; static const char *g_MemErrFile = "???"; static int32_t g_MemErrLine; void xalloc_set_location(int32_t line, const char *file, const char *func) { g_MemErrLine = line; g_MemErrFile = file; if (func) g_MemErrFunc = func; } #endif void handle_memerr(void) { if (g_MemErrHandler) { #ifdef DEBUGGINGAIDS g_MemErrHandler(g_MemErrLine, g_MemErrFile, g_MemErrFunc); #else g_MemErrHandler(0, "???", "???"); #endif } Bexit(EXIT_FAILURE); } void set_memerr_handler(void(*handlerfunc)(int32_t, const char *, const char *)) { g_MemErrHandler = handlerfunc; } ////////// #ifndef compat_h_macrodef__ void Bassert(int expr) { assert(expr); } int32_t Brand(void) { return rand(); } void *Bmalloc(bsize_t size) { return malloc(size); } void Bfree(void *ptr) { Bfree(ptr); } int32_t Bopen(const char *pathname, int32_t flags, uint32_t mode) { int32_t n=0,o=0; if (flags&BO_BINARY) n|=O_BINARY; else n|=O_TEXT; if ((flags&BO_RDWR)==BO_RDWR) n|=O_RDWR; else if ((flags&BO_RDWR)==BO_WRONLY) n|=O_WRONLY; else if ((flags&BO_RDWR)==BO_RDONLY) n|=O_RDONLY; if (flags&BO_APPEND) n|=O_APPEND; if (flags&BO_CREAT) n|=O_CREAT; if (flags&BO_TRUNC) n|=O_TRUNC; if (mode&BS_IREAD) o|=S_IREAD; if (mode&BS_IWRITE) o|=S_IWRITE; if (mode&BS_IEXEC) o|=S_IEXEC; return open(pathname,n,o); } int32_t Bclose(int32_t fd) { return close(fd); } bssize_t Bwrite(int32_t fd, const void *buf, bsize_t count) { return write(fd,buf,count); } bssize_t Bread(int32_t fd, void *buf, bsize_t count) { return read(fd,buf,count); } int32_t Blseek(int32_t fildes, int32_t offset, int32_t whence) { switch (whence) { case BSEEK_SET: whence=SEEK_SET; break; case BSEEK_CUR: whence=SEEK_CUR; break; case BSEEK_END: whence=SEEK_END; break; } return lseek(fildes,offset,whence); } BFILE *Bfopen(const char *path, const char *mode) { return (BFILE *)fopen(path,mode); } int32_t Bfclose(BFILE *stream) { return fclose((FILE *)stream); } void Brewind(BFILE *stream) { rewind((FILE *)stream); } int32_t Bfgetc(BFILE *stream) { return fgetc((FILE *)stream); } char *Bfgets(char *s, int32_t size, BFILE *stream) { return fgets(s,size,(FILE *)stream); } int32_t Bfputc(int32_t c, BFILE *stream) { return fputc(c,(FILE *)stream); } int32_t Bfputs(const char *s, BFILE *stream) { return fputs(s,(FILE *)stream); } bsize_t Bfread(void *ptr, bsize_t size, bsize_t nmemb, BFILE *stream) { return fread(ptr,size,nmemb,(FILE *)stream); } bsize_t Bfwrite(const void *ptr, bsize_t size, bsize_t nmemb, BFILE *stream) { return fwrite(ptr,size,nmemb,(FILE *)stream); } char *Bstrdup(const char *s) { return strdup(s); } char *Bstrcpy(char *dest, const char *src) { return strcpy(dest,src); } char *Bstrncpy(char *dest, const char *src, bsize_t n) { return Bstrncpy(dest,src,n); } int32_t Bstrcmp(const char *s1, const char *s2) { return strcmp(s1,s2); } int32_t Bstrncmp(const char *s1, const char *s2, bsize_t n) { return strncmp(s1,s2,n); } int32_t Bstrcasecmp(const char *s1, const char *s2) { #ifdef _MSC_VER return _stricmp(s1,s2); #else return strcasecmp(s1,s2); #endif } int32_t Bstrncasecmp(const char *s1, const char *s2, bsize_t n) { #ifdef _MSC_VER return _strnicmp(s1,s2,n); #else return strncasecmp(s1,s2,n); #endif } char *Bstrcat(char *dest, const char *src) { return strcat(dest,src); } char *Bstrncat(char *dest, const char *src, bsize_t n) { return strncat(dest,src,n); } bsize_t Bstrlen(const char *s) { return strlen(s); } char *Bstrchr(const char *s, int32_t c) { return strchr(s,c); } char *Bstrrchr(const char *s, int32_t c) { return strrchr(s,c); } int32_t Batoi(const char *nptr) { return strtol(nptr, NULL, 10); } int32_t Batol(const char *nptr) { return strtol(nptr, NULL, 10); } int32_t Bstrtol(const char *nptr, char **endptr, int32_t base) { return strtol(nptr,endptr,base); } uint32_t Bstrtoul(const char *nptr, char **endptr, int32_t base) { return strtoul(nptr,endptr,base); } void *Bmemcpy(void *dest, const void *src, bsize_t n) { return memcpy(dest,src,n); } void *Bmemmove(void *dest, const void *src, bsize_t n) { return memmove(dest,src,n); } void *Bmemchr(const void *s, int32_t c, bsize_t n) { return memchr(s,c,n); } void *Bmemset(void *s, int32_t c, bsize_t n) { return memset(s,c,n); } int32_t Bprintf(const char *format, ...) { va_list ap; int32_t r; va_start(ap,format); #ifdef _MSC_VER r = _vprintf(format,ap); #else r = vprintf(format,ap); #endif va_end(ap); return r; } int32_t Bsprintf(char *str, const char *format, ...) { va_list ap; int32_t r; va_start(ap,format); #ifdef _MSC_VER r = _vsprintf(str,format,ap); #else r = vsprintf(str,format,ap); #endif va_end(ap); return r; } int32_t Bsnprintf(char *str, bsize_t size, const char *format, ...) { va_list ap; int32_t r; va_start(ap,format); #ifdef _MSC_VER r = _vsnprintf(str,size,format,ap); #else r = vsnprintf(str,size,format,ap); #endif va_end(ap); return r; } int32_t Bvsnprintf(char *str, bsize_t size, const char *format, va_list ap) { #ifdef _MSC_VER return _vsnprintf(str,size,format,ap); #else return vsnprintf(str,size,format,ap); #endif } char *Bgetenv(const char *name) { return getenv(name); } char *Bgetcwd(char *buf, bsize_t size) { return getcwd(buf,size); } #endif // __compat_h_macrodef__ // // Stuff which must be a function // #ifdef _WIN32 typedef BOOL (WINAPI * aSHGetSpecialFolderPathAtype)(HWND, LPTSTR, int, BOOL); #endif char *Bgethomedir(void) { #ifdef _WIN32 aSHGetSpecialFolderPathAtype aSHGetSpecialFolderPathA; TCHAR appdata[MAX_PATH]; int32_t loaded = 0; HMODULE hShell32 = GetModuleHandle("shell32.dll"); if (hShell32 == NULL) { hShell32 = LoadLibrary("shell32.dll"); loaded = 1; } if (hShell32 == NULL) return NULL; aSHGetSpecialFolderPathA = (aSHGetSpecialFolderPathAtype)GetProcAddress(hShell32, "SHGetSpecialFolderPathA"); if (aSHGetSpecialFolderPathA != NULL) if (SUCCEEDED(aSHGetSpecialFolderPathA(NULL, appdata, CSIDL_APPDATA, FALSE))) { if (loaded) FreeLibrary(hShell32); return Bstrdup(appdata); } if (loaded) FreeLibrary(hShell32); return NULL; #elif defined __APPLE__ return osx_gethomedir(); #elif defined(GEKKO) // return current drive's name char *drv, cwd[BMAX_PATH] = {0}; getcwd(cwd, BMAX_PATH); drv = strchr(cwd, ':'); if (drv) drv[1] = '\0'; return Bstrdup(cwd); #else char *e = getenv("HOME"); if (!e) return NULL; return Bstrdup(e); #endif } char *Bgetappdir(void) { char *dir = NULL; #ifdef _WIN32 TCHAR appdir[MAX_PATH]; if (GetModuleFileName(NULL, appdir, MAX_PATH) > 0) { // trim off the filename char *slash = strrchr(appdir, '\\'); if (slash) slash[0] = 0; dir = strdup(appdir); } #elif defined __APPLE__ dir = osx_getappdir(); #elif defined(__linux) || defined(__NetBSD__) || defined(__OpenBSD__) char buf[PATH_MAX] = {0}; char buf2[PATH_MAX] = {0}; # ifdef __linux snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid()); # else // the BSDs.. except for FreeBSD which has a sysctl snprintf(buf, sizeof(buf), "/proc/%d/file", getpid()); # endif int len = readlink(buf, buf2, sizeof(buf2)); if (len != -1) { // remove executable name with dirname(3) // on Linux, dirname() will modify buf2 (cutting off executable name) and return it // on FreeBSD it seems to use some internal buffer instead.. anyway, just strdup() dir = strdup(dirname(buf2)); } #elif defined(__FreeBSD__) // the sysctl should also work when /proc/ is not mounted (which seems to // be common on FreeBSD), so use it.. char buf[PATH_MAX] = {0}; int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; size_t len = sizeof(buf)-1; int ret = sysctl(name, sizeof(name)/sizeof(name[0]), buf, &len, NULL, 0); if(ret == 0 && buf[0] != '\0') { // again, remove executable name with dirname() // on FreeBSD dirname() seems to use some internal buffer dir = strdup(dirname(buf)); } #endif return dir; } int32_t Bcorrectfilename(char *filename, int32_t removefn) { char *fn; char *tokarr[64], *first, *next = NULL, *token; int32_t i, ntok = 0, leadslash = 0, trailslash = 0; fn = Bstrdup(filename); if (!fn) return -1; for (first=fn; *first; first++) { #ifdef _WIN32 if (*first == '\\') *first = '/'; #endif } leadslash = (*fn == '/'); trailslash = (first>fn && first[-1] == '/'); first = fn; do { token = Bstrtoken(first, "/", &next, 1); first = NULL; if (!token) break; else if (token[0] == 0) continue; else if (token[0] == '.' && token[1] == 0) continue; else if (token[0] == '.' && token[1] == '.' && token[2] == 0) ntok = max(0,ntok-1); else tokarr[ntok++] = token; } while (1); if (!trailslash && removefn) { ntok = max(0,ntok-1); trailslash = 1; } if (ntok == 0 && trailslash && leadslash) trailslash = 0; first = filename; if (leadslash) *(first++) = '/'; for (i=0; i0) *(first++) = '/'; for (token=tokarr[i]; *token; token++) *(first++) = *token; } if (trailslash) *(first++) = '/'; *(first++) = 0; Bfree(fn); return 0; } int32_t Bcanonicalisefilename(char *filename, int32_t removefn) { char cwd[BMAX_PATH], fn[BMAX_PATH], *p; char *fnp = filename; #ifdef _WIN32 int32_t drv = 0; #endif #ifdef _WIN32 { if (filename[0] && filename[1] == ':') { // filename is prefixed with a drive drv = toupper(filename[0])-'A' + 1; fnp += 2; } if (!_getdcwd(drv, cwd, sizeof(cwd))) return -1; for (p=cwd; *p; p++) if (*p == '\\') *p = '/'; } #else if (!getcwd(cwd,sizeof(cwd))) return -1; #endif p = strrchr(cwd,'/'); if (!p || p[1]) strcat(cwd, "/"); strcpy(fn, fnp); #ifdef _WIN32 for (p=fn; *p; p++) if (*p == '\\') *p = '/'; #endif if (fn[0] != '/') { // we are dealing with a path relative to the current directory strcpy(filename, cwd); strcat(filename, fn); } else { #ifdef _WIN32 filename[0] = cwd[0]; filename[1] = ':'; filename[2] = 0; #else filename[0] = 0; #endif strcat(filename, fn); } fnp = filename; #ifdef _WIN32 fnp += 2; // skip the drive #endif UNREFERENCED_PARAMETER(removefn); // change the call below to use removefn instead of 1? return Bcorrectfilename(fnp,1); } char *Bgetsystemdrives(void) { #ifdef _WIN32 char *str, *p; DWORD drv, mask; int32_t number=0; drv = GetLogicalDrives(); if (drv == 0) return NULL; for (mask=1; mask<0x8000000l; mask<<=1) { if ((drv&mask) == 0) continue; number++; } str = p = (char *)Bmalloc(1 + (3*number)); if (!str) return NULL; number = 0; for (mask=1; mask<0x8000000l; mask<<=1, number++) { if ((drv&mask) == 0) continue; *(p++) = 'A' + number; *(p++) = ':'; *(p++) = 0; } *(p++) = 0; return str; #else // Perhaps have Unix OS's put /, /home/user, and /mnt/* in the "drives" list? return NULL; #endif } int32_t Bfilelength(int32_t fd) { struct Bstat st; if (Bfstat(fd, &st) < 0) return -1; return(int32_t)(st.st_size); } typedef struct { #ifdef _MSC_VER intptr_t dir; struct _finddata_t fid; #else DIR *dir; #endif struct Bdirent info; int32_t status; char name[1]; } BDIR_real; BDIR *Bopendir(const char *name) { BDIR_real *dirr; #ifdef _MSC_VER char *t,*tt; t = (char *)Bmalloc(strlen(name)+1+4); if (!t) return NULL; #endif dirr = (BDIR_real *)Bmalloc(sizeof(BDIR_real) + strlen(name)); if (!dirr) { #ifdef _MSC_VER Bfree(t); #endif return NULL; } #ifdef _MSC_VER strcpy(t,name); tt = t+strlen(name)-1; while (*tt == ' ' && tt>t) tt--; if (*tt != '/' && *tt != '\\') *(++tt) = '/'; *(++tt) = '*'; *(++tt) = '.'; *(++tt) = '*'; *(++tt) = 0; dirr->dir = _findfirst(t,&dirr->fid); Bfree(t); if (dirr->dir == -1) { Bfree(dirr); return NULL; } #else dirr->dir = opendir(name); if (dirr->dir == NULL) { Bfree(dirr); return NULL; } #endif dirr->status = 0; strcpy(dirr->name, name); return (BDIR *)dirr; } struct Bdirent *Breaddir(BDIR *dir) { BDIR_real *dirr = (BDIR_real *)dir; struct dirent *de; struct Bstat st; char *fn; #ifdef _MSC_VER if (dirr->status > 0) { if (_findnext(dirr->dir,&dirr->fid) != 0) { dirr->status = -1; return NULL; } } dirr->info.namlen = strlen(dirr->fid.name); dirr->info.name = dirr->fid.name; dirr->status++; #else de = readdir(dirr->dir); if (de == NULL) { dirr->status = -1; return NULL; } else { dirr->status++; } //# if defined(__WATCOMC__) || defined(__linux) || defined(__BEOS__) || defined(__QNX__) || defined(SKYOS) dirr->info.namlen = strlen(de->d_name); //# else // dirr->info.namlen = de->d_namlen; //# endif dirr->info.name = de->d_name; #endif dirr->info.mode = 0; dirr->info.size = 0; dirr->info.mtime = 0; fn = (char *)Bmalloc(strlen(dirr->name) + 1 + dirr->info.namlen + 1); if (fn) { Bsprintf(fn,"%s/%s",dirr->name,dirr->info.name); if (!Bstat(fn, &st)) { dirr->info.mode = st.st_mode; dirr->info.size = st.st_size; dirr->info.mtime = st.st_mtime; } Bfree(fn); } return &dirr->info; } int32_t Bclosedir(BDIR *dir) { BDIR_real *dirr = (BDIR_real *)dir; #ifdef _MSC_VER _findclose(dirr->dir); #else closedir(dirr->dir); #endif Bfree(dirr); return 0; } char *Bstrtoken(char *s, const char *delim, char **ptrptr, int32_t chop) { char *p, *start; if (!ptrptr) return NULL; if (s) p = s; else p = *ptrptr; if (!p) return NULL; while (*p != 0 && strchr(delim, *p)) p++; if (*p == 0) { *ptrptr = NULL; return NULL; } start = p; while (*p != 0 && !strchr(delim, *p)) p++; if (*p == 0) *ptrptr = NULL; else { if (chop) *(p++) = 0; *ptrptr = p; } return start; } char *Bstrtolower(char *str) { if (!str) return NULL; { int32_t i = 0, len = Bstrlen(str); if (len <= 0) return str; do { *(str+i) = Btolower(*(str+i)); i++; } while (--len); } return str; } //Brute-force case-insensitive, slash-insensitive, * and ? wildcard matcher //Given: string i and string j. string j can have wildcards //Returns: 1:matches, 0:doesn't match #ifndef WITHKPLIB extern char toupperlookup[256]; static int32_t wildmatch(const char *match, const char *wild) { do { if (*match && (toupperlookup[*wild] == toupperlookup[*match] || *wild == '?')) { wild++, match++; continue; } else if ((*match|*wild) == '\0') return 1; else if (*wild == '*') { while (*wild == '*') wild++; if (*wild == '\0') return 1; while (*match && toupperlookup[*match] != toupperlookup[*wild]) match++; if (toupperlookup[*match] == toupperlookup[*wild]) continue; } return 0; } while (1); } #endif #if !defined(_WIN32) char *Bstrlwr(char *s) { char *t = s; if (!s) return s; while (*t) { *t = Btolower(*t); t++; } return s; } char *Bstrupr(char *s) { char *t = s; if (!s) return s; while (*t) { *t = Btoupper(*t); t++; } return s; } #endif // // Bgetsysmemsize() -- gets the amount of system memory in the machine // #ifdef _WIN32 typedef BOOL (WINAPI *aGlobalMemoryStatusExType)(LPMEMORYSTATUSEX); #endif uint32_t Bgetsysmemsize(void) { #ifdef _WIN32 uint32_t siz = UINT32_MAX; HMODULE lib = LoadLibrary("KERNEL32.DLL"); if (lib) { aGlobalMemoryStatusExType aGlobalMemoryStatusEx = (aGlobalMemoryStatusExType)GetProcAddress(lib, "GlobalMemoryStatusEx"); if (aGlobalMemoryStatusEx) { //WinNT MEMORYSTATUSEX memst; memst.dwLength = sizeof(MEMORYSTATUSEX); if (aGlobalMemoryStatusEx(&memst)) siz = (uint32_t)min(UINT32_MAX, memst.ullTotalPhys); } else { // Yeah, there's enough Win9x hatred here that a perfectly good workaround // has been replaced by an error message. Oh well, we don't support 9x anyway. initprintf("Bgetsysmemsize(): error determining system memory size!\n"); } FreeLibrary(lib); } return siz; #elif (defined(_SC_PAGE_SIZE) || defined(_SC_PAGESIZE)) && defined(_SC_PHYS_PAGES) && !defined(GEKKO) uint32_t siz = UINT32_MAX; int64_t scpagesiz, scphyspages; #ifdef _SC_PAGE_SIZE scpagesiz = sysconf(_SC_PAGE_SIZE); #else scpagesiz = sysconf(_SC_PAGESIZE); #endif scphyspages = sysconf(_SC_PHYS_PAGES); if (scpagesiz >= 0 && scphyspages >= 0) siz = (uint32_t)min(UINT32_MAX, (int64_t)scpagesiz * (int64_t)scphyspages); //initprintf("Bgetsysmemsize(): %d pages of %d bytes, %d bytes of system memory\n", // scphyspages, scpagesiz, siz); return siz; #else return UINT32_MAX; #endif } #ifdef GEKKO int access(const char *pathname, int mode) { struct stat st; if (stat(pathname, &st)==-1) return -1; // TODO: Check mode against st_mode UNREFERENCED_PARAMETER(mode); return 0; } #endif #define LIBDIVIDE_BODY #include "libdivide.h"