diff options
Diffstat (limited to 'android_utils.c')
-rw-r--r-- | android_utils.c | 1946 |
1 files changed, 0 insertions, 1946 deletions
diff --git a/android_utils.c b/android_utils.c deleted file mode 100644 index b995504..0000000 --- a/android_utils.c +++ /dev/null @@ -1,1946 +0,0 @@ -/* Copyright (C) 2007-2008 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** 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. -*/ -#include "android_utils.h" -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <fcntl.h> -#ifdef _WIN32 -#include <process.h> -#include <shlobj.h> -#include <tlhelp32.h> -#include <io.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <stdint.h> -#include <limits.h> -#include <winbase.h> -#else -#include <unistd.h> -#include <sys/stat.h> -#include <time.h> -#include <signal.h> -#endif -#include "android.h" -#include "android_debug.h" - -#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) - -#ifdef _WIN32 -char* -win32_strsep(char** pline, const char* delim) -{ - char* line = *pline; - char* p = line; - - if (p == NULL) - return NULL; - - for (;;) { - int c = *p++; - const char* q = delim; - - if (c == 0) { - p = NULL; - break; - } - - while (*q) { - if (*q == c) { - p[-1] = 0; - goto Exit; - } - q++; - } - } -Exit: - *pline = p; - return line; -} -#endif - -/** PATH HANDLING ROUTINES - ** - ** path_parent() can be used to return the n-level parent of a given directory - ** this understands . and .. when encountered in the input path - **/ - -static __inline__ int -ispathsep(int c) -{ -#ifdef _WIN32 - return (c == '/' || c == '\\'); -#else - return (c == '/'); -#endif -} - -char* path_parent( const char* path, int levels ) -{ - const char* end = path + strlen(path); - char* result; - - while (levels > 0) { - const char* base; - - /* trim any trailing path separator */ - while (end > path && ispathsep(end[-1])) - end--; - - base = end; - while (base > path && !ispathsep(base[-1])) - base--; - - if (base <= path) /* we can't go that far */ - return NULL; - - if (end == base+1 && base[0] == '.') - goto Next; - - if (end == base+2 && base[0] == '.' && base[1] == '.') { - levels += 1; - goto Next; - } - - levels -= 1; - - Next: - end = base - 1; - } - result = malloc( end-path+1 ); - if (result != NULL) { - memcpy( result, path, end-path ); - result[end-path] = 0; - } - return result; -} - - -/** MISC FILE AND DIRECTORY HANDLING - **/ - -int -path_exists( const char* path ) -{ - int ret; - CHECKED(ret, access(path, F_OK)); - return (ret == 0) || (errno != ENOENT); -} - -/* checks that a path points to a regular file */ -int -path_is_regular( const char* path ) -{ - int ret; - struct stat st; - - CHECKED(ret, stat(path, &st)); - if (ret < 0) - return 0; - - return S_ISREG(st.st_mode); -} - - -/* checks that a path points to a directory */ -int -path_is_dir( const char* path ) -{ - int ret; - struct stat st; - - CHECKED(ret, stat(path, &st)); - if (ret < 0) - return 0; - - return S_ISDIR(st.st_mode); -} - -/* checks that one can read/write a given (regular) file */ -int -path_can_read( const char* path ) -{ - int ret; - CHECKED(ret, access(path, R_OK)); - return (ret == 0); -} - -int -path_can_write( const char* path ) -{ - int ret; - CHECKED(ret, access(path, R_OK)); - return (ret == 0); -} - -/* try to make a directory. returns 0 on success, -1 on failure - * (error code in errno) */ -int -path_mkdir( const char* path, int mode ) -{ -#ifdef _WIN32 - (void)mode; - return _mkdir(path); -#else - int ret; - CHECKED(ret, mkdir(path, mode)); - return ret; -#endif -} - -static int -path_mkdir_recursive( char* path, unsigned len, int mode ) -{ - char old_c; - int ret; - unsigned len2; - - /* get rid of trailing separators */ - while (len > 0 && ispathsep(path[len-1])) - len -= 1; - - if (len == 0) { - errno = ENOENT; - return -1; - } - - /* check that the parent exists, 'len2' is the length of - * the parent part of the path */ - len2 = len-1; - while (len2 > 0 && !ispathsep(path[len2-1])) - len2 -= 1; - - if (len2 > 0) { - old_c = path[len2]; - path[len2] = 0; - ret = 0; - if ( !path_exists(path) ) { - /* the parent doesn't exist, so try to create it */ - ret = path_mkdir_recursive( path, len2, mode ); - } - path[len2] = old_c; - - if (ret < 0) - return ret; - } - - /* at this point, we now the parent exists */ - old_c = path[len]; - path[len] = 0; - ret = path_mkdir( path, mode ); - path[len] = old_c; - - return ret; -} - -/* ensure that a given directory exists, create it if not, - 0 on success, -1 on failure (error code in errno) */ -int -path_mkdir_if_needed( const char* path, int mode ) -{ - int ret = 0; - - if (!path_exists(path)) { - ret = path_mkdir(path, mode); - - if (ret < 0 && errno == ENOENT) { - char temp[MAX_PATH]; - unsigned len = (unsigned)strlen(path); - - if (len > sizeof(temp)-1) { - errno = EINVAL; - return -1; - } - memcpy( temp, path, len ); - temp[len] = 0; - - return path_mkdir_recursive(temp, len, mode); - } - } - return ret; -} - -/* return the size of a given file in '*psize'. returns 0 on - * success, -1 on failure (error code in errno) */ -int -path_get_size( const char* path, uint64_t *psize ) -{ -#ifdef _WIN32 - /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */ - /* do not use OpenFile() because it has strange search behaviour that could */ - /* result in getting the size of a different file */ - LARGE_INTEGER size; - HANDLE file = CreateFile( /* lpFilename */ path, - /* dwDesiredAccess */ GENERIC_READ, - /* dwSharedMode */ FILE_SHARE_READ|FILE_SHARE_WRITE, - /* lpSecurityAttributes */ NULL, - /* dwCreationDisposition */ OPEN_EXISTING, - /* dwFlagsAndAttributes */ 0, - /* hTemplateFile */ NULL ); - if (file == INVALID_HANDLE_VALUE) { - /* ok, just to play fair */ - errno = ENOENT; - return -1; - } - if (!GetFileSizeEx(file, &size)) { - /* maybe we tried to get the size of a pipe or something like that ? */ - *psize = 0; - } - else { - *psize = (uint64_t) size.QuadPart; - } - CloseHandle(file); - return 0; -#else - int ret; - struct stat st; - - CHECKED(ret, stat(path, &st)); - if (ret == 0) { - *psize = (uint64_t) st.st_size; - } - return ret; -#endif -} - - -/** USEFUL STRING BUFFER FUNCTIONS - **/ - -char* -vbufprint( char* buffer, - char* buffer_end, - const char* fmt, - va_list args ) -{ - int len = vsnprintf( buffer, buffer_end - buffer, fmt, args ); - if (len < 0 || buffer+len >= buffer_end) { - if (buffer < buffer_end) - buffer_end[-1] = 0; - return buffer_end; - } - return buffer + len; -} - -char* -bufprint(char* buffer, char* end, const char* fmt, ... ) -{ - va_list args; - char* result; - - va_start(args, fmt); - result = vbufprint(buffer, end, fmt, args); - va_end(args); - return result; -} - -/** USEFUL DIRECTORY SUPPORT - ** - ** bufprint_app_dir() returns the directory where the emulator binary is located - ** - ** get_android_home() returns a user-specific directory where the emulator will - ** store its writable data (e.g. config files, profiles, etc...). - ** on Unix, this is $HOME/.android, on Windows, this is something like - ** "%USERPROFILE%/Local Settings/AppData/Android" on XP, and something different - ** on Vista. - ** - ** both functions return a string that must be freed by the caller - **/ - -#ifdef __linux__ -char* -bufprint_app_dir(char* buff, char* end) -{ - char path[1024]; - int len; - char* x; - - len = readlink("/proc/self/exe", path, sizeof(path)); - if (len <= 0 || len >= (int)sizeof(path)) goto Fail; - path[len] = 0; - - x = strrchr(path, '/'); - if (x == 0) goto Fail; - *x = 0; - - return bufprint(buff, end, "%s", path); -Fail: - fprintf(stderr,"cannot locate application directory\n"); - exit(1); - return end; -} - -#elif defined(__APPLE__) -/* the following hack is needed in order to build with XCode 3.1 - * don't ask me why, but it seems that there were changes in the - * GCC compiler that we don't have in our pre-compiled version - */ -#ifndef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ MAC_OS_X_VERSION_10_4 -#endif -#import <Carbon/Carbon.h> -#include <unistd.h> - -char* -bufprint_app_dir(char* buff, char* end) -{ - ProcessSerialNumber psn; - CFDictionaryRef dict; - CFStringRef value; - char s[PATH_MAX]; - char* x; - - GetCurrentProcess(&psn); - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - value = (CFStringRef)CFDictionaryGetValue(dict, - CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8); - x = strrchr(s, '/'); - if (x == 0) goto fail; - *x = 0; - - return bufprint(buff, end, "%s", s); -fail: - fprintf(stderr,"cannot locate application directory\n"); - exit(1); - return end; -} -#elif defined _WIN32 -char* -bufprint_app_dir(char* buff, char* end) -{ - char appDir[MAX_PATH]; - char* sep; - - GetModuleFileName( 0, appDir, sizeof(appDir)-1 ); - sep = strrchr( appDir, '\\' ); - if (sep) - *sep = 0; - - return bufprint(buff, end, "%s", appDir); -} -#else -char* -bufprint_app_dir(char* buff, char* end) -{ - return bufprint(buff, end, "."); -} -#endif - -#define _ANDROID_PATH ".android" - -char* -bufprint_config_path(char* buff, char* end) -{ -#ifdef _WIN32 - char path[MAX_PATH]; - - SHGetFolderPath( NULL, CSIDL_PROFILE, - NULL, 0, path); - - return bufprint(buff, end, "%s\\%s", path, _ANDROID_PATH ); -#else - const char* home = getenv("HOME"); - if (home == NULL) - home = "/tmp"; - return bufprint(buff, end, "%s/%s", home, _ANDROID_PATH ); -#endif -} - -char* -bufprint_config_file(char* buff, char* end, const char* suffix) -{ - char* p; - p = bufprint_config_path(buff, end); - p = bufprint(p, end, PATH_SEP "%s", suffix); - return p; -} - -char* -bufprint_temp_dir(char* buff, char* end) -{ -#ifdef _WIN32 - char path[MAX_PATH]; - DWORD retval; - - retval = GetTempPath( sizeof(path), path ); - if (retval > sizeof(path) || retval == 0) { - D( "can't locate TEMP directory" ); - pstrcpy(path, sizeof(path), "C:\\Temp"); - } - strncat( path, "\\AndroidEmulator", sizeof(path)-1 ); - _mkdir(path); - - return bufprint(buff, end, "%s", path); -#else - const char* tmppath = "/tmp/android"; - mkdir(tmppath, 0744); - return bufprint(buff, end, "%s", tmppath ); -#endif -} - -char* -bufprint_temp_file(char* buff, char* end, const char* suffix) -{ - char* p; - p = bufprint_temp_dir(buff, end); - p = bufprint(p, end, PATH_SEP "%s", suffix); - return p; -} - - - -/** FILE LOCKS SUPPORT - ** - ** a FileLock is useful to prevent several emulator instances from using the same - ** writable file (e.g. the userdata.img disk images). - ** - ** create a FileLock object with filelock_create(), ithis function should return NULL - ** only if the corresponding file path could not be locked. - ** - ** all file locks are automatically released and destroyed when the program exits. - ** the filelock_lock() function can also detect stale file locks that can linger - ** when the emulator crashes unexpectedly, and will happily clean them for you - ** - ** here's how it works, three files are used: - ** file - the data file accessed by the emulator - ** lock - a lock file (file + '.lock') - ** temp - a temporary file make unique with mkstemp - ** - ** when locking: - ** create 'temp' and store our pid in it - ** attemp to link 'lock' to 'temp' - ** if the link succeeds, we obtain the lock - ** unlink 'temp' - ** - ** when unlocking: - ** unlink 'lock' - ** - ** - ** on Windows, 'lock' is a directory name. locking is equivalent to - ** creating it... - ** - **/ - -struct FileLock -{ - const char* file; - const char* lock; - char* temp; - int locked; - FileLock* next; -}; - -/* used to cleanup all locks at emulator exit */ -static FileLock* _all_filelocks; - - -#define LOCK_NAME ".lock" -#define TEMP_NAME ".tmp-XXXXXX" - -#ifdef _WIN32 -#define PIDFILE_NAME "pid" -#endif - -/* returns 0 on success, -1 on failure */ -static int -filelock_lock( FileLock* lock ) -{ - int ret; -#ifdef _WIN32 - int pidfile_fd = -1; - - ret = _mkdir( lock->lock ); - if (ret < 0) { - if (errno == ENOENT) { - D( "could not access directory '%s', check path elements", lock->lock ); - return -1; - } else if (errno != EEXIST) { - D( "_mkdir(%s): %s", lock->lock, strerror(errno) ); - return -1; - } - - /* if we get here, it's because the .lock directory already exists */ - /* check to see if there is a pid file in it */ - D("directory '%s' already exist, waiting a bit to ensure that no other emulator instance is starting", lock->lock ); - { - int _sleep = 200; - int tries; - - for ( tries = 4; tries > 0; tries-- ) - { - pidfile_fd = open( lock->temp, O_RDONLY ); - - if (pidfile_fd >= 0) - break; - - Sleep( _sleep ); - _sleep *= 2; - } - } - - if (pidfile_fd < 0) { - D( "no pid file in '%s', assuming stale directory", lock->lock ); - } - else - { - /* read the pidfile, and check wether the corresponding process is still running */ - char buf[16]; - int len, lockpid; - HANDLE processSnapshot; - PROCESSENTRY32 pe32; - int is_locked = 0; - - len = read( pidfile_fd, buf, sizeof(buf)-1 ); - if (len < 0) { - D( "could not read pid file '%s'", lock->temp ); - close( pidfile_fd ); - return -1; - } - buf[len] = 0; - lockpid = atoi(buf); - - /* PID 0 is the IDLE process, and 0 is returned in case of invalid input */ - if (lockpid == 0) - lockpid = -1; - - close( pidfile_fd ); - - pe32.dwSize = sizeof( PROCESSENTRY32 ); - processSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); - - if ( processSnapshot == INVALID_HANDLE_VALUE ) { - D( "could not retrieve the list of currently active processes\n" ); - is_locked = 1; - } - else if ( !Process32First( processSnapshot, &pe32 ) ) - { - D( "could not retrieve first process id\n" ); - CloseHandle( processSnapshot ); - is_locked = 1; - } - else - { - do { - if (pe32.th32ProcessID == lockpid) { - is_locked = 1; - break; - } - } while (Process32Next( processSnapshot, &pe32 ) ); - - CloseHandle( processSnapshot ); - } - - if (is_locked) { - D( "the file '%s' is locked by process ID %d\n", lock->file, lockpid ); - return -1; - } - } - } - - /* write our PID into the pid file */ - pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC ); - if (pidfile_fd < 0) { - if (errno == EACCES) { - if ( unlink_file( lock->temp ) < 0 ) { - D( "could not remove '%s': %s\n", lock->temp, strerror(errno) ); - return -1; - } - pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC ); - } - if (pidfile_fd < 0) { - D( "could not create '%s': %s\n", lock->temp, strerror(errno) ); - return -1; - } - } - - { - char buf[16]; - sprintf( buf, "%ld", GetCurrentProcessId() ); - ret = write( pidfile_fd, buf, strlen(buf) ); - close(pidfile_fd); - if (ret < 0) { - D( "could not write PID to '%s'\n", lock->temp ); - return -1; - } - } - - lock->locked = 1; - return 0; -#else - int temp_fd = -1; - int lock_fd = -1; - int rc, tries, _sleep; - FILE* f = NULL; - char pid[8]; - struct stat st_temp; - - strcpy( lock->temp, lock->file ); - strcat( lock->temp, TEMP_NAME ); - temp_fd = mkstemp( lock->temp ); - - if (temp_fd < 0) { - D("cannot create locking temp file '%s'", lock->temp ); - goto Fail; - } - - sprintf( pid, "%d", getpid() ); - ret = write( temp_fd, pid, strlen(pid)+1 ); - if (ret < 0) { - D( "cannot write to locking temp file '%s'", lock->temp); - goto Fail; - } - close( temp_fd ); - temp_fd = -1; - - CHECKED(rc, lstat( lock->temp, &st_temp )); - if (rc < 0) { - D( "can't properly stat our locking temp file '%s'", lock->temp ); - goto Fail; - } - - /* now attempt to link the temp file to the lock file */ - _sleep = 0; - for ( tries = 4; tries > 0; tries-- ) - { - struct stat st_lock; - int rc; - - if (_sleep > 0) { - if (_sleep > 2000000) { - D( "cannot acquire lock file '%s'", lock->lock ); - goto Fail; - } - usleep( _sleep ); - } - _sleep += 200000; - - /* the return value of link() is buggy on NFS */ - CHECKED(rc, link( lock->temp, lock->lock )); - - CHECKED(rc, lstat( lock->lock, &st_lock )); - if (rc == 0 && - st_temp.st_rdev == st_lock.st_rdev && - st_temp.st_ino == st_lock.st_ino ) - { - /* SUCCESS */ - lock->locked = 1; - CHECKED(rc, unlink( lock->temp )); - return 0; - } - - /* if we get there, it means that the link() call failed */ - /* check the lockfile to see if it is stale */ - if (rc == 0) { - char buf[16]; - time_t now; - int lockpid = 0; - int lockfd; - int stale = 2; /* means don't know */ - struct stat st; - - CHECKED(rc, time( &now)); - st.st_mtime = now - 120; - - CHECKED(lockfd, open( lock->lock,O_RDONLY )); - if ( lockfd >= 0 ) { - int len; - - CHECKED(len, read( lockfd, buf, sizeof(buf)-1 )); - buf[len] = 0; - lockpid = atoi(buf); - - CHECKED(rc, fstat( lockfd, &st )); - if (rc == 0) - now = st.st_atime; - - CHECKED(rc, close(lockfd)); - } - /* if there is a PID, check that it is still alive */ - if (lockpid > 0) { - CHECKED(rc, kill( lockpid, 0 )); - if (rc == 0 || errno == EPERM) { - stale = 0; - } else if (rc < 0 && errno == ESRCH) { - stale = 1; - } - } - if (stale == 2) { - /* no pid, stale if the file is older than 1 minute */ - stale = (now >= st.st_mtime + 60); - } - - if (stale) { - D( "removing stale lockfile '%s'", lock->lock ); - CHECKED(rc, unlink( lock->lock )); - _sleep = 0; - tries++; - } - } - } - D("file '%s' is already in use by another process", lock->file ); - -Fail: - if (f) - fclose(f); - - if (temp_fd >= 0) { - close(temp_fd); - } - - if (lock_fd >= 0) { - close(lock_fd); - } - - unlink( lock->lock ); - unlink( lock->temp ); - return -1; -#endif -} - -void -filelock_release( FileLock* lock ) -{ - if (lock->locked) { -#ifdef _WIN32 - unlink_file( (char*)lock->temp ); - rmdir( (char*)lock->lock ); -#else - unlink( (char*)lock->lock ); -#endif - lock->locked = 0; - } -} - -static void -filelock_atexit( void ) -{ - FileLock* lock; - - for (lock = _all_filelocks; lock != NULL; lock = lock->next) - filelock_release( lock ); -} - -/* create a file lock */ -FileLock* -filelock_create( const char* file ) -{ - int file_len = strlen(file); - int lock_len = file_len + sizeof(LOCK_NAME); -#ifdef _WIN32 - int temp_len = lock_len + 1 + sizeof(PIDFILE_NAME); -#else - int temp_len = file_len + sizeof(TEMP_NAME); -#endif - int total_len = sizeof(FileLock) + file_len + lock_len + temp_len + 3; - - FileLock* lock = malloc(total_len); - - lock->file = (const char*)(lock + 1); - memcpy( (char*)lock->file, file, file_len+1 ); - - lock->lock = lock->file + file_len + 1; - memcpy( (char*)lock->lock, file, file_len+1 ); - strcat( (char*)lock->lock, LOCK_NAME ); - - lock->temp = (char*)lock->lock + lock_len + 1; -#ifdef _WIN32 - snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock ); -#else - lock->temp[0] = 0; -#endif - lock->locked = 0; - - if (filelock_lock(lock) < 0) { - free(lock); - return NULL; - } - - lock->next = _all_filelocks; - _all_filelocks = lock; - - if (lock->next == NULL) - atexit( filelock_atexit ); - - return lock; -} - -/** TEMP FILE SUPPORT - ** - ** simple interface to create an empty temporary file on the system. - ** - ** create the file with tempfile_create(), which returns a reference to a TempFile - ** object, or NULL if your system is so weird it doesn't have a temporary directory. - ** - ** you can then call tempfile_path() to retrieve the TempFile's real path to open - ** it. the returned path is owned by the TempFile object and should not be freed. - ** - ** all temporary files are destroyed when the program quits, unless you explicitely - ** close them before that with tempfile_close() - **/ - -struct TempFile -{ - const char* name; - TempFile* next; -}; - -static void tempfile_atexit(); -static TempFile* _all_tempfiles; - -TempFile* -tempfile_create( void ) -{ - TempFile* tempfile; - const char* tempname = NULL; - -#ifdef _WIN32 - char temp_namebuff[MAX_PATH]; - char temp_dir[MAX_PATH]; - char *p = temp_dir, *end = p + sizeof(temp_dir); - UINT retval; - - p = bufprint_temp_dir( p, end ); - if (p >= end) { - D( "TEMP directory path is too long" ); - return NULL; - } - - retval = GetTempFileName(temp_dir, "TMP", 0, temp_namebuff); - if (retval == 0) { - D( "can't create temporary file in '%s'", temp_dir ); - return NULL; - } - - tempname = temp_namebuff; -#else -#define TEMPLATE "/tmp/.android-emulator-XXXXXX" - int tempfd = -1; - char template[512]; - char *p = template, *end = p + sizeof(template); - - p = bufprint_temp_file( p, end, "emulator-XXXXXX" ); - if (p >= end) { - D( "Xcannot create temporary file in /tmp/android !!" ); - return NULL; - } - - D( "template: %s", template ); - tempfd = mkstemp( template ); - if (tempfd < 0) { - D("cannot create temporary file in /tmp/android !!"); - return NULL; - } - close(tempfd); - tempname = template; -#endif - tempfile = malloc( sizeof(*tempfile) + strlen(tempname) + 1 ); - tempfile->name = (char*)(tempfile + 1); - strcpy( (char*)tempfile->name, tempname ); - - tempfile->next = _all_tempfiles; - _all_tempfiles = tempfile; - - if ( !tempfile->next ) { - atexit( tempfile_atexit ); - } - - return tempfile; -} - -const char* -tempfile_path(TempFile* temp) -{ - return temp ? temp->name : NULL; -} - -void -tempfile_close(TempFile* tempfile) -{ -#ifdef _WIN32 - DeleteFile(tempfile->name); -#else - unlink(tempfile->name); -#endif -} - -/** TEMP FILE CLEANUP - ** - **/ - -/* we don't expect to use many temporary files */ -#define MAX_ATEXIT_FDS 16 - -typedef struct { - int count; - int fds[ MAX_ATEXIT_FDS ]; -} AtExitFds; - -static void -atexit_fds_add( AtExitFds* t, int fd ) -{ - if (t->count < MAX_ATEXIT_FDS) - t->fds[t->count++] = fd; - else { - dwarning("%s: over %d calls. Program exit may not cleanup all temporary files", - __FUNCTION__, MAX_ATEXIT_FDS); - } -} - -static void -atexit_fds_del( AtExitFds* t, int fd ) -{ - int nn; - for (nn = 0; nn < t->count; nn++) - if (t->fds[nn] == fd) { - /* move the last element to the current position */ - t->count -= 1; - t->fds[nn] = t->fds[t->count]; - break; - } -} - -static void -atexit_fds_close_all( AtExitFds* t ) -{ - int nn; - for (nn = 0; nn < t->count; nn++) - close(t->fds[nn]); -} - -static AtExitFds _atexit_fds[1]; - -void -atexit_close_fd(int fd) -{ - if (fd >= 0) - atexit_fds_add(_atexit_fds, fd); -} - -void -atexit_close_fd_remove(int fd) -{ - if (fd >= 0) - atexit_fds_del(_atexit_fds, fd); -} - -static void -tempfile_atexit( void ) -{ - TempFile* tempfile; - - atexit_fds_close_all( _atexit_fds ); - - for (tempfile = _all_tempfiles; tempfile; tempfile = tempfile->next) - tempfile_close(tempfile); -} - - -/** OTHER FILE UTILITIES - ** - ** make_empty_file() creates an empty file at a given path location. - ** if the file already exists, it is truncated without warning - ** - ** copy_file() copies one file into another. - ** - ** both functions return 0 on success, and -1 on error - **/ - -int -make_empty_file( const char* path ) -{ -#ifdef _WIN32 - int fd = _creat( path, S_IWRITE ); -#else - /* on Unix, only allow the owner to read/write, since the file * - * may contain some personal data we don't want to see exposed */ - int fd = creat(path, S_IRUSR | S_IWUSR); -#endif - if (fd >= 0) { - close(fd); - return 0; - } - return -1; -} - -int -copy_file( const char* dest, const char* source ) -{ - int fd, fs, result = -1; - - if ( access(source, F_OK) < 0 || - make_empty_file(dest) < 0) { - return -1; - } - -#ifdef _WIN32 - fd = _open(dest, _O_RDWR | _O_BINARY); - fs = _open(source, _O_RDONLY | _O_BINARY); -#else - fd = creat(dest, S_IRUSR | S_IWUSR); - fs = open(source, S_IREAD); -#endif - if (fs >= 0 && fd >= 0) { - char buf[4096]; - ssize_t total = 0; - ssize_t n; - result = 0; /* success */ - while ((n = read(fs, buf, 4096)) > 0) { - if (write(fd, buf, n) != n) { - /* write failed. Make it return -1 so that an - * empty file be created. */ - D("Failed to copy '%s' to '%s': %s (%d)", - source, dest, strerror(errno), errno); - result = -1; - break; - } - total += n; - } - } - - if (fs >= 0) { - close(fs); - } - if (fd >= 0) { - close(fd); - } - return result; -} - -int -unlink_file( const char* path ) -{ -#ifdef _WIN32 - int ret = _unlink( path ); - if (ret == -1 && errno == EACCES) { - /* a first call to _unlink will fail if the file is set read-only */ - /* we can however try to change its mode first and call unlink */ - /* again... */ - ret = _chmod( path, _S_IREAD | _S_IWRITE ); - if (ret == 0) - ret = _unlink( path ); - } - return ret; -#else - return unlink(path); -#endif -} - - -void* -load_text_file(const char *fn) -{ - char *data; - int sz; - int fd; - - data = NULL; - fd = open(fn, O_BINARY | O_RDONLY); - if(fd < 0) return NULL; - - sz = lseek(fd, 0, SEEK_END); - if(sz < 0) goto oops; - - if(lseek(fd, 0, SEEK_SET) != 0) goto oops; - - data = (char*) malloc(sz + 1); - if(data == 0) goto oops; - - if(read(fd, data, sz) != sz) goto oops; - close(fd); - data[sz] = 0; - - return data; - -oops: - close(fd); - if(data != 0) - free(data); - return NULL; -} - -/** HOST RESOLUTION SETTINGS - ** - ** return the main monitor's DPI resolution according to the host device - ** beware: this is not always reliable or even obtainable. - ** - ** returns 0 on success, or -1 in case of error (e.g. the system returns funky values) - **/ - -/** NOTE: the following code assumes that we exclusively use X11 on Linux, and Quartz on OS X - **/ - -#ifdef _WIN32 -int -get_monitor_resolution( int *px_dpi, int *py_dpi ) -{ - HDC displayDC = CreateDC( "DISPLAY", NULL, NULL, NULL ); - int xdpi, ydpi; - - if (displayDC == NULL) { - D( "%s: could not get display DC\n", __FUNCTION__ ); - return -1; - } - xdpi = GetDeviceCaps( displayDC, LOGPIXELSX ); - ydpi = GetDeviceCaps( displayDC, LOGPIXELSY ); - - /* sanity checks */ - if (xdpi < 20 || xdpi > 400 || ydpi < 20 || ydpi > 400) { - D( "%s: bad resolution: xpi=%d ydpi=%d", __FUNCTION__, - xdpi, ydpi ); - return -1; - } - - *px_dpi = xdpi; - *py_dpi = ydpi; - return 0; -} -#elif defined __APPLE__ -int -get_monitor_resolution( int *px_dpi, int *py_dpi ) -{ - fprintf(stderr, "emulator: FIXME: implement get_monitor_resolution on OS X\n" ); - return -1; -} -#else /* Linux and others */ -#include <SDL.h> -#include <SDL_syswm.h> -#include <dlfcn.h> -#include <X11/Xlib.h> -#define MM_PER_INCH 25.4 - -#define DYNLINK_FUNCTIONS \ - DYNLINK_FUNC(int,XDefaultScreen,(Display*)) \ - DYNLINK_FUNC(int,XDisplayWidth,(Display*,int)) \ - DYNLINK_FUNC(int,XDisplayWidthMM,(Display*,int)) \ - DYNLINK_FUNC(int,XDisplayHeight,(Display*,int)) \ - DYNLINK_FUNC(int,XDisplayHeightMM,(Display*,int)) \ - -#define DYNLINK_FUNCTIONS_INIT \ - x11_dynlink_init - -#include "dynlink.h" - -static int x11_lib_inited; -static void* x11_lib; - -int -x11_lib_init( void ) -{ - if (!x11_lib_inited) { - x11_lib_inited = 1; - - x11_lib = dlopen( "libX11.so", RTLD_NOW ); - - if (x11_lib == NULL) { - x11_lib = dlopen( "libX11.so.6", RTLD_NOW ); - } - if (x11_lib == NULL) { - D("%s: Could not find libX11.so on this machine", - __FUNCTION__); - return -1; - } - - if (x11_dynlink_init(x11_lib) < 0) { - D("%s: didn't find necessary symbols in libX11.so", - __FUNCTION__); - dlclose(x11_lib); - x11_lib = NULL; - } - } - return x11_lib ? 0 : -1; -} - - -int -get_monitor_resolution( int *px_dpi, int *py_dpi ) -{ - SDL_SysWMinfo info; - Display* display; - int screen; - int width, width_mm, height, height_mm, xdpi, ydpi; - - SDL_VERSION(&info.version); - - if ( !SDL_GetWMInfo(&info) ) { - D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError()); - return -1; - } - - if (x11_lib_init() < 0) - return -1; - - display = info.info.x11.display; - screen = FF(XDefaultScreen)(display); - - width = FF(XDisplayWidth)(display, screen); - width_mm = FF(XDisplayWidthMM)(display, screen); - height = FF(XDisplayHeight)(display, screen); - height_mm = FF(XDisplayHeightMM)(display, screen); - - if (width_mm <= 0 || height_mm <= 0) { - D( "%s: bad screen dimensions: width_mm = %d, height_mm = %d", - __FUNCTION__, width_mm, height_mm); - return -1; - } - - D( "%s: found screen width=%d height=%d width_mm=%d height_mm=%d", - __FUNCTION__, width, height, width_mm, height_mm ); - - xdpi = width * MM_PER_INCH / width_mm; - ydpi = height * MM_PER_INCH / height_mm; - - if (xdpi < 20 || xdpi > 400 || ydpi < 20 || ydpi > 400) { - D( "%s: bad resolution: xpi=%d ydpi=%d", __FUNCTION__, - xdpi, ydpi ); - return -1; - } - - *px_dpi = xdpi; - *py_dpi = ydpi; - - return 0; -} -#endif - - - -void -disable_sigalrm( signal_state_t *state ) -{ -#ifdef _WIN32 - (void)state; -#else - sigset_t set; - - sigemptyset(&set); - sigaddset(&set, SIGALRM); - pthread_sigmask (SIG_BLOCK, &set, &state->old); -#endif -} - -void -restore_sigalrm( signal_state_t *state ) -{ -#ifdef _WIN32 - (void)state; -#else - pthread_sigmask (SIG_SETMASK, &state->old, NULL); -#endif -} - -void -sleep_ms( int timeout_ms ) -{ -#ifdef _WIN32 - if (timeout_ms <= 0) - return; - - Sleep( timeout_ms ); -#else - if (timeout_ms <= 0) - return; - - BEGIN_NOSIGALRM - usleep( timeout_ms*1000 ); - END_NOSIGALRM -#endif -} - - -extern void -print_tabular( const char** strings, int count, - const char* prefix, int width ) -{ - int nrows, ncols, r, c, n, maxw = 0; - - for (n = 0; n < count; n++) { - int len = strlen(strings[n]); - if (len > maxw) - maxw = len; - } - maxw += 2; - ncols = width/maxw; - nrows = (count + ncols-1)/ncols; - - for (r = 0; r < nrows; r++) { - printf( "%s", prefix ); - for (c = 0; c < ncols; c++) { - int index = c*nrows + r; - if (index >= count) { - break; - } - printf( "%-*s", maxw, strings[index] ); - } - printf( "\n" ); - } -} - -extern void -stralloc_tabular( stralloc_t* out, - const char** strings, int count, - const char* prefix, int width ) -{ - int nrows, ncols, r, c, n, maxw = 0; - - for (n = 0; n < count; n++) { - int len = strlen(strings[n]); - if (len > maxw) - maxw = len; - } - maxw += 2; - ncols = width/maxw; - nrows = (count + ncols-1)/ncols; - - for (r = 0; r < nrows; r++) { - stralloc_add_str( out, prefix ); - for (c = 0; c < ncols; c++) { - int index = c*nrows + r; - if (index >= count) { - break; - } - stralloc_add_format( out, "%-*s", maxw, strings[index] ); - } - stralloc_add_str( out, "\n" ); - } -} - -extern void -string_translate_char( char* str, char from, char to ) -{ - char* p = str; - while (p != NULL && (p = strchr(p, from)) != NULL) - *p++ = to; -} - -extern void -buffer_translate_char( char* buff, - unsigned buffLen, - const char* src, - char fromChar, - char toChar ) -{ - int len = strlen(src); - - if (len >= buffLen) - len = buffLen-1; - - memcpy(buff, src, len); - buff[len] = 0; - - string_translate_char( buff, fromChar, toChar ); -} - - -/** DYNAMIC STRINGS - **/ - -extern void -stralloc_reset( stralloc_t* s ) -{ - free(s->s); - s->s = NULL; - s->n = 0; - s->a = 0; -} - -extern void -stralloc_ready( stralloc_t* s, unsigned int len ) -{ - unsigned old_max = s->a; - unsigned new_max = old_max; - - while (new_max < len) { - unsigned new_max2 = new_max + (new_max >> 1) + 16; - if (new_max2 < new_max) - new_max2 = UINT_MAX; - new_max = new_max2; - } - - s->s = realloc( s->s, new_max ); - if (s->s == NULL) { - derror( "%s: not enough memory to reallocate %ld bytes", - __FUNCTION__, new_max ); - exit(1); - } - s->a = new_max; -} - -extern void -stralloc_readyplus( stralloc_t* s, unsigned int len ) -{ - unsigned len2 = s->n + len; - - if (len2 < s->n) { /* overflow ? */ - derror("%s: trying to grow by too many bytes: %ld", - __FUNCTION__, len); - exit(1); - } - stralloc_ready( s, len2 ); -} - -extern void -stralloc_copy( stralloc_t* s, stralloc_t* from ) -{ - stralloc_ready(s, from->n); - memcpy( s->s, from->s, from->n ); - s->n = from->n; -} - -extern void -stralloc_append( stralloc_t* s, stralloc_t* from ) -{ - stralloc_readyplus( s, from->n ); - memcpy( s->s + s->n, from->s, from->n ); - s->n += from->n; -} - -extern void -stralloc_add_c( stralloc_t* s, int c ) -{ - stralloc_add_bytes( s, (char*)&c, 1 ); -} - -extern void -stralloc_add_str( stralloc_t* s, const char* str ) -{ - stralloc_add_bytes( s, str, strlen(str) ); -} - -extern void -stralloc_add_bytes( stralloc_t* s, const void* from, unsigned len ) -{ - stralloc_readyplus( s, len ); - memcpy( s->s + s->n, from, len ); - s->n += len; -} - -extern char* -stralloc_cstr( stralloc_t* s ) -{ - stralloc_readyplus( s, 1 ); - s->s[s->n] = 0; - return s->s; -} - -extern void -stralloc_format( stralloc_t* s, const char* fmt, ... ) -{ - stralloc_reset(s); - stralloc_ready(s, 10); - - while (1) { - int n; - va_list args; - - va_start(args, fmt); - n = vsnprintf( s->s, s->a, fmt, args ); - va_end(args); - - /* funky old C libraries returns -1 when truncation occurs */ - if (n > -1 && n < s->a) { - s->n = n; - break; - } - if (n > -1) { /* we now precisely what we need */ - stralloc_ready( s, n+1 ); - } else { - stralloc_ready( s, s->a*2 ); - } - } -} - -extern void -stralloc_add_format( stralloc_t* s, const char* fmt, ... ) -{ - STRALLOC_DEFINE(s2); - - stralloc_ready(s, 10); - while (1) { - int n; - va_list args; - - va_start(args, fmt); - n = vsnprintf( s2->s, s2->a, fmt, args ); - va_end(args); - - /* some C libraries return -1 when truncation occurs */ - if (n > -1 && n < s2->a) { - s2->n = n; - break; - } - if (n > -1) { /* we now precisely what we need */ - stralloc_ready( s2, n+1 ); - } else { - stralloc_ready( s2, s2->a*2 ); - } - } - - stralloc_append( s, s2 ); - stralloc_reset( s2 ); -} - - -extern void -stralloc_add_quote_c( stralloc_t* s, int c ) -{ - stralloc_add_quote_bytes( s, (char*)&c, 1 ); -} - -extern void -stralloc_add_quote_str( stralloc_t* s, const char* str ) -{ - stralloc_add_quote_bytes( s, str, strlen(str) ); -} - -extern void -stralloc_add_quote_bytes( stralloc_t* s, const void* from, unsigned len ) -{ - uint8_t* p = (uint8_t*) from; - uint8_t* end = p + len; - - for ( ; p < end; p++ ) { - int c = p[0]; - - if (c == '\\') { - stralloc_add_str( s, "\\\\" ); - } else if (c >= ' ' && c < 128) { - stralloc_add_c( s, c ); - } else if (c == '\n') { - stralloc_add_str( s, "\\n" ); - } else if (c == '\t') { - stralloc_add_str( s, "\\t" ); - } else if (c == '\r') { - stralloc_add_str( s, "\\r" ); - } else { - stralloc_add_format( s, "\\x%02x", c ); - } - } -} - -extern void -stralloc_add_hex( stralloc_t* s, unsigned value, int num_digits ) -{ - const char hexdigits[16] = "0123456789abcdef"; - int nn; - - if (num_digits <= 0) - return; - - stralloc_readyplus(s, num_digits); - for (nn = num_digits-1; nn >= 0; nn--) { - s->s[s->n+nn] = hexdigits[value & 15]; - value >>= 4; - } - s->n += num_digits; -} - -extern void -stralloc_add_hexdump( stralloc_t* s, void* base, int size, const char* prefix ) -{ - uint8_t* p = (uint8_t*)base; - const int max_count = 16; - int prefix_len = strlen(prefix); - - while (size > 0) { - int count = size > max_count ? max_count : size; - int count2; - int n; - - stralloc_add_bytes( s, prefix, prefix_len ); - stralloc_add_hex( s, p[0], 2 ); - - for (n = 1; n < count; n++) { - stralloc_add_c( s, ' ' ); - stralloc_add_hex( s, p[n], 2 ); - } - - count2 = 4 + 3*(max_count - count); - stralloc_readyplus( s, count2 ); - memset( s->s + s->n, ' ', count2 ); - s->n += count2; - - stralloc_readyplus(s, count+1); - for (n = 0; n < count; n++) { - int c = p[n]; - - if (c < 32 || c > 127) - c = '.'; - - s->s[s->n++] = c; - } - s->s[s->n++] = '\n'; - - size -= count; - p += count; - } -} - -/** TEMP CHAR STRINGS - ** - ** implement a circular ring of temporary string buffers - **/ - -typedef struct Temptring { - struct TempString* next; - char* buffer; - int size; -} TempString; - -#define MAX_TEMP_STRINGS 16 - -static TempString _temp_strings[ MAX_TEMP_STRINGS ]; -static int _temp_string_n; - -extern char* -tempstr_get( int size ) -{ - TempString* t = &_temp_strings[_temp_string_n]; - - if ( ++_temp_string_n >= MAX_TEMP_STRINGS ) - _temp_string_n = 0; - - size += 1; /* reserve 1 char for terminating zero */ - - if (t->size < size) { - t->buffer = realloc( t->buffer, size ); - if (t->buffer == NULL) { - derror( "%s: could not allocate %d bytes", - __FUNCTION__, size ); - exit(1); - } - t->size = size; - } - return t->buffer; -} - -extern char* -tempstr_from_stralloc( stralloc_t* s ) -{ - char* q = tempstr_get( s->n ); - - memcpy( q, s->s, s->n ); - q[s->n] = 0; - return q; -} - -/** QUOTING - ** - ** dumps a human-readable version of a string. this replaces - ** newlines with \n, etc... - **/ - -extern const char* -quote_bytes( const char* str, int len ) -{ - STRALLOC_DEFINE(s); - char* q; - - stralloc_add_quote_bytes( s, str, len ); - q = tempstr_from_stralloc( s ); - stralloc_reset(s); - return q; -} - -extern const char* -quote_str( const char* str ) -{ - int len = strlen(str); - return quote_bytes( str, len ); -} - -/** DYNAMIC ARRAYS OF POINTERS - **/ - -void -qvector_init( qvector_t* v ) -{ - v->i = NULL; - v->n = v->a = 0; -} - -void -qvector_reset( qvector_t* v ) -{ - free(v->i); - v->i = NULL; - v->n = v->a = 0; -} - -void -qvector_ready( qvector_t* v, unsigned len ) -{ - unsigned old_a = v->a; - unsigned new_a = old_a; - unsigned max_a = UINT_MAX/sizeof(v->i[0]); - - if (len <= old_a) - return; - - if (len > max_a) { - derror("panic: %s: length too long (%d)", __FUNCTION__, len); - exit(1); - } - - while (new_a < len) { - unsigned new_a2 = new_a + (new_a >> 1) + 8; - if (new_a2 < new_a || new_a2 > max_a) - new_a2 = max_a; - new_a = max_a; - } - v->i = realloc( v->i, new_a*sizeof(v->i[0]) ); - v->a = new_a; -} - -void -qvector_readyplus( qvector_t* v, unsigned len ) -{ - unsigned len2 = len + v->n; - - if (len2 < len) { - derror("panic: %s: length too long (%d)", __FUNCTION__, len); - exit(1); - } - qvector_ready(v, len2); -} - -void -qvector_add( qvector_t* v, void* item ) -{ - qvector_readyplus(v, 1); - v->i[v->n] = item; - v->n += 1; -} - -int -qvector_del( qvector_t* v, void* item ) -{ - int index = qvector_index(v, item); - if (index < 0) return 0; - qvector_remove(v, index); - return 1; -} - -extern void* -qvector_get( qvector_t* v, int index ) -{ - if ((unsigned)index >= (unsigned)v->n) - return NULL; - - return v->i[index]; -} - -extern void -qvector_set( qvector_t* v, int index, void* item ) -{ - if ((unsigned)index < (unsigned)v->n) - v->i[index] = item; - } - -int -qvector_len( qvector_t* v ) -{ - return v->n; -} - -int -qvector_index( qvector_t* v, void* item ) -{ - int nn; - for (nn = 0; nn < v->n; nn++) - if (v->i[nn] == item) - return nn; - return -1; -} - -void -qvector_insert( qvector_t* v, int index, void* item ) -{ - if (index < 0) index = 0; - if (index > v->n) index = v->n; - - memmove( v->i + index, v->i + index + 1, sizeof(v->i[0]) ); - v->i[index] = item; - v->n += 1; -} - -void -qvector_remove( qvector_t* v, int index ) -{ - if (index < 0 || index >= v->n ) - return; - - memmove( v->i + index + 1, v->i + index, v->n - index - 1 ); - v->n -= 1; -} - -void -qvector_remove_n( qvector_t* v, int index, int count ) -{ - int end = index + count; - - if (index < 0 || index >= v->n || end <= index) - return; - - if (end > v->n) { - end = v->n; - count = end - index; - } - - memmove( v->i + index + count, v->i + index, v->n - index - count ); - v->n -= count; -} - - -/** HEXADECIMAL CHARACTER SEQUENCES - **/ - -static int -hexdigit( int c ) -{ - unsigned d; - - d = (unsigned)(c - '0'); - if (d < 10) return d; - - d = (unsigned)(c - 'a'); - if (d < 6) return d+10; - - d = (unsigned)(c - 'A'); - if (d < 6) return d+10; - - return -1; -} - -int -hex2int( const uint8_t* hex, int len ) -{ - int result = 0; - while (len > 0) { - int c = hexdigit(*hex++); - if (c < 0) - return -1; - - result = (result << 4) | c; - len --; - } - return result; -} - -void -int2hex( uint8_t* hex, int len, int val ) -{ - static const uint8_t hexchars[16] = "0123456789abcdef"; - while ( --len >= 0 ) - *hex++ = hexchars[(val >> (len*4)) & 15]; -} |