diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:32 -0800 |
commit | 8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch) | |
tree | 7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /android/utils | |
parent | f721e3ac031f892af46f255a47d7f54a91317b30 (diff) | |
download | external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.zip external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.gz external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'android/utils')
-rw-r--r-- | android/utils/bufprint.c | 230 | ||||
-rw-r--r-- | android/utils/bufprint.h | 76 | ||||
-rw-r--r-- | android/utils/debug.c | 141 | ||||
-rw-r--r-- | android/utils/debug.h | 91 | ||||
-rw-r--r-- | android/utils/dirscanner.c | 203 | ||||
-rw-r--r-- | android/utils/dirscanner.h | 51 | ||||
-rw-r--r-- | android/utils/display-quartz.m | 111 | ||||
-rw-r--r-- | android/utils/display.c | 245 | ||||
-rw-r--r-- | android/utils/display.h | 31 | ||||
-rw-r--r-- | android/utils/filelock.c | 414 | ||||
-rw-r--r-- | android/utils/filelock.h | 38 | ||||
-rw-r--r-- | android/utils/ini.c | 416 | ||||
-rw-r--r-- | android/utils/ini.h | 131 | ||||
-rw-r--r-- | android/utils/misc.c | 193 | ||||
-rw-r--r-- | android/utils/misc.h | 67 | ||||
-rw-r--r-- | android/utils/path.c | 559 | ||||
-rw-r--r-- | android/utils/path.h | 152 | ||||
-rw-r--r-- | android/utils/reflist.c | 203 | ||||
-rw-r--r-- | android/utils/reflist.h | 129 | ||||
-rw-r--r-- | android/utils/stralloc.c | 300 | ||||
-rw-r--r-- | android/utils/stralloc.h | 60 | ||||
-rw-r--r-- | android/utils/system.c | 172 | ||||
-rw-r--r-- | android/utils/system.h | 161 | ||||
-rw-r--r-- | android/utils/tempfile.c | 198 | ||||
-rw-r--r-- | android/utils/tempfile.h | 52 | ||||
-rw-r--r-- | android/utils/timezone.c | 721 | ||||
-rw-r--r-- | android/utils/timezone.h | 31 |
27 files changed, 5176 insertions, 0 deletions
diff --git a/android/utils/bufprint.c b/android/utils/bufprint.c new file mode 100644 index 0000000..4309a4b --- /dev/null +++ b/android/utils/bufprint.c @@ -0,0 +1,230 @@ +/* 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/bufprint.h" +#include "android/utils/path.h" +#include "android/utils/debug.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include <unistd.h> +# include <sys/stat.h> +#endif + +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) + + +/** 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]; + int len; + char* sep; + + len = GetModuleFileName( 0, appDir, sizeof(appDir)-1 ); + if (len == 0) { + fprintf(stderr, "PANIC CITY!!\n"); + exit(1); + } + if (len >= (int)sizeof(appDir)) { + len = sizeof(appDir)-1; + appDir[len] = 0; + } + + 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 + const char* home = getenv("ANDROID_SDK_HOME"); + if (home != NULL) { + return bufprint(buff, end, "%s\\%s", home, _ANDROID_PATH ); + } else { + 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" ); + strncpy(path, "C:\\Temp", sizeof(path) ); + } + strncat( path, "\\AndroidEmulator", sizeof(path)-1 ); + path_mkdir(path, 0744); + + 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; +} + diff --git a/android/utils/bufprint.h b/android/utils/bufprint.h new file mode 100644 index 0000000..32d64dc --- /dev/null +++ b/android/utils/bufprint.h @@ -0,0 +1,76 @@ +/* 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. +*/ + +#ifndef _ANDROID_UTILS_BUFPRINT_H +#define _ANDROID_UTILS_BUFPRINT_H + +#include <stdarg.h> + +/** FORMATTED BUFFER PRINTING + ** + ** bufprint() allows your to easily and safely append formatted string + ** content to a given bounded character buffer, in a way that is easier + ** to use than raw snprintf() + ** + ** 'buffer' is the start position in the buffer, + ** 'buffend' is the end of the buffer, the function assumes (buffer <= buffend) + ** 'format' is a standard printf-style format string, followed by any number + ** of formatting arguments + ** + ** the function returns the next position in the buffer if everything fits + ** in it. in case of overflow or formatting error, it will always return "buffend" + ** + ** this allows you to chain several calls to bufprint() and only check for + ** overflow at the end, for exemple: + ** + ** char buffer[1024]; + ** char* p = buffer; + ** char* end = p + sizeof(buffer); + ** + ** p = bufprint(p, end, "%s/%s", first, second); + ** p = bufprint(p, end, "/%s", third); + ** if (p >= end) ---> overflow + ** + ** as a convenience, the appended string is zero-terminated if there is no overflow. + ** (this means that even if p >= end, the content of "buffer" is zero-terminated) + ** + ** vbufprint() is a variant that accepts a va_list argument + **/ + +extern char* vbufprint(char* buffer, char* buffend, const char* fmt, va_list args ); +extern char* bufprint (char* buffer, char* buffend, const char* fmt, ... ); + +/** USEFUL DIRECTORY SUPPORT + ** + ** bufprint_add_dir() appends the application's directory to a given bounded buffer + ** + ** bufprint_config_path() appends the applications' user-specific configuration directory + ** to a bounded buffer. on Unix this is usually ~/.android, and something a bit more + ** complex on Windows + ** + ** bufprint_config_file() appends the name of a file or directory relative to the + ** user-specific configuration directory to a bounded buffer. this really is equivalent + ** to concat-ing the config path + path separator + 'suffix' + ** + ** bufprint_temp_dir() appends the temporary directory's path to a given bounded buffer + ** + ** bufprint_temp_file() appens the name of a file or directory relative to the + ** temporary directory. equivalent to concat-ing the temp path + path separator + 'suffix' + **/ + +extern char* bufprint_app_dir (char* buffer, char* buffend); +extern char* bufprint_config_path(char* buffer, char* buffend); +extern char* bufprint_config_file(char* buffer, char* buffend, const char* suffix); +extern char* bufprint_temp_dir (char* buffer, char* buffend); +extern char* bufprint_temp_file (char* buffer, char* buffend, const char* suffix); + +#endif /* _ANDROID_UTILS_BUFPRINT_H */ diff --git a/android/utils/debug.c b/android/utils/debug.c new file mode 100644 index 0000000..32936d2 --- /dev/null +++ b/android/utils/debug.c @@ -0,0 +1,141 @@ +/* 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/debug.h" +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +void +dprint( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + fprintf( stdout, "emulator: "); + vfprintf( stdout, format, args ); + fprintf( stdout, "\n" ); + va_end( args ); +} + +void +dprintn( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + vfprintf( stdout, format, args ); + va_end( args ); +} + +void +dprintnv( const char* format, va_list args ) +{ + vfprintf( stdout, format, args ); +} + + +void +dwarning( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + dprintn( "emulator: WARNING: " ); + dprintnv( format, args ); + dprintn( "\n" ); + va_end( args ); +} + + +void +derror( const char* format, ... ) +{ + va_list args; + va_start( args, format ); + dprintn( "emulator: ERROR: " ); + dprintnv( format, args ); + dprintn( "\n" ); + va_end( args ); +} + +/** STDOUT/STDERR REDIRECTION + ** + ** allows you to shut temporarily shutdown stdout/stderr + ** this is useful to get rid of debug messages from ALSA and esd + ** on Linux. + **/ +static int stdio_disable_count; +static int stdio_save_out_fd; +static int stdio_save_err_fd; + +#ifdef _WIN32 +extern void +stdio_disable( void ) +{ + if (++stdio_disable_count == 1) { + int null_fd, out_fd, err_fd; + fflush(stdout); + out_fd = _fileno(stdout); + err_fd = _fileno(stderr); + stdio_save_out_fd = _dup(out_fd); + stdio_save_err_fd = _dup(err_fd); + null_fd = _open( "NUL", _O_WRONLY ); + _dup2(null_fd, out_fd); + _dup2(null_fd, err_fd); + close(null_fd); + } +} + +extern void +stdio_enable( void ) +{ + if (--stdio_disable_count == 0) { + int out_fd, err_fd; + fflush(stdout); + out_fd = _fileno(stdout); + err_fd = _fileno(stderr); + _dup2(stdio_save_out_fd, out_fd); + _dup2(stdio_save_err_fd, err_fd); + _close(stdio_save_out_fd); + _close(stdio_save_err_fd); + } +} +#else +extern void +stdio_disable( void ) +{ + if (++stdio_disable_count == 1) { + int null_fd, out_fd, err_fd; + fflush(stdout); + out_fd = fileno(stdout); + err_fd = fileno(stderr); + stdio_save_out_fd = dup(out_fd); + stdio_save_err_fd = dup(err_fd); + null_fd = open( "/dev/null", O_WRONLY ); + dup2(null_fd, out_fd); + dup2(null_fd, err_fd); + close(null_fd); + } +} + +extern void +stdio_enable( void ) +{ + if (--stdio_disable_count == 0) { + int out_fd, err_fd; + fflush(stdout); + out_fd = fileno(stdout); + err_fd = fileno(stderr); + dup2(stdio_save_out_fd, out_fd); + dup2(stdio_save_err_fd, err_fd); + close(stdio_save_out_fd); + close(stdio_save_err_fd); + } +} +#endif diff --git a/android/utils/debug.h b/android/utils/debug.h new file mode 100644 index 0000000..fdf93c9 --- /dev/null +++ b/android/utils/debug.h @@ -0,0 +1,91 @@ +/* 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. +*/ +#ifndef _ANDROID_UTILS_DEBUG_H +#define _ANDROID_UTILS_DEBUG_H + +#include <stdarg.h> + +#define VERBOSE_TAG_LIST \ + _VERBOSE_TAG(init, "emulator initialization") \ + _VERBOSE_TAG(console, "control console") \ + _VERBOSE_TAG(modem, "emulated GSM modem") \ + _VERBOSE_TAG(radio, "emulated GSM AT Command channel") \ + _VERBOSE_TAG(keys, "key bindings & presses") \ + _VERBOSE_TAG(slirp, "internal router/firewall") \ + _VERBOSE_TAG(timezone, "host timezone detection" ) \ + _VERBOSE_TAG(socket, "network sockets") \ + _VERBOSE_TAG(proxy, "network proxy support") \ + _VERBOSE_TAG(audio, "audio sub-system") \ + _VERBOSE_TAG(audioin, "audio input backend") \ + _VERBOSE_TAG(audioout, "audio output backend") \ + _VERBOSE_TAG(surface, "video surface support") \ + _VERBOSE_TAG(qemud, "qemud multiplexer daemon") \ + _VERBOSE_TAG(gps, "emulated GPS") \ + _VERBOSE_TAG(nand_limits, "nand/flash read/write thresholding") \ + _VERBOSE_TAG(hw_control, "emulated power/flashlight/led/vibrator") \ + _VERBOSE_TAG(avd_config, "android virtual device configuration") \ + +#define _VERBOSE_TAG(x,y) VERBOSE_##x, +typedef enum { + VERBOSE_TAG_LIST + VERBOSE_MAX /* do not remove */ +} VerboseTag; +#undef _VERBOSE_TAG + +/* defined in android_main.c */ +extern unsigned long android_verbose; + +#define VERBOSE_ENABLE(tag) \ + android_verbose |= (1 << VERBOSE_##tag) + +#define VERBOSE_DISABLE(tag) \ + android_verbose &= (1 << VERBOSE_##tag) + +#define VERBOSE_CHECK(tag) \ + ((android_verbose & (1 << VERBOSE_##tag)) != 0) + +#define VERBOSE_CHECK_ANY() \ + (android_verbose != 0) + +#define VERBOSE_PRINT(tag,...) \ + do { if (VERBOSE_CHECK(tag)) dprint(__VA_ARGS__); } while (0) + +/** DEBUG TRACE SUPPORT + ** + ** debug messages can be sent by calling these function + ** + ** 'dprint' prints the message, then appends a '\n\ + ** 'dprintn' simply prints the message as is + ** 'dprintnv' allows you to use a va_list argument + ** 'dwarning' prints a warning message, then appends a '\n' + ** 'derror' prints a severe error message, then appends a '\n' + */ + +extern void dprint( const char* format, ... ); +extern void dprintn( const char* format, ... ); +extern void dprintnv( const char* format, va_list args ); +extern void dwarning( const char* format, ... ); +extern void derror( const char* format, ... ); + +/** STDOUT/STDERR REDIRECTION + ** + ** allows you to shut temporarily shutdown stdout/stderr + ** this is useful to get rid of debug messages from ALSA and esd + ** on Linux. + **/ + +extern void stdio_disable( void ); +extern void stdio_enable( void ); + +/* */ + +#endif /* _ANDROID_UTILS_DEBUG_H */ diff --git a/android/utils/dirscanner.c b/android/utils/dirscanner.c new file mode 100644 index 0000000..fc63ef0 --- /dev/null +++ b/android/utils/dirscanner.c @@ -0,0 +1,203 @@ +/* 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/dirscanner.h" +#include "android/utils/bufprint.h" +#include "qemu-common.h" +#include <stddef.h> + +#define DIRSCANNER_BASE \ + char root[PATH_MAX]; \ + int rootLen; \ + char full[PATH_MAX]; \ + + +#if _WIN32 + +#include <io.h> + +struct DirScanner { + DIRSCANNER_BASE + intptr_t findIndex1; + struct _finddata_t findData; +}; + +/* note: findIndex1 contains the find index + 1 + * so a value of 0 means 'invalid' + */ + +static int +_dirScannerInit( DirScanner* s ) +{ + char* p = s->root + s->rootLen; + char* end = s->root + sizeof s->root; + int ret; + + /* create file spec by appending \* to root */ + p = bufprint(p, end, "\\*"); + if (p >= end) + return -1; + + ret = _findfirst(s->root, &s->findData); + + s->findIndex1 = ret+1; + return ret; +} + +static void +_dirScanner_done( DirScanner* s ) +{ + if (s->findIndex1 > 0) { + _findclose(s->findIndex1-1); + s->findIndex1 = 0; + } +} + +const char* +dirScanner_next( DirScanner* s ) +{ + char* ret = NULL; + + if (!s || s->findIndex1 <= 0) + return NULL; + + while (ret == NULL) { + ret = s->findData.name; + + /* ignore special directories */ + if (!strcmp(ret, ".") || !strcmp(ret, "..")) { + ret = NULL; + } + /* find next one */ + if (_findnext(s->findIndex1-1, &s->findData) < 0) { + _dirScanner_done(s); + break; + } + } + return ret; +} + +#else /* !_WIN32 */ + +#include <dirent.h> +struct DirScanner { + DIRSCANNER_BASE + DIR* dir; + struct dirent* entry; +}; + +static int +_dirScannerInit( DirScanner* s ) +{ + s->dir = opendir(s->root); + + if (s->dir == NULL) + return -1; + + s->entry = NULL; + return 0; +} + +static void +_dirScanner_done( DirScanner* s ) +{ + if (s->dir) { + closedir(s->dir); + s->dir = NULL; + } +} + +const char* +dirScanner_next( DirScanner* s ) +{ + char* ret = NULL; + + if (!s || s->dir == NULL) + return NULL; + + for (;;) + { + /* read new entry if needed */ + s->entry = readdir(s->dir); + if (s->entry == NULL) { + _dirScanner_done(s); + break; + } + + /* ignore special directories */ + ret = s->entry->d_name; + + if (!strcmp(ret,".") || !strcmp(ret,"..")) { + ret = NULL; + continue; + } + break; + } + return ret; +} + +#endif /* !_WIN32 */ + +DirScanner* +dirScanner_new ( const char* rootPath ) +{ + DirScanner* s = qemu_mallocz(sizeof *s); + char* p = s->root; + char* end = p + sizeof s->root; + + p = bufprint(p, end, "%s", rootPath); + if (p >= end) + goto FAIL; + + s->rootLen = (p - s->root); + + if (_dirScannerInit(s) < 0) + goto FAIL; + + return s; + +FAIL: + dirScanner_free(s); + return NULL; +} + + +void +dirScanner_free( DirScanner* s ) +{ + if (!s) + return; + + _dirScanner_done(s); + qemu_free(s); +} + + +const char* +dirScanner_nextFull( DirScanner* s ) +{ + const char* name = dirScanner_next(s); + char* p; + char* end; + + if (name == NULL) + return NULL; + + p = s->full; + end = p + sizeof s->full; + + p = bufprint(p, end, "%.*s/%s", s->rootLen, s->root, name); + if (p >= end) { + /* ignore if the full name is too long */ + return dirScanner_nextFull(s); + } + return s->full; +} diff --git a/android/utils/dirscanner.h b/android/utils/dirscanner.h new file mode 100644 index 0000000..871b05e --- /dev/null +++ b/android/utils/dirscanner.h @@ -0,0 +1,51 @@ +/* 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. +*/ +#ifndef _ANDROID_UTILS_DIR_H +#define _ANDROID_UTILS_DIR_H + +/* simple utility to parse directories for files */ +/* needed because Unix and Windows don't use the same stuff */ + +typedef struct DirScanner DirScanner; + +/* Create a new directory scanner object from a given rootPath. + * returns NULL in case of failure (error code in errno) + */ +DirScanner* dirScanner_new ( const char* rootPath ); + +/* Destroy a given directory scanner. You must always call + * this function to release proper system resources. + */ +void dirScanner_free( DirScanner* s ); + +/* Get the name of the next file from a directory scanner. + * Returns NULL when there is no more elements in the list. + * + * This is only the file name, use dirScanner_nextFull to + * get its full path. + * + * This will never return '.' and '..'. + * + * The returned string is owned by the scanner, and will + * change on the next call to this function or when the + * scanner is destroyed. + */ +const char* dirScanner_next( DirScanner* s ); + +/* A variant of dirScanner_next() which returns the full path + * to the next directory element. + */ +const char* dirScanner_nextFull( DirScanner* s ); + +/* */ + +#endif /* _ANDROID_UTILS_DIR_H */ diff --git a/android/utils/display-quartz.m b/android/utils/display-quartz.m new file mode 100644 index 0000000..594657e --- /dev/null +++ b/android/utils/display-quartz.m @@ -0,0 +1,111 @@ +/* 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. +*/ + +/* this is the Quartz-specific implementation of + * <android/utils/display.h> + */ + +#include "android/utils/display.h" +#include "android/utils/debug.h" + +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) + +#include <stdio.h> +#include <Cocoa/Cocoa.h> +#include <Carbon/Carbon.h> +#include <SDL_syswm.h> + +int +get_monitor_resolution( int *px_dpi, int *py_dpi ) +{ + fprintf(stderr, "emulator: FIXME: implement get_monitor_resolution on OS X\n" ); + return -1; +} + +int +get_nearest_monitor_rect( int *x, int *y, int *width, int *height ) +{ + SDL_SysWMinfo info; + NSWindow* window; + + SDL_VERSION(&info.version); + if ( SDL_GetWMInfo(&info) < 0 ) { + D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError()); + return -1; + } + window = info.nsWindowPtr; + if (window == NULL) { + D( "%s: SDL_GetWMInfo() returned NULL NSWindow ptr", + __FUNCTION__ ); + return -1; + } + else + { + NSRect frame = [ window frame ]; + int fx1 = frame.origin.x; + int fy1 = frame.origin.y; + int fx2 = frame.size.width + fx1; + int fy2 = frame.size.height + fy1; + NSArray* screens = [ NSScreen screens ]; + unsigned int count = [ screens count ]; + int bestScreen = -1; + int bestArea = 0; + + unsigned int n; + printf( "window frame (%d,%d) (%d,%d)\n", fx1, fy1, fx2, fy2 ); + + /* we need to compute which screen has the most window pixels */ + for (n = 0; n < count; n++) { + NSScreen* screen = [ screens objectAtIndex: n ]; + NSRect vis = [ screen visibleFrame ]; + int vx1 = vis.origin.x; + int vy1 = vis.origin.y; + int vx2 = vis.size.width + vx1; + int vy2 = vis.size.height + vy1; + int cx1, cx2, cy1, cy2, cArea; + + //printf( "screen %d/%d frame (%d,%d) (%d,%d)\n", n+1, count, + // vx1, vy1, vx2, vy2 ); + + if (fx1 >= vx2 || vx1 >= fx2 || fy1 >= vy2 || vy1 >= fy2) + continue; + + cx1 = (fx1 < vx1) ? vx1 : fx1; + cx2 = (fx2 > vx2) ? vx2 : fx2; + cy1 = (fy1 < vy1) ? vy1 : fy1; + cy2 = (fy2 > vy2) ? vy2 : fy2; + + if (cx1 >= cx2 || cy1 >= cy2) + continue; + + cArea = (cx2-cx1)*(cy2-cy1); + + if (bestScreen < 0 || cArea > bestArea) { + bestScreen = n; + bestArea = cArea; + } + } + if (bestScreen < 0) + bestScreen = 0; + + { + NSScreen* screen = [ screens objectAtIndex: bestScreen ]; + NSRect vis = [ screen visibleFrame ]; + + *x = vis.origin.x; + *y = vis.origin.y; + *width = vis.size.width; + *height = vis.size.height; + } + return 0; + } +}; diff --git a/android/utils/display.c b/android/utils/display.c new file mode 100644 index 0000000..e1ba507 --- /dev/null +++ b/android/utils/display.c @@ -0,0 +1,245 @@ +/* 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/display.h" +#include "android/utils/debug.h" + +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) + +/** 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 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <SDL_syswm.h> + +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; +} + +int +get_nearest_monitor_rect( int *x, int *y, int *width, int *height ) +{ + SDL_SysWMinfo info; + HMONITOR monitor; + MONITORINFO monitorInfo; + + SDL_VERSION(&info.version); + + if ( !SDL_GetWMInfo(&info) ) { + D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError()); + return -1; + } + + monitor = MonitorFromWindow( info.window, MONITOR_DEFAULTTONEAREST ); + monitorInfo.cbSize = sizeof(monitorInfo); + GetMonitorInfo( monitor, &monitorInfo ); + + *x = monitorInfo.rcMonitor.left; + *y = monitorInfo.rcMonitor.top; + *width = monitorInfo.rcMonitor.right - *x; + *height = monitorInfo.rcMonitor.bottom - *y; + + D("%s: found (x,y,w,h)=(%d,%d,%d,%d)", __FUNCTION__, + *x, *y, *width, *height); + + return 0; +} + + +#elif defined __APPLE__ + +/* the real implementation is in display-quartz.m, but + * unfortunately, the Android build system doesn't know + * how to build Objective-C sources, so provide stubs + * here instead. + * + * CONFIG_NO_COCOA is set by Makefile.android + */ + +#ifdef CONFIG_NO_COCOA +int +get_monitor_resolution( int *px_dpi, int *py_dpi ) +{ + return -1; +} + +int +get_nearest_monitor_rect( int *x, int *y, int *width, int *height ) +{ + return -1; +} +#endif /* CONFIG_NO_COCOA */ + +#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; +} + +int +get_nearest_monitor_rect( int *x, int *y, int *width, int *height ) +{ + SDL_SysWMinfo info; + Display* display; + int screen; + + 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); + + *x = 0; + *y = 0; + *width = FF(XDisplayWidth)(display, screen); + *height = FF(XDisplayHeight)(display, screen); + + D("%s: found (x,y,w,h)=(%d,%d,%d,%d)", __FUNCTION__, + *x, *y, *width, *height); + + return 0; +} + +#endif diff --git a/android/utils/display.h b/android/utils/display.h new file mode 100644 index 0000000..7254aca --- /dev/null +++ b/android/utils/display.h @@ -0,0 +1,31 @@ +/* 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. +*/ + +#ifndef _ANDROID_UTILS_DISPLAY_H +#define _ANDROID_UTILS_DISPLAY_H + +/** 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) + **/ +extern int get_monitor_resolution( int *px_dpi, int *py_dpi ); + +/** return the size in pixels of the nearest monitor for the current window. + ** this is used to implement full-screen presentation mode. + **/ + +extern int get_nearest_monitor_rect( int *x, int *y, int *width, int *height ); + +#endif /* _ANDROID_UTILS_DISPLAY_H */ diff --git a/android/utils/filelock.c b/android/utils/filelock.c new file mode 100644 index 0000000..a8b18da --- /dev/null +++ b/android/utils/filelock.c @@ -0,0 +1,414 @@ +/* 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/filelock.h" +#include "android/utils/path.h" +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/stat.h> +#include <time.h> +#include <fcntl.h> +#ifdef _WIN32 +# include <process.h> +# include <windows.h> +# include <tlhelp32.h> +#else +# include <sys/types.h> +# include <unistd.h> +# include <signal.h> +#endif + +#define D(...) ((void)0) + +#ifndef CHECKED +# ifdef _WIN32 +# define CHECKED(ret, call) (ret) = (call) +# else +# define CHECKED(ret, call) do { (ret) = (call); } while ((ret) < 0 && errno == EINTR) +# endif +#endif + +/** 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 ( path_delete_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 + path_delete_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; +} diff --git a/android/utils/filelock.h b/android/utils/filelock.h new file mode 100644 index 0000000..9bc86b5 --- /dev/null +++ b/android/utils/filelock.h @@ -0,0 +1,38 @@ +/* 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. +*/ + +#ifndef _ANDROID_UTILS_FILELOCK_H +#define _ANDROID_UTILS_FILELOCK_H + +/** 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(), the function will return + ** NULL only if the corresponding path is already locked by another emulator + ** of if the path is read-only. + ** + ** note that 'path' can designate a non-existing path and that the lock creation + ** function can detect stale file locks that can longer when the emulator + ** crashes unexpectedly, and will happily clean them for you. + ** + ** you can call filelock_release() to release a file lock explicitely. otherwise + ** all file locks are automatically released when the program exits. + **/ + +typedef struct FileLock FileLock; + +extern FileLock* filelock_create ( const char* path ); +extern void filelock_release( FileLock* lock ); + +#endif /* _ANDROID_UTILS_FILELOCK_H */ diff --git a/android/utils/ini.c b/android/utils/ini.c new file mode 100644 index 0000000..95bb4e3 --- /dev/null +++ b/android/utils/ini.c @@ -0,0 +1,416 @@ +/* Copyright (C) 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/ini.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <errno.h> +#include "android/utils/debug.h" +#include "android/utils/system.h" /* for ASTRDUP */ +#include "android/utils/bufprint.h" +#include "osdep.h" + +/* W() is used to print warnings, D() to print debugging info */ +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(avd_config,__VA_ARGS__) + +/* a simple .ini file parser and container for Android + * no sections support. see android/utils/ini.h for + * more details on the supported file format. + */ +typedef struct { + char* key; + char* value; +} IniPair; + +struct IniFile { + int numPairs; + int maxPairs; + IniPair* pairs; +}; + +void +iniFile_free( IniFile* i ) +{ + int nn; + for (nn = 0; nn < i->numPairs; nn++) { + AFREE(i->pairs[nn].key); + i->pairs[nn].key = NULL; + i->pairs[nn].value = NULL; + } + AFREE(i->pairs); + AFREE(i); +} + +static IniFile* +iniFile_alloc( void ) +{ + IniFile* i; + + ANEW0(i); + return i; +} + +static void +iniFile_addPair( IniFile* i, const char* key, int keyLen, + const char* value, int valueLen ) +{ + IniPair* pair; + + if (i->numPairs >= i->maxPairs) { + int oldMax = i->maxPairs; + int newMax = oldMax + (oldMax >> 1) + 4; + + AARRAY_RENEW(i->pairs, newMax); + i->maxPairs = newMax; + } + + pair = i->pairs + i->numPairs; + + AARRAY_NEW(pair->key, keyLen + valueLen + 2); + memcpy(pair->key, key, keyLen); + pair->key[keyLen] = 0; + + pair->value = pair->key + keyLen + 1; + memcpy(pair->value, value, valueLen); + pair->value[valueLen] = 0; + + i->numPairs += 1; +} + +const char* +iniFile_getValue( IniFile* i, const char* key ) +{ + if (i && key) { + int nn; + + for (nn = 0; nn < i->numPairs; nn++) { + if (!strcmp(i->pairs[nn].key,key)) + return i->pairs[nn].value; + } + } + return NULL; +} + +int +iniFile_getPairCount( IniFile* i ) +{ + return i ? i->numPairs : 0; +} + +void +iniFile_getPair( IniFile* i, + int index, + const char* *pKey, + const char* *pValue ) +{ + const char* key = NULL; + const char* value = NULL; + + if (i && index >= 0 && index < i->numPairs) { + key = i->pairs[index].key; + value = i->pairs[index].value; + } + *pKey = key; + *pValue = value; +} + +/* NOTE: we avoid using <ctype.h> functions to avoid locale-specific + * behaviour that can be the source of strange bugs. + */ + +static const char* +skipSpaces( const char* p ) +{ + while (*p == ' ' || *p == '\t') + p ++; + return p; +} + +static const char* +skipToEOL( const char* p ) +{ + while (*p && (*p != '\n' && *p != '\r')) + p ++; + + if (*p) { + p ++; + if (p[-1] == '\r' && p[0] == '\n') + p ++; + } + return p; +} + +static int +isKeyStartChar( int c ) +{ + return ((unsigned)(c-'a') < 26 || + (unsigned)(c-'A') < 26 || + c == '_'); +} + +static int +isKeyChar( int c ) +{ + return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-'); +} + +IniFile* +iniFile_newFromMemory( const char* text, const char* fileName ) +{ + const char* p = text; + IniFile* ini = iniFile_alloc(); + int lineno = 0; + + if (!fileName) + fileName = "<memoryFile>"; + + D("%s: parsing as .ini file", fileName); + + while (*p) { + const char* key; + int keyLen; + const char* value; + int valueLen; + + lineno += 1; + + /* skip leading whitespace */ + p = skipSpaces(p); + + /* skip comments and empty lines */ + if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') { + p = skipToEOL(p); + continue; + } + + /* check the key name */ + key = p++; + if (!isKeyStartChar(*key)) { + p = skipToEOL(p); + W("%4d: key name doesn't start with valid character. line ignored", + lineno); + continue; + } + + while (isKeyChar(*p)) + p++; + + keyLen = p - key; + p = skipSpaces(p); + + /* check the equal */ + if (*p != '=') { + W("%4d: missing expected assignment operator (=). line ignored", + lineno); + p = skipToEOL(p); + continue; + } + p += 1; + + /* skip spaces before the value */ + p = skipSpaces(p); + value = p; + + /* find the value */ + while (*p && (*p != '\n' && *p != '\r')) + p += 1; + + /* remove trailing spaces */ + while (p > value && (p[-1] == ' ' || p[-1] == '\t')) + p --; + + valueLen = p - value; + + iniFile_addPair(ini, key, keyLen, value, valueLen); + D("%4d: KEY='%.*s' VALUE='%.*s'", lineno, + keyLen, key, valueLen, value); + + p = skipToEOL(p); + } + + D("%s: parsing finished", fileName); + + return ini; +} + +IniFile* +iniFile_newFromFile( const char* filepath ) +{ + FILE* fp = fopen(filepath, "rt"); + char* text; + long size; + IniFile* ini = NULL; + + if (fp == NULL) { + D("could not open .ini file: %s: %s", + filepath, strerror(errno)); + return NULL; + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* avoid reading a very large file that was passed by mistake + * this threshold is quite liberal. + */ +#define MAX_INI_FILE_SIZE 655360 + + if (size < 0 || size > MAX_INI_FILE_SIZE) { + W("hardware configuration file '%s' too large (%ld bytes)", + filepath, size); + goto EXIT; + } + + /* read the file, add a sentinel at the end of it */ + AARRAY_NEW(text, size+1); + fread(text, 1, size, fp); + text[size] = 0; + + ini = iniFile_newFromMemory(text, filepath); + AFREE(text); + +EXIT: + fclose(fp); + return ini; +} + +int +iniFile_saveToFile( IniFile* f, const char* filepath ) +{ + FILE* fp = fopen(filepath, "wt"); + IniPair* pair = f->pairs; + IniPair* pairEnd = pair + f->numPairs; + int result = 0; + + if (fp == NULL) { + D("could not create .ini file: %s: %s", + filepath, strerror(errno)); + return -1; + } + + for ( ; pair < pairEnd; pair++ ) { + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value); + if (fwrite(temp, p - temp, 1, fp) != 1) { + result = -1; + break; + } + } + + fclose(fp); + return result; +} + +char* +iniFile_getString( IniFile* f, const char* key ) +{ + const char* val = iniFile_getValue(f, key); + + if (!val) + return NULL; + + return ASTRDUP(val); +} + +int +iniFile_getInteger( IniFile* f, const char* key, int defaultValue ) +{ + const char* valueStr = iniFile_getValue(f, key); + int value = defaultValue; + + if (valueStr != NULL) { + char* end; + long l = strtol(valueStr, &end, 10); + if (end != NULL && end[0] == 0 && (int)l == l) + value = l; + } + return value; +} + +double +iniFile_getDouble( IniFile* f, const char* key, double defaultValue ) +{ + const char* valueStr = iniFile_getValue(f, key); + double value = defaultValue; + + if (valueStr != NULL) { + char* end; + double d = strtod(valueStr, &end); + if (end != NULL && end[0] == 0) + value = d; + } + return value; +} + +int +iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue ) +{ + const char* value = iniFile_getValue(f, key); + + if (!value) + value = defaultValue; + + if (!strcmp(value,"1") || + !strcmp(value,"yes") || + !strcmp(value,"YES") || + !strcmp(value,"true") || + !strcmp(value,"TRUE")) + { + return 1; + } + else + return 0; +} + +int64_t +iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue ) +{ + const char* valStr = iniFile_getValue(f, key); + int64_t value = 0; + + if (!valStr) + valStr = defaultValue; + + if (valStr != NULL) { + char* end; + + value = strtoll(valStr, &end, 10); + if (*end == 'k' || *end == 'K') + value *= 1024ULL; + else if (*end == 'm' || *end == 'M') + value *= 1024*1024ULL; + else if (*end == 'g' || *end == 'G') + value *= 1024*1024*1024ULL; + } + return value; +} + +int64_t +iniFile_getInt64( IniFile* f, const char* key, int64_t defaultValue ) +{ + const char* valStr = iniFile_getValue(f, key); + int64_t value = defaultValue; + + if (valStr != NULL) { + char* end; + int64_t d; + + d = strtoll(valStr, &end, 10); + if (end != NULL && end[0] == 0) + value = d; + } + return value; +} + diff --git a/android/utils/ini.h b/android/utils/ini.h new file mode 100644 index 0000000..a176bfe --- /dev/null +++ b/android/utils/ini.h @@ -0,0 +1,131 @@ +/* Copyright (C) 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. +*/ +#ifndef _ANDROID_UTILS_INI_H +#define _ANDROID_UTILS_INI_H + +#include <stdint.h> + +/* the emulator supports a simple .ini file format for its configuration + * files. Here's the BNF for it: + * + * file := <line>* + * line := <comment> | <LF> | <assignment> + * comment := (';'|'#') <noLF>* <LF> + * assignment := <space>* <keyName> <space>* '=' <space>* <valueString> <space>* <LF> + * keyName := <keyNameStartChar> <keyNameChar>* + * keyNameStartChar := [A-Za-z_] + * keyNameChar := [A-Za-z0-9_.-] + * valueString := <noLF>* + * space := ' ' | '\t' + * LF := '\r\n' | '\n' | '\r' + * noLF := [^<LF>] + * + * Or, in English: + * + * - no support for sections + * - empty lines are ignored, as well as lines beginning with ';' or '#' + * - lines must be of the form: "<keyName> = <value>" + * - key names must start with a letter or an underscore + * - other key name characters can be letters, digits, underscores, dots or dashes + * + * - leading and trailing space are allowed and ignored before/after the key name + * and before/after the value + * + * - there is no restriction on the value, except that it can't contain + * leading/trailing space/tab characters or newline/charfeed characters + * + * - empty values are possible, and will be stored as an empty string. + * - any badly formatted line is discarded (and will print a warning) + * + */ + +/* an opaque structure used to model an .ini configuration file */ +typedef struct IniFile IniFile; + +/* creates a new IniFile object from a config file loaded in memory. + * 'fileName' is only used when writing a warning to stderr in case + * of badly formed output + */ +IniFile* iniFile_newFromMemory( const char* text, const char* fileName ); + +/* creates a new IniFile object from a file path, + * returns NULL if the file cannot be opened. + */ +IniFile* iniFile_newFromFile( const char* filePath); + +/* try to write an IniFile into a given file. + * returns 0 on success, -1 on error (see errno for error code) + */ +int iniFile_saveToFile( IniFile* f, const char* filePath ); + +/* free an IniFile object */ +void iniFile_free( IniFile* f ); + +/* returns the number of (key.value) pairs in an IniFile */ +int iniFile_getPairCount( IniFile* f ); + +/* return a specific (key,value) pair from an IniFile. + * if the index is not correct, both '*pKey' and '*pValue' will be + * set to NULL. + * + * you should probably use iniFile_getValue() and its variants instead + */ +void iniFile_getPair( IniFile* f, + int index, + const char* *pKey, + const char* *pValue ); + +/* returns the value of a given key from an IniFile. + * NULL if the key is not assigned in the corresponding configuration file + */ +const char* iniFile_getValue( IniFile* f, const char* key ); + +/* returns a copy of the value of a given key, or NULL + */ +char* iniFile_getString( IniFile* f, const char* key ); + +/* returns an integer value, or a default in case the value string is + * missing or badly formatted + */ +int iniFile_getInteger( IniFile* f, const char* key, int defaultValue ); + +/* returns a 64-bit integer value, or a default in case the value string is + * missing or badly formatted + */ +int64_t iniFile_getInt64( IniFile* f, const char* key, int64_t defaultValue ); + +/* returns a double value, or a default in case the value string is + * missing or badly formatted + */ +double iniFile_getDouble( IniFile* f, const char* key, double defaultValue ); + +/* returns a copy of a given key's value, if any, or NULL if it is missing + * caller must call free() to release it */ +char* iniFile_getString( IniFile* f, const char* key ); + +/* parses a key value as a boolean. Accepted values are "1", "0", "yes", "YES", + * "no" and "NO". Returns either 1 or 0. + * note that the default value must be provided as a string too + */ +int iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue ); + +/* parses a key value as a disk size. this means it can be an integer followed + * by a suffix that can be one of "mMkKgG" which correspond to KiB, MiB and GiB + * multipliers. + * + * NOTE: we consider that 1K = 1024, not 1000. + */ +int64_t iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue ); + +/* */ + +#endif /* _ANDROID_UTILS_INI_H */ diff --git a/android/utils/misc.c b/android/utils/misc.c new file mode 100644 index 0000000..818ab78 --- /dev/null +++ b/android/utils/misc.c @@ -0,0 +1,193 @@ +/* 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/misc.h" +#include "android/utils/stralloc.h" +#include "android/utils/debug.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +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 +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 ); +} + + +/** 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_format( const char* fmt, ... ) +{ + va_list args; + char* result; + STRALLOC_DEFINE(s); + va_start(args, fmt); + stralloc_formatv(s, fmt, args); + va_end(args); + result = stralloc_to_tempstr(s); + stralloc_reset(s); + return result; +} + +/** 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 = stralloc_to_tempstr( s ); + stralloc_reset(s); + return q; +} + +extern const char* +quote_str( const char* str ) +{ + int len = strlen(str); + return quote_bytes( str, len ); +} + +/** 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]; +} diff --git a/android/utils/misc.h b/android/utils/misc.h new file mode 100644 index 0000000..0db1e28 --- /dev/null +++ b/android/utils/misc.h @@ -0,0 +1,67 @@ +/* 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. +*/ +#ifndef _ANDROID_UTILS_MISC_H +#define _ANDROID_UTILS_MISC_H + +#include <stdint.h> + +/** TABULAR OUTPUT + ** + ** prints a list of strings in row/column format + ** + **/ + +extern void print_tabular( const char** strings, int count, + const char* prefix, int width ); + +/** CHARACTER TRANSLATION + ** + ** converts one character into another in strings + **/ + +extern void buffer_translate_char( char* buff, + unsigned buffLen, + const char* src, + char fromChar, + char toChar ); + +extern void string_translate_char( char* str, char from, char to ); + +/** TEMP CHAR STRINGS + ** + ** implement a circular ring of temporary string buffers + **/ + +extern char* tempstr_get( int size ); +extern char* tempstr_format( const char* fmt, ... ); + +/** 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 ); +extern const char* quote_str( const char* str ); + +/** DECIMAL AND HEXADECIMAL CHARACTER SEQUENCES + **/ + +/* decodes a sequence of 'len' hexadecimal chars from 'hex' into + * an integer. returns -1 in case of error (i.e. badly formed chars) + */ +extern int hex2int( const uint8_t* hex, int len ); + +/* encodes an integer 'val' into 'len' hexadecimal charaters into 'hex' */ +extern void int2hex( uint8_t* hex, int len, int val ); + +#endif /* _ANDROID_UTILS_MISC_H */ diff --git a/android/utils/path.c b/android/utils/path.c new file mode 100644 index 0000000..b15b6de --- /dev/null +++ b/android/utils/path.c @@ -0,0 +1,559 @@ +/* Copyright (C) 2007-2009 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/path.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 + +#define D(...) ((void)0) + +#ifndef CHECKED +# ifdef _WIN32 +# define CHECKED(ret, call) (ret) = (call) +# else +# define CHECKED(ret, call) do { (ret) = (call); } while ((ret) < 0 && errno == EINTR) +# endif +#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; +} + +static char* +substring_dup( const char* start, const char* end ) +{ + int len = end - start; + char* result = android_alloc(len+1); + memcpy(result, start, len); + result[len] = 0; + return result; +} + +int +path_split( const char* path, char* *pdirname, char* *pbasename ) +{ + const char* end = path + strlen(path); + const char* last; + char* basename; + + /* prepare for errors */ + if (pdirname) + *pdirname = NULL; + if (pbasename) + *pbasename = NULL; + + /* handle empty path case */ + if (end == path) { + return -1; + } + + /* strip trailing path separators */ + while (end > path && ispathsep(end[-1])) + end -= 1; + + /* handle "/" and degenerate cases like "////" */ + if (end == path) { + return -1; + } + + /* find last separator */ + last = end; + while (last > path && !ispathsep(last[-1])) + last -= 1; + + /* handle cases where there is no path separator */ + if (last == path) { + if (pdirname) + *pdirname = ASTRDUP("."); + if (pbasename) + *pbasename = substring_dup(path,end); + return 0; + } + + /* handle "/foo" */ + if (last == path+1) { + if (pdirname) + *pdirname = ASTRDUP("/"); + if (pbasename) + *pbasename = substring_dup(path+1,end); + return 0; + } + + /* compute basename */ + basename = substring_dup(last,end); + if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) { + AFREE(basename); + return -1; + } + + if (pbasename) + *pbasename = basename; + else { + AFREE(basename); + } + + /* compute dirname */ + if (pdirname != NULL) + *pdirname = substring_dup(path,last-1); + + return 0; +} + +char* +path_basename( const char* path ) +{ + char* basename; + + if (path_split(path, NULL, &basename) < 0) + return NULL; + + return basename; +} + +char* +path_dirname( const char* path ) +{ + char* dirname; + + if (path_split(path, &dirname, NULL) < 0) + return NULL; + + return dirname; +} + + + + + +/** MISC FILE AND DIRECTORY HANDLING + **/ + +ABool +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 */ +ABool +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 */ +ABool +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 */ +ABool +path_can_read( const char* path ) +{ + int ret; + CHECKED(ret, access(path, R_OK)); + return (ret == 0); +} + +ABool +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) */ +APosixStatus +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 APosixStatus +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) */ +APosixStatus +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) */ +APosixStatus +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 +} + + +ABool +path_is_absolute( const char* path ) +{ +#ifdef _WIN32 + if (path == NULL) + return 0; + + if (path[0] == '/' || path[0] == '\\') + return 1; + + /* 'C:' is always considered to be absolute + * even if used with a relative path like C:foo which + * is different from C:\foo + */ + if (path[0] != 0 && path[1] == ':') + return 1; + + return 0; +#else + return (path != NULL && path[0] == '/'); +#endif +} + + +/** OTHER FILE UTILITIES + ** + ** path_empty_file() creates an empty file at a given path location. + ** if the file already exists, it is truncated without warning + ** + ** path_copy_file() copies one file into another. + ** + ** both functions return 0 on success, and -1 on error + **/ + +APosixStatus +path_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; +} + +APosixStatus +path_copy_file( const char* dest, const char* source ) +{ + int fd, fs, result = -1; + + /* if the destination doesn't exist, create it */ + if ( access(source, F_OK) < 0 || + path_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; +} + + +APosixStatus +path_delete_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* +path_load_file(const char *fn, size_t *pSize) +{ + char* data; + int sz; + int fd; + + if (pSize) + *pSize = 0; + + data = NULL; + + fd = open(fn, O_BINARY | O_RDONLY); + if(fd < 0) return NULL; + + do { + sz = lseek(fd, 0, SEEK_END); + if(sz < 0) break; + + if (pSize) + *pSize = (size_t) sz; + + if (lseek(fd, 0, SEEK_SET) != 0) + break; + + data = (char*) malloc(sz + 1); + if(data == NULL) break; + + if (read(fd, data, sz) != sz) + break; + + close(fd); + data[sz] = 0; + + return data; + } while (0); + + close(fd); + + if(data != NULL) + free(data); + + return NULL; +} + diff --git a/android/utils/path.h b/android/utils/path.h new file mode 100644 index 0000000..e822834 --- /dev/null +++ b/android/utils/path.h @@ -0,0 +1,152 @@ +/* Copyright (C) 2007-2009 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. +*/ +#ifndef _ANDROID_UTILS_PATH_H +#define _ANDROID_UTILS_PATH_H + +#include <android/utils/system.h> +#include <stdint.h> /* for uint64_t */ + +/** MISC FILE AND DIRECTORY HANDLING + **/ + +/* O_BINARY is required in the MS C library to avoid opening file + * in text mode (the default, ahhhhh) + */ +#if !defined(_WIN32) && !defined(O_BINARY) +# define O_BINARY 0 +#endif + +/* define PATH_SEP as a string containing the directory separateor */ +#ifdef _WIN32 +# define PATH_SEP "\\" +#else +# define PATH_SEP "/" +#endif + +/* get MAX_PATH, note that PATH_MAX is set to 260 on Windows for + * stupid backwards-compatibility reason, though any 32-bit version + * of the OS handles much much longer paths + */ +#ifdef _WIN32 +# undef MAX_PATH +# define MAX_PATH 1024 +# undef PATH_MAX +# define PATH_MAX MAX_PATH +#else +# include <limits.h> +# define MAX_PATH PATH_MAX +#endif + +/* checks that a given file exists */ +extern ABool path_exists( const char* path ); + +/* checks that a path points to a regular file */ +extern ABool path_is_regular( const char* path ); + +/* checks that a path points to a directory */ +extern ABool path_is_dir( const char* path ); + +/* checks that a path is absolute or not */ +extern ABool path_is_absolute( const char* path ); + +/* checks that one can read/write a given (regular) file */ +extern ABool path_can_read( const char* path ); +extern ABool path_can_write( const char* path ); + +/* try to make a directory */ +extern APosixStatus path_mkdir( const char* path, int mode ); + +/* ensure that a given directory exists, create it if not, + 0 on success, -1 on error */ +extern APosixStatus path_mkdir_if_needed( const char* path, int mode ); + +/* return the size of a given file in '*psize'. returns 0 on + * success, -1 on failure (error code in errno) */ +extern APosixStatus path_get_size( const char* path, uint64_t *psize ); + +/* path_parent() can be used to return the n-level parent of a given directory + * this understands . and .. when encountered in the input path. + * + * the returned string must be freed by the caller. + */ +extern char* path_parent( const char* path, int levels ); + +/* split a path into a (dirname,basename) pair. the result strings must be freed + * by the caller. Return 0 on success, or -1 on error. Error conditions include + * the following: + * - 'path' is empty + * - 'path' is "/" or degenerate cases like "////" + * - basename is "." or ".." + * + * if there is no directory separator in path, *dirname will be set to "." + * if the path is of type "/foo", then *dirname will be set to "/" + * + * pdirname can be NULL if you don't want the directory name + * pbasename can be NULL if you don't want the base name + */ +extern int path_split( const char* path, char* *pdirname, char* *pbasename ); + +/* a convenience function to retrieve the directory name as returned by + * path_split(). Returns NULL if path_split() returns an error. + * the result string must be freed by the caller + */ +extern char* path_dirname( const char* path ); + +/* a convenience function to retrieve the base name as returned by + * path_split(). Returns NULL if path_split() returns an error. + * the result must be freed by the caller. + */ +extern char* path_basename( const char* path ); + +/** OTHER FILE UTILITIES + ** + ** path_empty_file() creates an empty file at a given path location. + ** if the file already exists, it is truncated without warning + ** + ** path_copy_file() copies one file into another. + ** + ** unlink_file() is equivalent to unlink() on Unix, on Windows, + ** it will handle the case where _unlink() fails because the file is + ** read-only by trying to change its access rights then calling _unlink() + ** again. + ** + ** these functions return 0 on success, and -1 on error + ** + ** load_text_file() reads a file into a heap-allocated memory block, + ** and appends a 0 to it. the caller must free it + **/ + +/* creates an empty file at a given location. If the file already + * exists, it is truncated without warning. returns 0 on success, + * or -1 on failure. + */ +extern APosixStatus path_empty_file( const char* path ); + +/* copies on file into another one. 0 on success, -1 on failure + * (error code in errno). Does not work on directories */ +extern APosixStatus path_copy_file( const char* dest, const char* source ); + +/* unlink/delete a given file. Note that on Win32, this will + * fail if the program has an opened handle to the file + */ +extern APosixStatus path_delete_file( const char* path ); + +/* try to load a given file into a heap-allocated block. + * if 'pSize' is not NULL, this will set the file's size in '*pSize' + * note that this actually zero-terminates the file for convenience. + * In case of failure, NULL is returned and the error code is in errno + */ +extern void* path_load_file( const char* path, size_t *pSize ); + +/* */ + +#endif /* _ANDROID_UTILS_PATH_H */ diff --git a/android/utils/reflist.c b/android/utils/reflist.c new file mode 100644 index 0000000..bc604cd --- /dev/null +++ b/android/utils/reflist.c @@ -0,0 +1,203 @@ +/* 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/reflist.h" +#include <stdlib.h> +#include <string.h> + +void +areflist_setEmpty(ARefList* l) +{ + if (l->iteration > 0) { + /* deferred empty, set all items to NULL + * to stop iterations */ + void** items = areflist_items(l); + memset(items, 0, l->count*sizeof(items[0])); + l->iteration |= 1; + } else { + /* direct empty */ + if (l->max > 1) { + free(l->u.items); + l->max = 1; + } + } + l->count = 0; +} + +int +areflist_indexOf(ARefList* l, void* item) +{ + if (item) { + void** items = (l->max == 1) ? &l->u.item0 : l->u.items; + void** end = items + l->count; + void** ii = items; + + for ( ; ii < end; ii += 1 ) + if (*ii == item) + return (ii - items); + } + return -1; +} + +static void +areflist_grow(ARefList* l, int count) +{ + int newcount = l->count + count; + if (newcount > l->max) { + int oldmax = l->max == 1 ? 0 : l->max; + int newmax = oldmax; + void** olditems = l->max == 1 ? NULL : l->u.items; + void** newitems; + + while (oldmax < newcount) + oldmax += (oldmax >> 1) + 4; + + newitems = realloc(olditems, newmax*sizeof(newitems[0])); + + l->u.items = newitems; + l->max = (uint16_t) newmax; + } +} + + +void +areflist_add(ARefList* l, void* item) +{ + if (item) { + void** items; + + if (l->count >= l->max) { + areflist_grow(l, 1); + } + items = areflist_items(l); + items[l->count] = item; + l->count += 1; + } +} + +void* +areflist_pop(ARefList* l) +{ + void* item = NULL; + void** items = areflist_items(l); + + if (l->count > 0) { + if (l->iteration > 0) { + /* deferred deletion */ + int nn; + for (nn = l->count-1; nn > 0; nn--) { + item = items[nn]; + if (item != NULL) { + l->count -= 1; + return item; + } + } + } else { + /* normal pop */ + item = items[--l->count]; + if (l->count <= 0 && l->max > 1) { + free(l->u.items); + l->max = 1; + } + } + } + return item; +} + +ABool +areflist_del(ARefList* l, void* item) +{ + if (item) { + int index = areflist_indexOf(l, item); + if (index >= 0) { + void** items = areflist_items(l); + + if (l->iteration > 0) { + /* deferred deletion */ + items[index] = NULL; + l->iteration |= 1; + } else { + /* direct deletion */ + if (l->max > 1) { + memmove(items + index, items + index + 1, l->count - index - 1); + if (--l->count == 0) { + free(l->u.items); + l->max = 1; + } + } else { + l->u.item0 = NULL; + l->count = 0; + } + } + return 1; + } + } + return 0; +} + +void +_areflist_remove_deferred(ARefList* l) +{ + if (l->iteration & 1) { + /* remove all NULL elements from the array */ + void** items = areflist_items(l); + int rr = 0; + int ww = 0; + for ( ; rr < l->count; rr++ ) { + if (items[rr] != NULL) + items[ww++] = items[rr]; + } + l->count = (int16_t) ww; + + /* if the list is empty, release its array */ + if (l->count == 0 && l->max > 1) { + free(l->u.items); + l->max = 1; + } + } + l->iteration = 0; +} + +void +areflist_copy(ARefList* dst, ARefList* src) +{ + dst[0] = src[0]; + + if (src->max > 1) { + dst->u.items = malloc( dst->count*sizeof(void*) ); + dst->max = dst->count; + } +} + +void* +areflist_get(ARefList* l, int n) +{ + if ((unsigned)n >= (unsigned)l->count) + return NULL; + + if (l->max == 1) + return l->u.item0; + + return l->u.items[n]; +} + +void** +areflist_at(ARefList* l, int n) +{ + void** items; + + if ((unsigned)n >= (unsigned)l->count) + return NULL; + + items = (l->max == 1) ? &l->u.item0 : l->u.items; + + return items + n; +} diff --git a/android/utils/reflist.h b/android/utils/reflist.h new file mode 100644 index 0000000..dffaef8 --- /dev/null +++ b/android/utils/reflist.h @@ -0,0 +1,129 @@ +/* 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. +*/ +#ifndef _ANDROID_UTILS_REFLIST_H +#define _ANDROID_UTILS_REFLIST_H + +#include "android/utils/system.h" + +/* definitions for a smart list of references to generic objects. + * supports safe deletion and addition while they are being iterated + * with AREFLIST_FOREACH() macro + */ + +typedef struct ARefList { + uint16_t count, max; + uint16_t iteration; + union { + struct ARefList* next; + void* item0; + void** items; + } u; +} ARefList; + +AINLINED void +areflist_init(ARefList* l) +{ + l->count = 0; + l->max = 1; + l->iteration = 0; +} + +void areflist_setEmpty(ARefList* l); + +AINLINED void +areflist_done(ARefList* l) +{ + areflist_setEmpty(l); +} + +AINLINED ABool +areflist_isEmpty(ARefList* l) +{ + return (l->count == 0); +} + +int areflist_indexOf(ARefList* l, void* item); + +AINLINED ABool +areflist_has(ARefList* l, void* item) +{ + return areflist_indexOf(l, item) >= 0; +} + +/* if 'item' is not NULL, append it to the list. An item + * can be added several times to a list */ +void areflist_add(ARefList* l, void* item); + +/* if 'item' is not NULL, try to remove it from the list */ +/* returns TRUE iff the item was found in the list */ +ABool areflist_del(ARefList* l, void* item); + +AINLINED void +areflist_push(ARefList* l, void* item) +{ + areflist_add(l, item); +} + +void* areflist_pop(ARefList* l); + +AINLINED void** +areflist_items(ARefList* l) +{ + return (l->max == 1) ? &l->u.item0 : l->u.items; +} + +AINLINED int +areflist_count(ARefList* l) +{ + return l->count; +} + +/* return a pointer to the n-th list array entry, + or NULL in case of invalid index */ +void** areflist_at(ARefList* l, int n); + +/* return the n-th array entry, or NULL in case of invalid index */ +void* areflist_get(ARefList* l, int n); + +/* used internally */ +void _areflist_remove_deferred(ARefList* l); + +#define AREFLIST_FOREACH(list_,item_,statement_) \ + ({ ARefList* _reflist = (list_); \ + int _reflist_i = 0; \ + int _reflist_n = _reflist->count; \ + _reflist->iteration += 2; \ + for ( ; _reflist_i < _reflist_n; _reflist_i++ ) { \ + void** __reflist_at = areflist_at(_reflist, _reflist_i); \ + void* item_ = *__reflist_at; \ + if (item_ != NULL) { \ + statement_; \ + } \ + } \ + _reflist->iteration -= 2; \ + if (_reflist->iteration == 1) \ + _areflist_remove_deferred(_reflist); \ + }) + +/* use this to delete the currently iterated element */ +#define AREFLIST_DEL_ITERATED() \ + ({ *_reflist_at = NULL; \ + _reflist->iteration |= 1; }) + +/* use this to replace the currently iterated element */ +#define AREFLIST_SET_ITERATED(item) \ + ({ *_reflist_at = (item); \ + if (item == NULL) _reflist->iteration |= 1; }) + +void areflist_copy(ARefList* dst, ARefList* src); + +#endif /* _ANDROID_UTILS_REFLIST_H */ diff --git a/android/utils/stralloc.c b/android/utils/stralloc.c new file mode 100644 index 0000000..2a924e4 --- /dev/null +++ b/android/utils/stralloc.c @@ -0,0 +1,300 @@ +/* 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/stralloc.h" +#include "android/utils/debug.h" +#include "android/utils/misc.h" +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> + +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" ); + } +} + +/** 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 char* +stralloc_to_tempstr( stralloc_t* s ) +{ + char* q = tempstr_get( s->n ); + + memcpy( q, s->s, s->n ); + q[s->n] = 0; + return q; +} + +extern void +stralloc_formatv( stralloc_t* s, const char* fmt, va_list args ) +{ + stralloc_reset(s); + stralloc_ready(s,10); + + while (1) { + int n; + va_list args2; + + va_copy(args2, args); + n = vsnprintf( s->s, s->a, fmt, args2 ); + va_end(args2); + + /* 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_format( stralloc_t* s, const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + stralloc_formatv(s, fmt, args); + va_end(args); +} + +extern void +stralloc_add_formatv( stralloc_t* s, const char* fmt, va_list args ) +{ + STRALLOC_DEFINE(s2); + stralloc_formatv(s2, fmt, args); + stralloc_append( s, s2 ); + stralloc_reset( s2 ); +} + +extern void +stralloc_add_format( stralloc_t* s, const char* fmt, ... ) +{ + va_list args; + va_start(args, fmt); + stralloc_add_formatv( s, fmt, args ); + va_end(args); +} + +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; + } +} + diff --git a/android/utils/stralloc.h b/android/utils/stralloc.h new file mode 100644 index 0000000..4d17060 --- /dev/null +++ b/android/utils/stralloc.h @@ -0,0 +1,60 @@ +/* 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. +*/ + +#ifndef _ANDROID_UTILS_STRALLOC_H +#define _ANDROID_UTILS_STRALLOC_H + +#include <stddef.h> +#include <stdarg.h> + +/** DYNAMIC STRINGS + **/ + +typedef struct { + char* s; + unsigned n; + unsigned a; +} stralloc_t; + +#define STRALLOC_INIT { NULL, 0, 0 } +#define STRALLOC_DEFINE(s) stralloc_t s[1] = { STRALLOC_INIT } + +extern void stralloc_reset( stralloc_t* s ); +extern void stralloc_ready( stralloc_t* s, unsigned len ); +extern void stralloc_readyplus( stralloc_t* s, unsigned len ); + +extern void stralloc_copy( stralloc_t* s, stralloc_t* from ); +extern void stralloc_append( stralloc_t* s, stralloc_t* from ); + +extern void stralloc_add_c( stralloc_t* s, int c ); +extern void stralloc_add_str( stralloc_t* s, const char* str ); +extern void stralloc_add_bytes( stralloc_t* s, const void* from, unsigned len ); + +extern char* stralloc_cstr( stralloc_t* s ); + +extern void stralloc_format( stralloc_t* s, const char* fmt, ... ); +extern void stralloc_formatv( stralloc_t* s, const char* fmt, va_list args ); +extern void stralloc_add_format( stralloc_t* s, const char* fmt, ... ); + +extern void stralloc_add_quote_c( stralloc_t* s, int c ); +extern void stralloc_add_quote_str( stralloc_t* s, const char* str ); +extern void stralloc_add_quote_bytes( stralloc_t* s, const void* from, unsigned len ); + +extern void stralloc_add_hex( stralloc_t* s, unsigned value, int num_digits ); +extern void stralloc_add_hexdump( stralloc_t* s, void* base, int size, const char* prefix ); + +extern void stralloc_tabular( stralloc_t* s, const char** strings, int count, + const char* prefix, int width ); + +extern char* stralloc_to_tempstr( stralloc_t* s ); + +#endif /* ANDROID_UTILS_STRALLOC_H */ diff --git a/android/utils/system.c b/android/utils/system.c new file mode 100644 index 0000000..e09fd6b --- /dev/null +++ b/android/utils/system.c @@ -0,0 +1,172 @@ +/* Copyright (C) 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/system.h" +#include <stdlib.h> +#include <stdio.h> +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> /* for Sleep */ +#else +# include <unistd.h> /* for usleep */ +#endif + +void* +android_alloc( size_t size ) +{ + void* block; + + if (size == 0) + return NULL; + + block = malloc(size); + if (block != NULL) + return block; + + fprintf(stderr, "PANIC: not enough memory\n"); + exit(1); + return NULL; +} + +void* +android_alloc0( size_t size ) +{ + void* block; + + if (size == 0) + return NULL; + + block = calloc(1, size); + if (block != NULL) + return block; + + fprintf(stderr, "PANIC: not enough memory\n"); + exit(1); + return NULL; +} + +void* +android_realloc( void* block, size_t size ) +{ + void* block2; + + if (size == 0) { + free(block); + return NULL; + } + block2 = realloc(block, size); + if (block2 != NULL) + return block2; + + fprintf(stderr, "PANIC: not enough memory to reallocate %d bytes\n", size); + exit(1); + return NULL; +} + +void +android_free( void* block ) +{ + if (block) + free(block); +} + +char* +android_strdup( const char* str ) +{ + int len; + char* copy; + + if (str == NULL) + return NULL; + + len = strlen(str); + copy = malloc(len+1); + memcpy(copy, str, len); + copy[len] = 0; + + return copy; +} + +#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 + + +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 +} diff --git a/android/utils/system.h b/android/utils/system.h new file mode 100644 index 0000000..804aa7d --- /dev/null +++ b/android/utils/system.h @@ -0,0 +1,161 @@ +/* Copyright (C) 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. +*/ +#ifndef _ANDROID_UTILS_SYSTEM_H +#define _ANDROID_UTILS_SYSTEM_H + +#include <string.h> +#include <stdint.h> + +/* the following functions perform 'checked allocations', i.e. + * they abort if there is not enough memory. + */ + +/* checked malloc, only returns NULL if size is 0 */ +void* android_alloc( size_t size ); + +/* checked calloc, only returns NULL if size is 0 */ +void* android_alloc0( size_t size ); + +/* checked realloc, only returns NULL if size if 0 */ +void* android_realloc( void* block, size_t size ); + +/* free memory block */ +void android_free( void* block ); + +/* convenience macros */ + +#define AZERO(p) memset((char*)(p),0,sizeof(*(p))) +#define ANEW(p) (p = android_alloc(sizeof(*p))) +#define ANEW0(p) (p = android_alloc0(sizeof(*p))) +#define AFREE(p) android_free(p) + +#define AMEM_ZERO(dst,size) memset((char*)(dst), 0, (size_t)(size)) +#define AMEM_COPY(dst,src,size) memcpy((char*)(dst),(const char*)(src),(size_t)(size)) +#define AMEM_MOVE(dst,src,size) memmove((char*)(dst),(const char*)(src),(size_t)(size)) + +#define AARRAY_NEW(p,count) ((p) = android_alloc(sizeof(*p)*(count))) +#define AARRAY_NEW0(p,count) ((p) = android_alloc0(sizeof(*p)*(count))) + +#define AARRAY_RENEW(p,count) ((p) = android_realloc((p),sizeof(*(p))*(count))) + +#define AARRAY_COPY(dst,src,count) AMEM_COPY(dst,src,(count)*sizeof((dst)[0])) +#define AARRAY_MOVE(dst,src,count) AMEM_MOVE(dst,src,(count)*sizeof((dst)[0])) +#define AARRAY_ZERO(dst,count) AMEM_ZERO(dst,(count)*sizeof((dst)[0])) + +#define AARRAY_STATIC_LEN(a) (sizeof((a))/sizeof((a)[0])) + +#define AINLINED static __inline__ + +/* unlike strdup(), this accepts NULL as valid input (and will return NULL then) */ +char* android_strdup(const char* src); + +#define ASTRDUP(str) android_strdup(str) + +/* used for functions that return a Posix-style status code, i.e. + * 0 means success, -1 means failure with the error code in 'errno' + */ +typedef int APosixStatus; + +/* used for functions that return or accept a boolean type */ +typedef int ABool; + +/** Stringification macro + **/ +#ifndef STRINGIFY +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) +#endif + +/** Concatenation macros + **/ +#ifndef GLUE +#define _GLUE(x,y) x##y +#define GLUE(x,y) _GLUE(x,y) + +#define _GLUE3(x,y,z) x##y##z +#define GLUE3(x,y,z) _GLUE3(x,y,z) +#endif + +/** Handle strsep() on Win32 + **/ +#ifdef _WIN32 +# undef strsep +# define strsep win32_strsep +extern char* win32_strsep(char** pline, const char* delim); +#endif + +/** Handle strcasecmp on Windows + **/ +#ifdef _WIN32 +# define strcasecmp stricmp +#endif + +/** EINTR HANDLING + ** + ** since QEMU uses SIGALRM pretty extensively, having a system call returning + ** EINTR on Unix happens very frequently. provide a simple macro to guard against + ** this. + **/ + +#ifdef _WIN32 +# define CHECKED(ret, call) (ret) = (call) +#else +# define CHECKED(ret, call) do { (ret) = (call); } while ((ret) < 0 && errno == EINTR) +#endif + +/** SIGNAL HANDLING + ** + ** the following can be used to block SIGALRM for a given period of time. + ** use with caution, the QEMU execution loop uses SIGALRM extensively + ** + **/ +#ifdef _WIN32 +typedef struct { int dumy; } signal_state_t; +#else +#include <signal.h> +typedef struct { sigset_t old; } signal_state_t; +#endif + +extern void disable_sigalrm( signal_state_t *state ); +extern void restore_sigalrm( signal_state_t *state ); + +#ifdef _WIN32 + +#define BEGIN_NOSIGALRM \ + { + +#define END_NOSIGALRM \ + } + +#else /* !WIN32 */ + +#define BEGIN_NOSIGALRM \ + { signal_state_t __sigalrm_state; \ + disable_sigalrm( &__sigalrm_state ); + +#define END_NOSIGALRM \ + restore_sigalrm( &__sigalrm_state ); \ + } + +#endif /* !WIN32 */ + +/** TIME HANDLING + ** + ** sleep for a given time in milliseconds. note: this uses + ** disable_sigalrm()/restore_sigalrm() + **/ + +extern void sleep_ms( int timeout ); + +/* */ + +#endif /* _ANDROID_UTILS_SYSTEM_H */ diff --git a/android/utils/tempfile.c b/android/utils/tempfile.c new file mode 100644 index 0000000..6374c12 --- /dev/null +++ b/android/utils/tempfile.c @@ -0,0 +1,198 @@ +/* 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/tempfile.h" +#include "android/utils/bufprint.h" +#include "android/utils/debug.h" + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else +# include <unistd.h> +#endif + +#define D(...) ((void)0) + +/** 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); +} diff --git a/android/utils/tempfile.h b/android/utils/tempfile.h new file mode 100644 index 0000000..4264a84 --- /dev/null +++ b/android/utils/tempfile.h @@ -0,0 +1,52 @@ +/* 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. +*/ + +#ifndef _ANDROID_UTILS_TEMPFILE_H +#define _ANDROID_UTILS_TEMPFILE_H + +/** 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() + **/ + +typedef struct TempFile TempFile; + +extern TempFile* tempfile_create( void ); +extern const char* tempfile_path( TempFile* temp ); +extern void tempfile_close( TempFile* temp ); + +/** TEMP FILE CLEANUP + ** + ** We delete all temporary files in atexit()-registered callbacks. + ** however, the Win32 DeleteFile is unable to remove a file unless + ** all HANDLEs to it are closed in the terminating process. + ** + ** Call 'atexit_close_fd' on a newly open-ed file descriptor to indicate + ** that you want it closed in atexit() time. You should always call + ** this function unless you're certain that the corresponding file + ** cannot be temporary. + ** + ** Call 'atexit_close_fd_remove' before explicitely closing a 'fd' + **/ +extern void atexit_close_fd(int fd); +extern void atexit_close_fd_remove(int fd); + +#endif /* _ANDROID_UTILS_TEMPFILE_H */ diff --git a/android/utils/timezone.c b/android/utils/timezone.c new file mode 100644 index 0000000..b5588a3 --- /dev/null +++ b/android/utils/timezone.c @@ -0,0 +1,721 @@ +/* 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/debug.h" +#include "android/utils/timezone.h" +#include "android/utils/bufprint.h" +#include "android/android.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "qemu-common.h" + +#define DEBUG 1 + +#if 1 +# define D_ACTIVE VERBOSE_CHECK(timezone) +#else +# define D_ACTIVE DEBUG +#endif + +#if DEBUG +# define D(...) do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0) +#else +# define D(...) ((void)0) +#endif + + + +static const char* get_zoneinfo_timezone( void ); /* forward */ + +static char android_timezone0[256]; +static const char* android_timezone; +static int android_timezone_init; + +static int +check_timezone_is_zoneinfo(const char* tz) +{ + const char* slash1 = NULL, *slash2 = NULL; + + if (tz == NULL) + return 0; + + /* the name must be of the form Area/Location or Area/Location/SubLocation */ + slash1 = strchr( tz, '/' ); + if (slash1 == NULL || slash1[1] == 0) + return 0; + + slash2 = strchr( slash1+1, '/'); + if (slash2 != NULL) { + if (slash2[1] == 0 || strchr(slash2+1, '/') != NULL) + return 0; + } + + return 1; +} + +int +timezone_set( const char* tzname ) +{ + int len; + + if ( !check_timezone_is_zoneinfo(tzname) ) + return -1; + + len = strlen(tzname); + if (len > sizeof(android_timezone0)-1) + return -1; + + strcpy( android_timezone0, tzname ); + android_timezone = android_timezone0; + android_timezone_init = 1; + + return 0; +} + + +char* +bufprint_zoneinfo_timezone( char* p, char* end ) +{ + const char* tz = get_zoneinfo_timezone(); + + if (tz == NULL || !check_timezone_is_zoneinfo(tz)) + return bufprint(p, end, "Unknown/Unknown"); + else + return bufprint(p, end, "%s", tz); +} + +/* on OS X, the timezone directory is always /usr/share/zoneinfo + * this makes things easy. + */ +#ifdef __APPLE__ + +#include <unistd.h> +#include <limits.h> +#define LOCALTIME_FILE "/etc/localtime" +#define ZONEINFO_DIR "/usr/share/zoneinfo/" +static const char* +get_zoneinfo_timezone( void ) +{ + if (!android_timezone_init) { + const char* tz = getenv("TZ"); + char buff[PATH_MAX+1]; + + android_timezone_init = 1; + if (tz == NULL) { + int len = readlink(LOCALTIME_FILE, buff, sizeof(buff)); + if (len < 0) { + dprint( "### WARNING: Could not read %s, something is very wrong on your system", + LOCALTIME_FILE); + return NULL; + } + + buff[len] = 0; + D("%s: %s points to %s\n", __FUNCTION__, LOCALTIME_FILE, buff); + if ( memcmp(buff, ZONEINFO_DIR, sizeof(ZONEINFO_DIR)-1) ) { + dprint( "### WARNING: %s does not point to %s, can't determine zoneinfo timezone name", + LOCALTIME_FILE, ZONEINFO_DIR ); + return NULL; + } + tz = buff + sizeof(ZONEINFO_DIR)-1; + if ( !check_timezone_is_zoneinfo(tz) ) { + dprint( "### WARNING: %s does not point to zoneinfo-compatible timezone name\n", LOCALTIME_FILE ); + return NULL; + } + } + pstrcpy( android_timezone0, sizeof(android_timezone0), tz ); + android_timezone = android_timezone0; + } + D( "found timezone %s", android_timezone ); + return android_timezone; +} + +#endif /* __APPLE__ */ + +/* on Linux, with glibc2, the zoneinfo directory can be changed with TZDIR environment variable + * but should be /usr/share/zoneinfo by default. /etc/localtime is not guaranteed to exist on + * all platforms, so if it doesn't, try $TZDIR/localtime, then /usr/share/zoneinfo/locatime + * ugly, isn't it ? + * + * besides, modern Linux distribution don't make /etc/localtime a symlink but a straight copy of + * the original timezone file. the only way to know which zoneinfo name to retrieve is to compare + * it with all files in $TZDIR (at least those that match Area/Location or Area/Location/SubLocation + */ +#ifdef __linux__ + +#include <unistd.h> +#include <limits.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#define ZONEINFO_DIR "/usr/share/zoneinfo/" +#define LOCALTIME_FILE1 "/etc/localtime" + +typedef struct { + const char* localtime; + struct stat localtime_st; + char* path_end; + char* path_root; + char path[ PATH_MAX ]; +} ScanDataRec; + +static int +compare_timezone_to_localtime( ScanDataRec* scan, + const char* path ) +{ + struct stat st; + int fd1, fd2, result = 0; + + D( "%s: comparing %s:", __FUNCTION__, path ); + + if ( stat( path, &st ) < 0 ) { + D( " can't stat: %s\n", strerror(errno) ); + return 0; + } + + if ( st.st_size != scan->localtime_st.st_size ) { + D( " size mistmatch (%lld != %lld)\n", st.st_size, scan->localtime_st.st_size ); + return 0; + } + + fd1 = open( scan->localtime, O_RDONLY ); + if (fd1 < 0) { + D(" can't open %s: %s\n", scan->localtime, strerror(errno) ); + return 0; + } + fd2 = open( path, O_RDONLY ); + if (fd2 < 0) { + D(" can't open %s: %s\n", path, strerror(errno) ); + close(fd1); + return 0; + } + do { + off_t nn; + + for (nn = 0; nn < st.st_size; nn++) { + char temp[2]; + int ret; + + do { ret = read(fd1, &temp[0], 1); } while (ret < 0 && errno == EINTR); + if (ret < 0) break; + + do { ret = read(fd2, &temp[1], 1); } while (ret < 0 && errno == EINTR); + if (ret < 0) break; + + if (temp[0] != temp[1]) + break; + } + + result = (nn == st.st_size); + + } while (0); + + D( result ? " MATCH\n" : "no match\n" ); + + close(fd2); + close(fd1); + + return result; +} + +static const char* +scan_timezone_dir( ScanDataRec* scan, + char* top, + int depth ) +{ + DIR* d = opendir( scan->path ); + const char* result = NULL; + + D( "%s: entering '%s\n", __FUNCTION__, scan->path ); + if (d != NULL) { + struct dirent* ent; + while ((ent = readdir(d)) != NULL) { + struct stat ent_st; + char* p = top; + + if (ent->d_name[0] == '.') /* avoid hidden and special files */ + continue; + + p = bufprint( p, scan->path_end, "/%s", ent->d_name ); + if (p >= scan->path_end) + continue; + + //D( "%s: scanning '%s'\n", __FUNCTION__, scan->path ); + + if ( stat( scan->path, &ent_st ) < 0 ) + continue; + + if ( S_ISDIR(ent_st.st_mode) && depth < 2 ) + { + //D( "%s: directory '%s'\n", __FUNCTION__, scan->path ); + result = scan_timezone_dir( scan, p, depth + 1 ); + if (result != NULL) + break; + } + else if ( S_ISREG(ent_st.st_mode) && (depth >= 1 && depth <= 2) ) + { + char* name = scan->path_root + 1; + + if ( check_timezone_is_zoneinfo( name ) ) + { + if (compare_timezone_to_localtime( scan, scan->path )) + { + result = strdup( name ); + D( "%s: found '%s'\n", __FUNCTION__, result ); + break; + } + } + else + { + //D( "%s: ignoring '%s'\n", __FUNCTION__, scan->path ); + } + } + } + closedir(d); + } + return result; +} + +static const char* +get_zoneinfo_timezone( void ) +{ + if (!android_timezone_init) + { + const char* tz = getenv( "TZ" ); + + android_timezone_init = 1; + + if ( tz != NULL && !check_timezone_is_zoneinfo(tz) ) { + D( "%s: ignoring non zoneinfo formatted TZ environment variable: '%s'\n", + __FUNCTION__, tz ); + tz = NULL; + } + + if (tz == NULL) { + char* tzdir = NULL; + int tzdirlen = 0; + char* localtime = NULL; + int len; + char temp[ PATH_MAX ]; + + /* determine the correct timezone directory */ + { + const char* env = getenv("TZDIR"); + const char* zoneinfo_dir = ZONEINFO_DIR; + + if (env == NULL) + env = zoneinfo_dir; + + if ( access( env, R_OK ) != 0 ) { + if ( env == zoneinfo_dir ) { + fprintf( stderr, + "### WARNING: could not find %s directory. unable to determine host timezone\n", env ); + } else { + D( "%s: TZDIR does not point to valid directory, using %s instead\n", + __FUNCTION__, zoneinfo_dir ); + env = zoneinfo_dir; + } + return NULL; + } + tzdir = strdup(env); + } + + /* remove trailing slash, if any */ + len = strlen(tzdir); + if (len > 0 && tzdir[len-1] == '/') { + tzdir[len-1] = 0; + len -= 1; + } + tzdirlen = len; + D( "%s: found timezone dir as %s\n", __FUNCTION__, tzdir ); + + /* try to find the localtime file */ + localtime = LOCALTIME_FILE1; + if ( access( localtime, R_OK ) != 0 ) { + char *p = temp, *end = p + sizeof(temp); + + p = bufprint( p, end, "%s/%s", tzdir, "localtime" ); + if (p >= end || access( temp, R_OK ) != 0 ) { + fprintf( stderr, "### WARNING: could not find %s or %s. unable to determine host timezone\n", + LOCALTIME_FILE1, temp ); + goto Exit; + } + localtime = temp; + } + localtime = strdup(localtime); + D( "%s: found localtime file as %s\n", __FUNCTION__, localtime ); + +#if 1 + /* if the localtime file is a link, make a quick check */ + len = readlink( localtime, temp, sizeof(temp)-1 ); + if (len >= 0 && len > tzdirlen + 2) { + temp[len] = 0; + + /* verify that the link points to tzdir/<something> where <something> is a valid zoneinfo name */ + if ( !memcmp( temp, tzdir, tzdirlen ) && temp[tzdirlen] == '/' ) { + if ( check_timezone_is_zoneinfo( temp + tzdirlen + 1 ) ) { + /* we have it ! */ + tz = temp + tzdirlen + 1; + D( "%s: found zoneinfo timezone %s from %s symlink\n", __FUNCTION__, tz, localtime ); + goto Exit; + } + D( "%s: %s link points to non-zoneinfo filename %s, comparing contents\n", + __FUNCTION__, localtime, temp ); + } + } +#endif + + /* otherwise, parse all files under tzdir and see if we have something that looks like it */ + { + ScanDataRec scan[1]; + + if ( stat( localtime, &scan->localtime_st ) < 0 ) { + fprintf( stderr, "### WARNING: can't access '%s', unable to determine host timezone\n", + localtime ); + goto Exit; + } + + scan->localtime = localtime; + scan->path_end = scan->path + sizeof(scan->path); + scan->path_root = bufprint( scan->path, scan->path_end, "%s", tzdir ); + + tz = scan_timezone_dir( scan, scan->path_root, 0 ); + } + + Exit: + if (tzdir) + free(tzdir); + if (localtime) + free(localtime); + + if (tz == NULL) + return NULL; + + pstrcpy( android_timezone0, sizeof(android_timezone0), tz ); + android_timezone = android_timezone0; + } + D( "found timezone %s\n", android_timezone ); + } + return android_timezone; +} + +#endif /* __linux__ */ + + +/* on Windows, we need to translate the Windows timezone into a ZoneInfo one */ +#ifdef _WIN32 +#include <time.h> + +typedef struct { + const char* win_name; + const char* zoneinfo_name; +} Win32Timezone; + +/* table generated from http://unicode.org/cldr/data/diff/supplemental/windows_tzid.html */ +static const Win32Timezone _win32_timezones[] = { + { "AUS Central Standard Time" , "Australia/Darwin" }, + { "AUS Eastern Standard Time" , "Australia/Sydney" }, + { "Acre Standard Time" , "America/Rio_Branco" }, + { "Afghanistan Standard Time" , "Asia/Kabul" }, + { "Africa_Central Standard Time" , "Africa/Kigali" }, + { "Africa_Eastern Standard Time" , "Africa/Kampala" }, + { "Africa_FarWestern Standard Time" , "Africa/El_Aaiun" }, + { "Africa_Southern Standard Time" , "Africa/Johannesburg" }, + { "Africa_Western Standard Time" , "Africa/Niamey" }, + { "Aktyubinsk Standard Time" , "Asia/Aqtobe" }, + { "Alaska Standard Time" , "America/Juneau" }, + { "Alaska_Hawaii Standard Time" , "America/Anchorage" }, + { "Alaskan Standard Time" , "America/Anchorage" }, + { "Almaty Standard Time" , "Asia/Almaty" }, + { "Amazon Standard Time" , "America/Manaus" }, + { "America_Central Standard Time" , "America/Winnipeg" }, + { "America_Eastern Standard Time" , "America/Panama" }, + { "America_Mountain Standard Time" , "America/Edmonton" }, + { "America_Pacific Standard Time" , "America/Vancouver" }, + { "Anadyr Standard Time" , "Asia/Anadyr" }, + { "Aqtau Standard Time" , "Asia/Aqtau" }, + { "Aqtobe Standard Time" , "Asia/Aqtobe" }, + { "Arab Standard Time" , "Asia/Riyadh" }, + { "Arabian Standard Time" , "Asia/Bahrain" }, + { "Arabic Standard Time" , "Asia/Baghdad" }, + { "Argentina Standard Time" , "America/Buenos_Aires" }, + { "Argentina_Western Standard Time" , "America/Mendoza" }, + { "Armenia Standard Time" , "Asia/Yerevan" }, + { "Ashkhabad Standard Time" , "Asia/Ashgabat" }, + { "Atlantic Standard Time" , "America/Curacao" }, + { "Australia_Central Standard Time" , "Australia/Adelaide" }, + { "Australia_CentralWestern Standard Time", "Australia/Eucla" }, + { "Australia_Eastern Standard Time" , "Australia/Sydney" }, + { "Australia_Western Standard Time" , "Australia/Perth" }, + { "Azerbaijan Standard Time" , "Asia/Baku" }, + { "Azores Standard Time" , "Atlantic/Azores" }, + { "Baku Standard Time" , "Asia/Baku" }, + { "Bangladesh Standard Time" , "Asia/Dhaka" }, + { "Bering Standard Time" , "America/Adak" }, + { "Bhutan Standard Time" , "Asia/Thimphu" }, + { "Bolivia Standard Time" , "America/La_Paz" }, + { "Borneo Standard Time" , "Asia/Kuching" }, + { "Brasilia Standard Time" , "America/Sao_Paulo" }, + { "British Standard Time" , "Europe/London" }, + { "Brunei Standard Time" , "Asia/Brunei" }, + { "Canada Central Standard Time" , "America/Regina" }, + { "Cape Verde Standard Time" , "Atlantic/Cape_Verde" }, + { "Cape_Verde Standard Time" , "Atlantic/Cape_Verde" }, + { "Caucasus Standard Time" , "Asia/Yerevan" }, + { "Cen. Australia Standard Time" , "Australia/Adelaide" }, + { "Central Standard Time" , "America/Chicago" }, + { "Central America Standard Time" , "America/Guatemala" }, + { "Central Asia Standard Time" , "Asia/Dhaka" }, + { "Central Brazilian Standard Time" , "America/Manaus" }, + { "Central Europe Standard Time" , "Europe/Prague" }, + { "Central European Standard Time" , "Europe/Warsaw" }, + { "Central Pacific Standard Time" , "Pacific/Guadalcanal" }, + { "Central Standard Time (Mexico)" , "America/Mexico_City" }, + { "Chamorro Standard Time" , "Pacific/Guam" }, + { "Changbai Standard Time" , "Asia/Harbin" }, + { "Chatham Standard Time" , "Pacific/Chatham" }, + { "Chile Standard Time" , "America/Santiago" }, + { "China Standard Time" , "Asia/Taipei" }, + { "Choibalsan Standard Time" , "Asia/Choibalsan" }, + { "Christmas Standard Time" , "Indian/Christmas" }, + { "Cocos Standard Time" , "Indian/Cocos" }, + { "Colombia Standard Time" , "America/Bogota" }, + { "Cook Standard Time" , "Pacific/Rarotonga" }, + { "Cuba Standard Time" , "America/Havana" }, + { "Dacca Standard Time" , "Asia/Dhaka" }, + { "Dateline Standard Time" , "Pacific/Kwajalein" }, + { "Davis Standard Time" , "Antarctica/Davis" }, + { "Dominican Standard Time" , "America/Santo_Domingo" }, + { "DumontDUrville Standard Time" , "Antarctica/DumontDUrville" }, + { "Dushanbe Standard Time" , "Asia/Dushanbe" }, + { "Dutch_Guiana Standard Time" , "America/Paramaribo" }, + { "E. Africa Standard Time" , "Africa/Nairobi" }, + { "E. Australia Standard Time" , "Australia/Brisbane" }, + { "E. Europe Standard Time" , "Europe/Minsk" }, + { "E. South America Standard Time" , "America/Sao_Paulo" }, + { "East_Timor Standard Time" , "Asia/Dili" }, + { "Easter Standard Time" , "Pacific/Easter" }, + { "Eastern Standard Time" , "America/New_York" }, + { "Ecuador Standard Time" , "America/Guayaquil" }, + { "Egypt Standard Time" , "Africa/Cairo" }, + { "Ekaterinburg Standard Time" , "Asia/Yekaterinburg" }, + { "Europe_Central Standard Time" , "Europe/Oslo" }, + { "Europe_Eastern Standard Time" , "Europe/Vilnius" }, + { "Europe_Western Standard Time" , "Atlantic/Canary" }, + { "FLE Standard Time" , "Europe/Helsinki" }, + { "Falkland Standard Time" , "Atlantic/Stanley" }, + { "Fiji Standard Time" , "Pacific/Fiji" }, + { "French_Guiana Standard Time" , "America/Cayenne" }, + { "French_Southern Standard Time" , "Indian/Kerguelen" }, + { "Frunze Standard Time" , "Asia/Bishkek" }, + { "GMT Standard Time" , "Europe/Dublin" }, + { "GTB Standard Time" , "Europe/Istanbul" }, + { "Galapagos Standard Time" , "Pacific/Galapagos" }, + { "Gambier Standard Time" , "Pacific/Gambier" }, + { "Georgia Standard Time" , "Asia/Tbilisi" }, + { "Georgian Standard Time" , "Asia/Tbilisi" }, + { "Gilbert_Islands Standard Time" , "Pacific/Tarawa" }, + { "Goose_Bay Standard Time" , "America/Goose_Bay" }, + { "Greenland Standard Time" , "America/Godthab" }, + { "Greenland_Central Standard Time" , "America/Scoresbysund" }, + { "Greenland_Eastern Standard Time" , "America/Scoresbysund" }, + { "Greenland_Western Standard Time" , "America/Godthab" }, + { "Greenwich Standard Time" , "Africa/Casablanca" }, + { "Guam Standard Time" , "Pacific/Guam" }, + { "Gulf Standard Time" , "Asia/Muscat" }, + { "Guyana Standard Time" , "America/Guyana" }, + { "Hawaii_Aleutian Standard Time" , "Pacific/Honolulu" }, + { "Hawaiian Standard Time" , "Pacific/Honolulu" }, + { "Hong_Kong Standard Time" , "Asia/Hong_Kong" }, + { "Hovd Standard Time" , "Asia/Hovd" }, + { "India Standard Time" , "Asia/Calcutta" }, + { "Indian_Ocean Standard Time" , "Indian/Chagos" }, + { "Indochina Standard Time" , "Asia/Vientiane" }, + { "Indonesia_Central Standard Time" , "Asia/Makassar" }, + { "Indonesia_Eastern Standard Time" , "Asia/Jayapura" }, + { "Indonesia_Western Standard Time" , "Asia/Jakarta" }, + { "Iran Standard Time" , "Asia/Tehran" }, + { "Irish Standard Time" , "Europe/Dublin" }, + { "Irkutsk Standard Time" , "Asia/Irkutsk" }, + { "Israel Standard Time" , "Asia/Jerusalem" }, + { "Japan Standard Time" , "Asia/Tokyo" }, + { "Jordan Standard Time" , "Asia/Amman" }, + { "Kamchatka Standard Time" , "Asia/Kamchatka" }, + { "Karachi Standard Time" , "Asia/Karachi" }, + { "Kashgar Standard Time" , "Asia/Kashgar" }, + { "Kazakhstan_Eastern Standard Time" , "Asia/Almaty" }, + { "Kazakhstan_Western Standard Time" , "Asia/Aqtobe" }, + { "Kizilorda Standard Time" , "Asia/Qyzylorda" }, + { "Korea Standard Time" , "Asia/Seoul" }, + { "Kosrae Standard Time" , "Pacific/Kosrae" }, + { "Krasnoyarsk Standard Time" , "Asia/Krasnoyarsk" }, + { "Kuybyshev Standard Time" , "Europe/Samara" }, + { "Kwajalein Standard Time" , "Pacific/Kwajalein" }, + { "Kyrgystan Standard Time" , "Asia/Bishkek" }, + { "Lanka Standard Time" , "Asia/Colombo" }, + { "Liberia Standard Time" , "Africa/Monrovia" }, + { "Line_Islands Standard Time" , "Pacific/Kiritimati" }, + { "Long_Shu Standard Time" , "Asia/Chongqing" }, + { "Lord_Howe Standard Time" , "Australia/Lord_Howe" }, + { "Macau Standard Time" , "Asia/Macau" }, + { "Magadan Standard Time" , "Asia/Magadan" }, + { "Malaya Standard Time" , "Asia/Kuala_Lumpur" }, + { "Malaysia Standard Time" , "Asia/Kuching" }, + { "Maldives Standard Time" , "Indian/Maldives" }, + { "Marquesas Standard Time" , "Pacific/Marquesas" }, + { "Marshall_Islands Standard Time" , "Pacific/Majuro" }, + { "Mauritius Standard Time" , "Indian/Mauritius" }, + { "Mawson Standard Time" , "Antarctica/Mawson" }, + { "Mexico Standard Time" , "America/Mexico_City" }, + { "Mexico Standard Time 2 Standard Time" , "America/Chihuahua" }, + { "Mid-Atlantic Standard Time" , "America/Noronha" }, + { "Middle East Standard Time" , "Asia/Beirut" }, + { "Mongolia Standard Time" , "Asia/Ulaanbaatar" }, + { "Montevideo Standard Time" , "America/Montevideo" }, + { "Moscow Standard Time" , "Europe/Moscow" }, + { "Mountain Standard Time" , "America/Denver" }, + { "Mountain Standard Time (Mexico)" , "America/Chihuahua" }, + { "Myanmar Standard Time" , "Asia/Rangoon" }, + { "N. Central Asia Standard Time" , "Asia/Novosibirsk" }, + { "Namibia Standard Time" , "Africa/Windhoek" }, + { "Nauru Standard Time" , "Pacific/Nauru" }, + { "Nepal Standard Time" , "Asia/Katmandu" }, + { "New Zealand Standard Time" , "Pacific/Auckland" }, + { "New_Caledonia Standard Time" , "Pacific/Noumea" }, + { "New_Zealand Standard Time" , "Pacific/Auckland" }, + { "Newfoundland Standard Time" , "America/St_Johns" }, + { "Niue Standard Time" , "Pacific/Niue" }, + { "Norfolk Standard Time" , "Pacific/Norfolk" }, + { "Noronha Standard Time" , "America/Noronha" }, + { "North Asia Standard Time" , "Asia/Krasnoyarsk" }, + { "North Asia East Standard Time" , "Asia/Ulaanbaatar" }, + { "North_Mariana Standard Time" , "Pacific/Saipan" }, + { "Novosibirsk Standard Time" , "Asia/Novosibirsk" }, + { "Omsk Standard Time" , "Asia/Omsk" }, + { "Oral Standard Time" , "Asia/Oral" }, + { "Pacific Standard Time" , "America/Los_Angeles" }, + { "Pacific SA Standard Time" , "America/Santiago" }, + { "Pacific Standard Time (Mexico)" , "America/Tijuana" }, + { "Pakistan Standard Time" , "Asia/Karachi" }, + { "Palau Standard Time" , "Pacific/Palau" }, + { "Papua_New_Guinea Standard Time" , "Pacific/Port_Moresby" }, + { "Paraguay Standard Time" , "America/Asuncion" }, + { "Peru Standard Time" , "America/Lima" }, + { "Philippines Standard Time" , "Asia/Manila" }, + { "Phoenix_Islands Standard Time" , "Pacific/Enderbury" }, + { "Pierre_Miquelon Standard Time" , "America/Miquelon" }, + { "Pitcairn Standard Time" , "Pacific/Pitcairn" }, + { "Ponape Standard Time" , "Pacific/Ponape" }, + { "Qyzylorda Standard Time" , "Asia/Qyzylorda" }, + { "Reunion Standard Time" , "Indian/Reunion" }, + { "Romance Standard Time" , "Europe/Paris" }, + { "Rothera Standard Time" , "Antarctica/Rothera" }, + { "Russian Standard Time" , "Europe/Moscow" }, + { "SA Eastern Standard Time" , "America/Buenos_Aires" }, + { "SA Pacific Standard Time" , "America/Bogota" }, + { "SA Western Standard Time" , "America/Caracas" }, + { "SE Asia Standard Time" , "Asia/Bangkok" }, + { "Sakhalin Standard Time" , "Asia/Sakhalin" }, + { "Samara Standard Time" , "Europe/Samara" }, + { "Samarkand Standard Time" , "Asia/Samarkand" }, + { "Samoa Standard Time" , "Pacific/Apia" }, + { "Seychelles Standard Time" , "Indian/Mahe" }, + { "Shevchenko Standard Time" , "Asia/Aqtau" }, + { "Singapore Standard Time" , "Asia/Singapore" }, + { "Solomon Standard Time" , "Pacific/Guadalcanal" }, + { "South Africa Standard Time" , "Africa/Johannesburg" }, + { "South_Georgia Standard Time" , "Atlantic/South_Georgia" }, + { "Sri Lanka Standard Time" , "Asia/Colombo" }, + { "Suriname Standard Time" , "America/Paramaribo" }, + { "Sverdlovsk Standard Time" , "Asia/Yekaterinburg" }, + { "Syowa Standard Time" , "Antarctica/Syowa" }, + { "Tahiti Standard Time" , "Pacific/Tahiti" }, + { "Taipei Standard Time" , "Asia/Taipei" }, + { "Tajikistan Standard Time" , "Asia/Dushanbe" }, + { "Tashkent Standard Time" , "Asia/Tashkent" }, + { "Tasmania Standard Time" , "Australia/Hobart" }, + { "Tbilisi Standard Time" , "Asia/Tbilisi" }, + { "Tokelau Standard Time" , "Pacific/Fakaofo" }, + { "Tokyo Standard Time" , "Asia/Tokyo" }, + { "Tonga Standard Time" , "Pacific/Tongatapu" }, + { "Truk Standard Time" , "Pacific/Truk" }, + { "Turkey Standard Time" , "Europe/Istanbul" }, + { "Turkmenistan Standard Time" , "Asia/Ashgabat" }, + { "Tuvalu Standard Time" , "Pacific/Funafuti" }, + { "US Eastern Standard Time" , "America/Indianapolis" }, + { "US Mountain Standard Time" , "America/Phoenix" }, + { "Uralsk Standard Time" , "Asia/Oral" }, + { "Uruguay Standard Time" , "America/Montevideo" }, + { "Urumqi Standard Time" , "Asia/Urumqi" }, + { "Uzbekistan Standard Time" , "Asia/Tashkent" }, + { "Vanuatu Standard Time" , "Pacific/Efate" }, + { "Venezuela Standard Time" , "America/Caracas" }, + { "Vladivostok Standard Time" , "Asia/Vladivostok" }, + { "Volgograd Standard Time" , "Europe/Volgograd" }, + { "Vostok Standard Time" , "Antarctica/Vostok" }, + { "W. Australia Standard Time" , "Australia/Perth" }, + { "W. Central Africa Standard Time" , "Africa/Lagos" }, + { "W. Europe Standard Time" , "Europe/Berlin" }, + { "Wake Standard Time" , "Pacific/Wake" }, + { "Wallis Standard Time" , "Pacific/Wallis" }, + { "West Asia Standard Time" , "Asia/Karachi" }, + { "West Pacific Standard Time" , "Pacific/Guam" }, + { "Yakutsk Standard Time" , "Asia/Yakutsk" }, + { "Yekaterinburg Standard Time" , "Asia/Yekaterinburg" }, + { "Yerevan Standard Time" , "Asia/Yerevan" }, + { "Yukon Standard Time" , "America/Yakutat" }, + { NULL, NULL } +}; + +static const char* +get_zoneinfo_timezone( void ) +{ + if (!android_timezone_init) + { + char tzname[128]; + time_t t = time(NULL); + struct tm* tm = localtime(&t); + const Win32Timezone* win32tz = _win32_timezones; + + android_timezone_init = 1; + + if (!tm) { + D("%s: could not determine current date/time\n", __FUNCTION__); + return NULL; + } + + memset(tzname, 0, sizeof(tzname)); + strftime(tzname, sizeof(tzname) - 1, "%Z", tm); + + for (win32tz = _win32_timezones; win32tz->win_name != NULL; win32tz++) + if ( !strcmp(win32tz->win_name, tzname) ) { + android_timezone = win32tz->zoneinfo_name; + goto Exit; + } + +#if 0 /* TODO */ + /* we didn't find it, this may come from localized versions of Windows. we're going to explore the registry, + * as the code in Postgresql does... + */ +#endif + D( "%s: could not determine current timezone\n", __FUNCTION__ ); + return NULL; + } +Exit: + D( "emulator: found timezone %s\n", android_timezone ); + return android_timezone; +} + +#endif /* _WIN32 */ + diff --git a/android/utils/timezone.h b/android/utils/timezone.h new file mode 100644 index 0000000..bf5f731 --- /dev/null +++ b/android/utils/timezone.h @@ -0,0 +1,31 @@ +/* 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. +*/ +#ifndef _ANDROID_UTILS_TIMEZONE_H +#define _ANDROID_UTILS_TIMEZONE_H + +/* try to set the default host timezone, returns 0 on success, or -1 if + * 'tzname' is not in zoneinfo format (e.g. Area/Location) + */ +extern int timezone_set( const char* tzname ); + +/* append the current host "zoneinfo" timezone name to a given buffer. note + * that this is something like "America/Los_Angeles", and not the human-friendly "PST" + * this is required by the Android emulated system... + * + * the implementation of this function is really tricky and is OS-dependent + * on Unix systems, it needs to cater to the TZ environment variable, uhhh + * + * if TZ is defined to something like "CET" or "PST", this will return the name "Unknown/Unknown" + */ +extern char* bufprint_zoneinfo_timezone( char* buffer, char* end ); + +#endif /* _ANDROID_UTILS_TIMEZONE_H */ |