aboutsummaryrefslogtreecommitdiffstats
path: root/android/utils
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
commit8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch)
tree7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /android/utils
parentf721e3ac031f892af46f255a47d7f54a91317b30 (diff)
downloadexternal_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.c230
-rw-r--r--android/utils/bufprint.h76
-rw-r--r--android/utils/debug.c141
-rw-r--r--android/utils/debug.h91
-rw-r--r--android/utils/dirscanner.c203
-rw-r--r--android/utils/dirscanner.h51
-rw-r--r--android/utils/display-quartz.m111
-rw-r--r--android/utils/display.c245
-rw-r--r--android/utils/display.h31
-rw-r--r--android/utils/filelock.c414
-rw-r--r--android/utils/filelock.h38
-rw-r--r--android/utils/ini.c416
-rw-r--r--android/utils/ini.h131
-rw-r--r--android/utils/misc.c193
-rw-r--r--android/utils/misc.h67
-rw-r--r--android/utils/path.c559
-rw-r--r--android/utils/path.h152
-rw-r--r--android/utils/reflist.c203
-rw-r--r--android/utils/reflist.h129
-rw-r--r--android/utils/stralloc.c300
-rw-r--r--android/utils/stralloc.h60
-rw-r--r--android/utils/system.c172
-rw-r--r--android/utils/system.h161
-rw-r--r--android/utils/tempfile.c198
-rw-r--r--android/utils/tempfile.h52
-rw-r--r--android/utils/timezone.c721
-rw-r--r--android/utils/timezone.h31
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 */