auto import from //depot/cupcake/@135843
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..8b79ceb
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+  include $(call first-makefiles-under,$(LOCAL_PATH))
+else
+  include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+	      adb \
+	      libcutils \
+	      liblog \
+	      libnetutils \
+	      libpixelflinger \
+	      libzipfile \
+	   ))
+endif
diff --git a/README b/README
new file mode 100644
index 0000000..0083247
--- /dev/null
+++ b/README
@@ -0,0 +1,20 @@
+
+The system/ directory is intended for pieces of the world that are the
+core of the embedded linux platform at the heart of Android.  These
+essential bits are required for basic booting, operation, and debugging.
+
+They should not depend on libraries outside of system/... (some of them
+do currently -- they need to be updated or changed) and they should not
+be required for the simulator build.
+
+The license for all these pieces should be clean (Apache2, BSD, or MIT).
+
+Currently system/bluetooth/... and system/extra/... have some pieces
+with GPL/LGPL licensed code.
+
+Assorted Issues:
+
+- pppd depends on libutils for logging
+- pppd depends on libcrypt/libcrypto
+- init, linker, debuggerd, toolbox, usbd depend on libcutils
+- should probably rename bionic to libc
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644
index 0000000..2296610
--- /dev/null
+++ b/adb/Android.mk
@@ -0,0 +1,140 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for adb
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# adb host tool
+# =========================================================
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim)
+include $(CLEAR_VARS)
+
+# Default to a virtual (sockets) usb interface
+USB_SRCS :=
+EXTRA_SRCS :=
+
+ifeq ($(HOST_OS),linux)
+  USB_SRCS := usb_linux.c
+  EXTRA_SRCS := get_my_path_linux.c
+  LOCAL_LDLIBS += -lrt -lncurses -lpthread
+endif
+
+ifeq ($(HOST_OS),darwin)
+  USB_SRCS := usb_osx.c
+  EXTRA_SRCS := get_my_path_darwin.c
+  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+endif
+
+ifeq ($(HOST_OS),windows)
+  USB_SRCS := usb_windows.c
+  EXTRA_SRCS := get_my_path_windows.c
+  EXTRA_STATIC_LIBS := AdbWinApi
+  LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api/
+  ifneq ($(strip $(USE_CYGWIN)),)
+    LOCAL_LDLIBS += -lpthread
+  else
+    LOCAL_LDLIBS += -lws2_32
+    USE_SYSDEPS_WIN32 := 1
+  endif
+endif
+
+LOCAL_SRC_FILES := \
+	adb.c \
+	console.c \
+	transport.c \
+	transport_local.c \
+	transport_usb.c \
+	commandline.c \
+	adb_client.c \
+	sockets.c \
+	services.c \
+	file_sync_client.c \
+	$(EXTRA_SRCS) \
+	$(USB_SRCS) \
+	shlist.c \
+	utils.c \
+
+
+ifneq ($(USE_SYSDEPS_WIN32),)
+  LOCAL_SRC_FILES += sysdeps_win32.c
+endif
+
+LOCAL_CFLAGS += -O2 -g -DADB_HOST=1  -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
+LOCAL_MODULE := adb
+
+LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS)
+ifeq ($(USE_SYSDEPS_WIN32),)
+	LOCAL_STATIC_LIBRARIES += libcutils
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
+endif
+
+endif
+
+# adbd device daemon
+# =========================================================
+
+# build adbd in all non-simulator builds
+BUILD_ADBD := false
+ifneq ($(TARGET_SIMULATOR),true)
+    BUILD_ADBD := true
+endif
+
+# build adbd for the Linux simulator build
+# so we can use it to test the adb USB gadget driver on x86
+ifeq ($(HOST_OS),linux)
+    BUILD_ADBD := true
+endif
+
+
+ifeq ($(BUILD_ADBD),true)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	adb.c \
+	transport.c \
+	transport_local.c \
+	transport_usb.c \
+	sockets.c \
+	services.c \
+	file_sync_service.c \
+	jdwp_service.c \
+	framebuffer_service.c \
+	remount_service.c \
+	usb_linux_client.c \
+	log_service.c \
+	utils.c \
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+# TODO: This should probably be board specific, whether or not the kernel has
+# the gadget driver; rather than relying on the architecture type.
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_CFLAGS += -DANDROID_GADGET=1
+endif
+
+LOCAL_MODULE := adbd
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+
+ifeq ($(TARGET_SIMULATOR),true)
+  LOCAL_STATIC_LIBRARIES := libcutils
+  LOCAL_LDLIBS += -lpthread
+  include $(BUILD_HOST_EXECUTABLE)
+else
+  LOCAL_STATIC_LIBRARIES := libcutils libc
+  include $(BUILD_EXECUTABLE)
+endif
+
+endif
diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT
new file mode 100644
index 0000000..6a5191a
--- /dev/null
+++ b/adb/OVERVIEW.TXT
@@ -0,0 +1,139 @@
+Implementation notes regarding ADB.
+
+I. General Overview:
+
+The Android Debug Bridge (ADB) is used to:
+
+- keep track of all Android devices and emulators instances
+  connected to or running on a given host developer machine
+
+- implement various control commands (e.g. "adb shell", "adb pull", etc..)
+  for the benefit of clients (command-line users, or helper programs like
+  DDMS). These commands are what is called a 'service' in ADB.
+
+As a whole, everything works through the following components:
+
+  1. The ADB server
+
+    This is a background process that runs on the host machine. Its purpose
+    if to sense the USB ports to know when devices are attached/removed,
+    as well as when emulator instances start/stop.
+
+    It thus maintains a list of "connected devices" and assigns a 'state'
+    to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
+    this below).
+
+    The ADB server is really one giant multiplexing loop whose purpose is
+    to orchestrate the exchange of data (packets, really) between clients,
+    services and devices.
+
+
+  2. The ADB daemon (adbd)
+
+    The 'adbd' program runs as a background process within an Android device
+    or emulated system. Its purpose is to connect to the ADB server
+    (through USB for devices, through TCP for emulators) and provide a
+    few services for clients that run on the host.
+
+    The ADB server considers that a device is ONLINE when it has succesfully
+    connected to the adbd program within it. Otherwise, the device is OFFLINE,
+    meaning that the ADB server detected a new device/emulator, but could not
+    connect to the adbd daemon.
+
+    the BOOTLOADER and RECOVERY states correspond to alternate states of
+    devices when they are in the bootloader or recovery mode.
+
+  3. The ADB command-line client
+
+    The 'adb' command-line program is used to run adb commands from a shell
+    or a script. It first tries to locate the ADB server on the host machine,
+    and will start one automatically if none is found.
+
+    then, the client sends its service requests to the ADB server. It doesn't
+    need to know.
+
+    Currently, a single 'adb' binary is used for both the server and client.
+    this makes distribution and starting the server easier.
+
+
+  4. Services
+
+    There are essentially two kinds of services that a client can talk to.
+
+    Host Services:
+      these services run within the ADB Server and thus do not need to
+      communicate with a device at all. A typical example is "adb devices"
+      which is used to return the list of currently known devices and their
+      state. They are a few couple other services though.
+
+    Local Services:
+      these services either run within the adbd daemon, or are started by
+      it on the device. The ADB server is used to multiplex streams
+      between the client and the service running in adbd. In this case
+      its role is to initiate the connection, then of being a pass-through
+      for the data.
+
+
+II. Protocol details:
+
+  1. Client <-> Server protocol:
+
+    This details the protocol used between ADB clients and the ADB
+    server itself. The ADB server listens on TCP:localhost:5037.
+
+    A client sends a request using the following format:
+
+        1. A 4-byte hexadecimal string giving the length of the payload
+        2. Followed by the payload itself.
+
+    For example, to query the ADB server for its internal version number,
+    the client will do the following:
+
+        1. Connect to tcp:localhost:5037
+        2. Send the string "000Chost:version" to the corresponding socket
+
+    The 'host:' prefix is used to indicate that the request is addressed
+    to the server itself (we will talk about other kinds of requests later).
+    The content length is encoded in ASCII for easier debugging.
+
+    The server should answer a request with one of the following:
+
+        1. For success, the 4-byte "OKAY" string
+
+        2. For failure, the 4-byte "FAIL" string, followed by a
+           4-byte hex length, followed by a string giving the reason
+           for failure.
+
+        3. As a special exception, for 'host:version', a 4-byte
+           hex string corresponding to the server's internal version number
+
+    Note that the connection is still alive after an OKAY, which allows the
+    client to make other requests. But in certain cases, an OKAY will even
+    change the state of the connection. 
+
+    For example, the case of the 'host:transport:<serialnumber>' request,
+    where '<serialnumber>' is used to identify a given device/emulator; after
+    the "OKAY" answer, all further requests made by the client will go
+    directly to the corresponding adbd daemon.
+
+    The file SERVICES.TXT lists all services currently implemented by ADB.
+
+
+  2. Transports:
+
+    An ADB transport models a connection between the ADB server and one device
+    or emulator. There are currently two kinds of transports:
+
+       - USB transports, for physical devices through USB
+
+       - Local transports, for emulators running on the host, connected to
+         the server through TCP
+
+    In theory, it should be possible to write a local transport that proxies
+    a connection between an ADB server and a device/emulator connected to/
+    running on another machine. This hasn't been done yet though.
+
+    Each transport can carry one or more multiplexed streams between clients
+    and the device/emulator they point to. The ADB server must handle
+    unexpected transport disconnections (e.g. when a device is physically
+    unplugged) properly.
diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT
new file mode 100644
index 0000000..b0124a4
--- /dev/null
+++ b/adb/SERVICES.TXT
@@ -0,0 +1,236 @@
+This file tries to document all requests a client can make
+to the ADB server of an adbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here.
+
+HOST SERVICES:
+
+host:version
+    Ask the ADB server for its internal version number.
+
+    As a special exception, the server will respond with a 4-byte
+    hex string corresponding to its internal version number, without
+    any OKAY or FAIL.
+
+host:kill
+    Ask the ADB server to quit immediately. This is used when the
+    ADB client detects that an obsolete server is running after an
+    upgrade.
+
+host:devices
+    Ask to return the list of available Android devices and their
+    state. After the OKAY, this is followed by a 4-byte hex len,
+    and a string that will be dumped as-is by the client, then
+    the connection is closed
+
+host:track-devices
+    This is a variant of host:devices which doesn't close the
+    connection. Instead, a new device list description is sent
+    each time a device is added/removed or the state of a given
+    device changes (hex4 + content). This allows tools like DDMS
+    to track the state of connected devices in real-time without
+    polling the server repeatedly.
+
+host:emulator:<port>
+    This is a special query that is sent to the ADB server when a
+    new emulator starts up. <port> is a decimal number corresponding
+    to the emulator's ADB control port, i.e. the TCP port that the
+    emulator will forward automatically to the adbd daemon running
+    in the emulator system.
+
+    This mechanism allows the ADB server to know when new emulator
+    instances start.
+
+host:transport:<serial-number>
+    Ask to switch the connection to the device/emulator identified by
+    <serial-number>. After the OKAY response, every client request will
+    be sent directly to the adbd daemon running on the device.
+    (Used to implement the -s option)
+
+host:transport-usb
+    Ask to switch the connection to one device connected through USB
+    to the host machine. This will fail if there are more than one such
+    devices. (Used to implement the -d convenience option)
+
+host:transport-local
+    Ask to switch the connection to one emulator connected through TCP.
+    This will fail if there is more than one such emulator instance
+    running. (Used to implement the -e convenience option)
+
+host:transport-any
+    Another host:transport variant. Ask to switch the connection to
+    either the device or emulator connect to/running on the host.
+    Will fail if there is more than one such device/emulator available.
+    (Used when neither -s, -d or -e are provided)
+
+host-serial:<serial-number>:<request>
+    This is a special form of query, where the 'host-serial:<serial-number>:'
+    prefix can be used to indicate that the client is asking the ADB server
+    for information related to a specific device. <request> can be in one
+    of the format described below.
+
+host-usb:<request>
+    A variant of host-serial used to target the single USB device connected
+    to the host. This will fail if there is none or more than one.
+
+host-local:<request>
+    A variant of host-serial used to target the single emulator instance
+    running on the host. This will fail if therre is none or more than one.
+
+host:<request>
+    When asking for information related to a device, 'host:' can also be
+    interpreted as 'any single device or emulator connected to/running on
+    the host'.
+
+<host-prefix>:get-product
+    XXX
+
+<host-prefix>:get-serialno
+    Returns the serial number of the corresponding device/emulator.
+    Note that emulator serial numbers are of the form "emulator-5554"
+
+<host-prefix>:get-state
+    Returns the state of a given device as a string.
+
+<host-prefix>:forward:<local>;<remote>
+    Asks the ADB server to forward local connections from <local>
+    to the <remote> address on a given device.
+
+    There, <host-prefix> can be one of the
+    host-serial/host-usb/host-local/host prefixes as described previously
+    and indicates which device/emulator to target.
+
+    the format of <local> is one of:
+
+        tcp:<port>      -> TCP connection on localhost:<port>
+        local:<path>    -> Unix local domain socket on <path>
+
+    the format of <remote> is one of:
+
+        tcp:<port>      -> TCP localhost:<port> on device
+        local:<path>    -> Unix local domain socket on device
+        jdwp:<pid>      -> JDWP thread on VM process <pid>
+
+    or even any one of the local services described below.
+
+
+
+LOCAL SERVICES:
+
+All the queries below assumed that you already switched the transport
+to a real device, or that you have used a query prefix as described
+above.
+
+shell:command arg1 arg2 ...
+    Run 'command arg1 arg2 ...' in a shell on the device, and return
+    its output and error streams. Note that arguments must be separated
+    by spaces. If an argument contains a space, it must be quoted with
+    double-quotes. Arguments cannot contain double quotes or things
+    will go very wrong.
+
+    Note that this is the non-interactive version of "adb shell"
+
+shell:
+    Start an interactive shell session on the device. Redirect
+    stdin/stdout/stderr as appropriate. Note that the ADB server uses
+    this to implement "adb shell", but will also cook the input before
+    sending it to the device (see interactive_shell() in commandline.c)
+
+remount:
+    Ask adbd to remount the device's filesystem in read-write mode,
+    instead of read-only. This is usually necessary before performing
+    an "adb sync" or "adb push" request.
+
+    This request may not succeed on certain builds which do not allow
+    that.
+
+dev:<path>
+    Opens a device file and connects the client directly to it for
+    read/write purposes. Useful for debugging, but may require special
+    priviledges and thus may not run on all devices. <path> is a full
+    path from the root of the filesystem.
+
+tcp:<port>
+    Tries to connect to tcp port <port> on localhost.
+
+tcp:<port>:<server-name>
+    Tries to connect to tcp port <port> on machine <server-name> from
+    the device. This can be useful to debug some networking/proxy
+    issues that can only be revealed on the device itself.
+
+local:<path>
+    Tries to connect to a Unix domain socket <path> on the device
+
+localreserved:<path>
+localabstract:<path>
+localfilesystem:<path>
+    Variants of local:<path> that are used to access other Android
+    socket namespaces.
+
+log:<name>
+    Opens one of the system logs (/dev/log/<name>) and allows the client
+    to read them directly. Used to implement 'adb logcat'. The stream
+    will be read-only for the client.
+
+framebuffer:
+    This service is used to send snapshots of the framebuffer to a client.
+    It requires sufficient priviledges but works as follow:
+
+      After the OKAY, the service sends 16-byte binary structure
+      containing the following fields (little-endian format):
+
+            depth:   uint32_t:    framebuffer depth
+            size:    uint32_t:    framebuffer size in bytes
+            width:   uint32_t:    framebuffer width in pixels
+            height:  uint32_t:    framebuffer height in pixels
+
+      With the current implementation, depth is always 16, and
+      size is always width*height*2
+
+      Then, each time the client wants a snapshot, it should send
+      one byte through the channel, which will trigger the service
+      to send it 'size' bytes of framebuffer data.
+
+      If the adbd daemon doesn't have sufficient priviledges to open
+      the framebuffer device, the connection is simply closed immediately.
+
+dns:<server-name>
+    This service is an exception because it only runs within the ADB server.
+    It is used to implement USB networking, i.e. to provide a network connection
+    to the device through the host machine (note: this is the exact opposite of
+    network thetering).
+
+    It is used to perform a gethostbyname(<address>) on the host and return
+    the corresponding IP address as a 4-byte string.
+
+recover:<size>
+    This service is used to upload a recovery image to the device. <size>
+    must be a number corresponding to the size of the file. The service works
+    by:
+
+       - creating a file named /tmp/update
+       - reading 'size' bytes from the client and writing them to /tmp/update
+       - when everything is read succesfully, create a file named /tmp/update.start
+
+    This service can only work when the device is in recovery mode. Otherwise,
+    the /tmp directory doesn't exist and the connection will be closed immediately.
+
+jdwp:<pid>
+    Connects to the JDWP thread running in the VM of process <pid>.
+
+track-jdwp
+    This is used to send the list of JDWP pids periodically to the client.
+    The format of the returned data is the following:
+
+        <hex4>:    the length of all content as a 4-char hexadecimal string
+        <content>: a series of ASCII lines of the following format:
+                        <pid> "\n"
+
+    This service is used by DDMS to know which debuggable processes are running
+    on the device/emulator.
+
+    Note that there is no single-shot service to retrieve the list only once.
+
+sync:
+    This starts the file synchronisation service, used to implement "adb push"
+    and "adb pull". Since this service is pretty complex, it will be detailed
+    in a companion document named SYNC.TXT
diff --git a/adb/adb.c b/adb/adb.c
new file mode 100644
index 0000000..fa5269f
--- /dev/null
+++ b/adb/adb.c
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define  TRACE_TAG   TRACE_ADB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+
+#if !ADB_HOST
+#include <private/android_filesystem_config.h>
+#endif
+
+
+int HOST = 0;
+
+static const char *adb_device_banner = "device";
+
+void fatal(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+void fatal_errno(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "error: %s: ", strerror(errno));
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(-1);
+}
+
+int   adb_trace_mask;
+
+/* read a comma/space/colum/semi-column separated list of tags
+ * from the ADB_TRACE environment variable and build the trace
+ * mask from it. note that '1' and 'all' are special cases to
+ * enable all tracing
+ */
+void  adb_trace_init(void)
+{
+    const char*  p = getenv("ADB_TRACE");
+    const char*  q;
+
+    static const struct {
+        const char*  tag;
+        int           flag;
+    } tags[] = {
+        { "1", 0 },
+        { "all", 0 },
+        { "adb", TRACE_ADB },
+        { "sockets", TRACE_SOCKETS },
+        { "packets", TRACE_PACKETS },
+        { "rwx", TRACE_RWX },
+        { "usb", TRACE_USB },
+        { "sync", TRACE_SYNC },
+        { "sysdeps", TRACE_SYSDEPS },
+        { "transport", TRACE_TRANSPORT },
+        { "jdwp", TRACE_JDWP },
+        { NULL, 0 }
+    };
+
+    if (p == NULL)
+            return;
+
+    /* use a comma/column/semi-colum/space separated list */
+    while (*p) {
+        int  len, tagn;
+
+        q = strpbrk(p, " ,:;");
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+        len = q - p;
+
+        for (tagn = 0; tags[tagn].tag != NULL; tagn++)
+        {
+            int  taglen = strlen(tags[tagn].tag);
+
+            if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
+            {
+                int  flag = tags[tagn].flag;
+                if (flag == 0) {
+                    adb_trace_mask = ~0;
+                    return;
+                }
+                adb_trace_mask |= (1 << flag);
+                break;
+            }
+        }
+        p = q;
+        if (*p)
+            p++;
+    }
+}
+
+
+apacket *get_apacket(void)
+{
+    apacket *p = malloc(sizeof(apacket));
+    if(p == 0) fatal("failed to allocate an apacket");
+    memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+    return p;
+}
+
+void put_apacket(apacket *p)
+{
+    free(p);
+}
+
+void handle_online(void)
+{
+    D("adb: online\n");
+#if !ADB_HOST
+    property_set("adb.connected","1");
+#endif
+}
+
+void handle_offline(atransport *t)
+{
+    D("adb: offline\n");
+    //Close the associated usb
+    run_transport_disconnects(t);
+#if !ADB_HOST
+    property_set("adb.connected","");
+#endif
+}
+
+#if TRACE_PACKETS
+#define DUMPMAX 32
+void print_packet(const char *label, apacket *p)
+{
+    char *tag;
+    char *x;
+    unsigned count;
+
+    switch(p->msg.command){
+    case A_SYNC: tag = "SYNC"; break;
+    case A_CNXN: tag = "CNXN" ; break;
+    case A_OPEN: tag = "OPEN"; break;
+    case A_OKAY: tag = "OKAY"; break;
+    case A_CLSE: tag = "CLSE"; break;
+    case A_WRTE: tag = "WRTE"; break;
+    default: tag = "????"; break;
+    }
+
+    fprintf(stderr, "%s: %s %08x %08x %04x \"",
+            label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
+    count = p->msg.data_length;
+    x = (char*) p->data;
+    if(count > DUMPMAX) {
+        count = DUMPMAX;
+        tag = "\n";
+    } else {
+        tag = "\"\n";
+    }
+    while(count-- > 0){
+        if((*x >= ' ') && (*x < 127)) {
+            fputc(*x, stderr);
+        } else {
+            fputc('.', stderr);
+        }
+        x++;
+    }
+    fprintf(stderr, tag);
+}
+#endif
+
+static void send_ready(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_ready \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_close(unsigned local, unsigned remote, atransport *t)
+{
+    D("Calling send_close \n");
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    p->msg.arg0 = local;
+    p->msg.arg1 = remote;
+    send_packet(p, t);
+}
+
+static void send_connect(atransport *t)
+{
+    D("Calling send_connect \n");
+    apacket *cp = get_apacket();
+    cp->msg.command = A_CNXN;
+    cp->msg.arg0 = A_VERSION;
+    cp->msg.arg1 = MAX_PAYLOAD;
+    snprintf((char*) cp->data, sizeof cp->data, "%s::",
+            HOST ? "host" : adb_device_banner);
+    cp->msg.data_length = strlen((char*) cp->data) + 1;
+    send_packet(cp, t);
+#if ADB_HOST
+        /* XXX why sleep here? */
+    // allow the device some time to respond to the connect message
+    adb_sleep_ms(1000);
+#endif
+}
+
+static char *connection_state_name(atransport *t)
+{
+    if (t == NULL) {
+        return "unknown";
+    }
+
+    switch(t->connection_state) {
+    case CS_BOOTLOADER:
+        return "bootloader";
+    case CS_DEVICE:
+        return "device";
+    case CS_OFFLINE:
+        return "offline";
+    default:
+        return "unknown";
+    }
+}
+
+void parse_banner(char *banner, atransport *t)
+{
+    char *type, *product, *end;
+
+    D("parse_banner: %s\n", banner);
+    type = banner;
+    product = strchr(type, ':');
+    if(product) {
+        *product++ = 0;
+    } else {
+        product = "";
+    }
+
+        /* remove trailing ':' */
+    end = strchr(product, ':');
+    if(end) *end = 0;
+
+        /* save product name in device structure */
+    if (t->product == NULL) {
+        t->product = strdup(product);
+    } else if (strcmp(product, t->product) != 0) {
+        free(t->product);
+        t->product = strdup(product);
+    }
+
+    if(!strcmp(type, "bootloader")){
+        D("setting connection_state to CS_BOOTLOADER\n");
+        t->connection_state = CS_BOOTLOADER;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "device")) {
+        D("setting connection_state to CS_DEVICE\n");
+        t->connection_state = CS_DEVICE;
+        update_transports();
+        return;
+    }
+
+    if(!strcmp(type, "recovery")) {
+        D("setting connection_state to CS_RECOVERY\n");
+        t->connection_state = CS_RECOVERY;
+        update_transports();
+        return;
+    }
+
+    t->connection_state = CS_HOST;
+}
+
+void handle_packet(apacket *p, atransport *t)
+{
+    asocket *s;
+
+    D("handle_packet() %d\n", p->msg.command);
+
+    print_packet("recv", p);
+
+    switch(p->msg.command){
+    case A_SYNC:
+        if(p->msg.arg0){
+            send_packet(p, t);
+            if(HOST) send_connect(t);
+        } else {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+            send_packet(p, t);
+        }
+        return;
+
+    case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+            /* XXX verify version, etc */
+        if(t->connection_state != CS_OFFLINE) {
+            t->connection_state = CS_OFFLINE;
+            handle_offline(t);
+        }
+        parse_banner((char*) p->data, t);
+        handle_online();
+        if(!HOST) send_connect(t);
+        break;
+
+    case A_OPEN: /* OPEN(local-id, 0, "destination") */
+        if(t->connection_state != CS_OFFLINE) {
+            char *name = (char*) p->data;
+            name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
+            s = create_local_service_socket(name);
+            if(s == 0) {
+                send_close(0, p->msg.arg0, t);
+            } else {
+                s->peer = create_remote_socket(p->msg.arg0, t);
+                s->peer->peer = s;
+                send_ready(s->id, s->peer->id, t);
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_OKAY: /* READY(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                if(s->peer == 0) {
+                    s->peer = create_remote_socket(p->msg.arg0, t);
+                    s->peer->peer = s;
+                }
+                s->ready(s);
+            }
+        }
+        break;
+
+    case A_CLSE: /* CLOSE(local-id, remote-id, "") */
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                s->close(s);
+            }
+        }
+        break;
+
+    case A_WRTE:
+        if(t->connection_state != CS_OFFLINE) {
+            if((s = find_local_socket(p->msg.arg1))) {
+                unsigned rid = p->msg.arg0;
+                p->len = p->msg.data_length;
+
+                if(s->enqueue(s, p) == 0) {
+                    D("Enqueue the socket\n");
+                    send_ready(s->id, rid, t);
+                }
+                return;
+            }
+        }
+        break;
+
+    default:
+        printf("handle_packet: what is %08x?!\n", p->msg.command);
+    }
+
+    put_apacket(p);
+}
+
+alistener listener_list = {
+    .next = &listener_list,
+    .prev = &listener_list,
+};
+
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l)
+{
+    asocket *s;
+
+    if(ev & FDE_READ) {
+        struct sockaddr addr;
+        socklen_t alen;
+        int fd;
+
+        alen = sizeof(addr);
+        fd = adb_socket_accept(_fd, &addr, &alen);
+        if(fd < 0) return;
+
+        adb_socket_setbufsize(fd, CHUNK_SIZE);
+
+        s = create_local_socket(fd);
+        if(s) {
+            connect_to_smartsocket(s);
+            return;
+        }
+
+        adb_close(fd);
+    }
+}
+
+static void listener_event_func(int _fd, unsigned ev, void *_l)
+{
+    alistener *l = _l;
+    asocket *s;
+
+    if(ev & FDE_READ) {
+        struct sockaddr addr;
+        socklen_t alen;
+        int fd;
+
+        alen = sizeof(addr);
+        fd = adb_socket_accept(_fd, &addr, &alen);
+        if(fd < 0) return;
+
+        s = create_local_socket(fd);
+        if(s) {
+            s->transport = l->transport;
+            connect_to_remote(s, l->connect_to);
+            return;
+        }
+
+        adb_close(fd);
+    }
+}
+
+static void  free_listener(alistener*  l)
+{
+    if (l->next) {
+        l->next->prev = l->prev;
+        l->prev->next = l->next;
+        l->next = l->prev = l;
+    }
+
+    // closes the corresponding fd
+    fdevent_remove(&l->fde);
+
+    if (l->local_name)
+        free((char*)l->local_name);
+
+    if (l->connect_to)
+        free((char*)l->connect_to);
+
+    if (l->transport) {
+        remove_transport_disconnect(l->transport, &l->disconnect);
+    }
+    free(l);
+}
+
+static void listener_disconnect(void*  _l, atransport*  t)
+{
+    alistener*  l = _l;
+
+    free_listener(l);
+}
+
+int local_name_to_fd(const char *name)
+{
+    int port;
+
+    if(!strncmp("tcp:", name, 4)){
+        int  ret;
+        port = atoi(name + 4);
+        ret = socket_loopback_server(port, SOCK_STREAM);
+        return ret;
+    }
+#ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
+    // It's non-sensical to support the "reserved" space on the adb host side
+    if(!strncmp(name, "local:", 6)) {
+        return socket_local_server(name + 6,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localabstract:", 14)) {
+        return socket_local_server(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localfilesystem:", 16)) {
+        return socket_local_server(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+    }
+
+#endif
+    printf("unknown local portname '%s'\n", name);
+    return -1;
+}
+
+static int remove_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+    alistener *l;
+
+    for (l = listener_list.next; l != &listener_list; l = l->next) {
+        if (!strcmp(local_name, l->local_name) &&
+            !strcmp(connect_to, l->connect_to) &&
+            l->transport && l->transport == transport) {
+
+            listener_disconnect(l, transport);
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int install_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+    alistener *l;
+
+    //printf("install_listener('%s','%s')\n", local_name, connect_to);
+
+    for(l = listener_list.next; l != &listener_list; l = l->next){
+        if(strcmp(local_name, l->local_name) == 0) {
+            char *cto;
+
+                /* can't repurpose a smartsocket */
+            if(l->connect_to[0] == '*') {
+                return -1;
+            }
+
+            cto = strdup(connect_to);
+            if(cto == 0) {
+                return -1;
+            }
+
+            //printf("rebinding '%s' to '%s'\n", local_name, connect_to);
+            free((void*) l->connect_to);
+            l->connect_to = cto;
+            if (l->transport != transport) {
+                remove_transport_disconnect(l->transport, &l->disconnect);
+                l->transport = transport;
+                add_transport_disconnect(l->transport, &l->disconnect);
+            }
+            return 0;
+        }
+    }
+
+    if((l = calloc(1, sizeof(alistener))) == 0) goto nomem;
+    if((l->local_name = strdup(local_name)) == 0) goto nomem;
+    if((l->connect_to = strdup(connect_to)) == 0) goto nomem;
+
+
+    l->fd = local_name_to_fd(local_name);
+    if(l->fd < 0) {
+        free((void*) l->local_name);
+        free((void*) l->connect_to);
+        free(l);
+        printf("cannot bind '%s'\n", local_name);
+        return -2;
+    }
+
+    close_on_exec(l->fd);
+    if(!strcmp(l->connect_to, "*smartsocket*")) {
+        fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
+    } else {
+        fdevent_install(&l->fde, l->fd, listener_event_func, l);
+    }
+    fdevent_set(&l->fde, FDE_READ);
+
+    l->next = &listener_list;
+    l->prev = listener_list.prev;
+    l->next->prev = l;
+    l->prev->next = l;
+    l->transport = transport;
+
+    if (transport) {
+        l->disconnect.opaque = l;
+        l->disconnect.func   = listener_disconnect;
+        add_transport_disconnect(transport, &l->disconnect);
+    }
+    return 0;
+
+nomem:
+    fatal("cannot allocate listener");
+    return 0;
+}
+
+#ifdef HAVE_FORKEXEC
+static void sigchld_handler(int n)
+{
+    int status;
+    while(waitpid(-1, &status, WNOHANG) > 0) ;
+}
+#endif
+
+#ifdef HAVE_WIN32_PROC
+static BOOL WINAPI ctrlc_handler(DWORD type)
+{
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
+static void adb_cleanup(void)
+{
+    usb_cleanup();
+}
+
+void start_logging(void)
+{
+#ifdef HAVE_WIN32_PROC
+    char    temp[ MAX_PATH ];
+    FILE*   fnul;
+    FILE*   flog;
+
+    GetTempPath( sizeof(temp) - 8, temp );
+    strcat( temp, "adb.log" );
+
+    /* Win32 specific redirections */
+    fnul = fopen( "NUL", "rt" );
+    if (fnul != NULL)
+        stdin[0] = fnul[0];
+
+    flog = fopen( temp, "at" );
+    if (flog == NULL)
+        flog = fnul;
+
+    setvbuf( flog, NULL, _IONBF, 0 );
+
+    stdout[0] = flog[0];
+    stderr[0] = flog[0];
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#else
+    int fd;
+
+    fd = unix_open("/dev/null", O_RDONLY);
+    dup2(fd, 0);
+
+    fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
+    if(fd < 0) {
+        fd = unix_open("/dev/null", O_WRONLY);
+    }
+    dup2(fd, 1);
+    dup2(fd, 2);
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+#endif
+}
+
+#if !ADB_HOST
+void start_device_log(void)
+{
+    int fd;
+    char    path[100];
+
+    snprintf(path, sizeof path, "/data/adb_%ld.txt", (long)time(NULL));
+    fd = unix_open(path, O_WRONLY | O_CREAT | O_APPEND, 0640);
+    if (fd < 0)
+        return;
+
+    // redirect stdout and stderr to the log file
+    dup2(fd, 1);
+    dup2(fd, 2);
+    fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid());
+
+    fd = unix_open("/dev/null", O_RDONLY);
+    dup2(fd, 0);
+
+    // log everything
+    adb_trace_mask = ~0;
+    // except TRACE_RWX is a bit too verbose
+    adb_trace_mask &= ~TRACE_RWX;
+}
+#endif
+
+#if ADB_HOST
+int launch_server()
+{
+#ifdef HAVE_WIN32_PROC
+    /* we need to start the server in the background                    */
+    /* we create a PIPE that will be used to wait for the server's "OK" */
+    /* message since the pipe handles must be inheritable, we use a     */
+    /* security attribute                                               */
+    HANDLE                pipe_read, pipe_write;
+    SECURITY_ATTRIBUTES   sa;
+    STARTUPINFO           startup;
+    PROCESS_INFORMATION   pinfo;
+    char                  program_path[ MAX_PATH ];
+    int                   ret;
+
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+
+    /* create pipe, and ensure its read handle isn't inheritable */
+    ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
+    if (!ret) {
+        fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+        return -1;
+    }
+
+    SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+
+    ZeroMemory( &startup, sizeof(startup) );
+    startup.cb = sizeof(startup);
+    startup.hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
+    startup.hStdOutput = pipe_write;
+    startup.hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+    startup.dwFlags    = STARTF_USESTDHANDLES;
+
+    ZeroMemory( &pinfo, sizeof(pinfo) );
+
+    /* get path of current program */
+    GetModuleFileName( NULL, program_path, sizeof(program_path) );
+
+    ret = CreateProcess(
+            program_path,                              /* program path  */
+            "adb fork-server server",
+                                    /* the fork-server argument will set the
+                                       debug = 2 in the child           */
+            NULL,                   /* process handle is not inheritable */
+            NULL,                    /* thread handle is not inheritable */
+            TRUE,                          /* yes, inherit some handles */
+            DETACHED_PROCESS, /* the new process doesn't have a console */
+            NULL,                     /* use parent's environment block */
+            NULL,                    /* use parent's starting directory */
+            &startup,                 /* startup info, i.e. std handles */
+            &pinfo );
+
+    CloseHandle( pipe_write );
+
+    if (!ret) {
+        fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+        CloseHandle( pipe_read );
+        return -1;
+    }
+
+    CloseHandle( pinfo.hProcess );
+    CloseHandle( pinfo.hThread );
+
+    /* wait for the "OK\n" message */
+    {
+        char  temp[3];
+        DWORD  count;
+
+        ret = ReadFile( pipe_read, temp, 3, &count, NULL );
+        CloseHandle( pipe_read );
+        if ( !ret ) {
+            fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+            return -1;
+        }
+        if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+            fprintf(stderr, "ADB server didn't ACK\n" );
+            return -1;
+        }
+    }
+#elif defined(HAVE_FORKEXEC)
+    char    path[PATH_MAX];
+    int     fd[2];
+
+    // set up a pipe so the child can tell us when it is ready.
+    // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+    if (pipe(fd)) {
+        fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
+        return -1;
+    }
+    get_my_path(path);
+    pid_t pid = fork();
+    if(pid < 0) return -1;
+
+    if (pid == 0) {
+        // child side of the fork
+
+        // redirect stderr to the pipe
+        // we use stderr instead of stdout due to stdout's buffering behavior.
+        adb_close(fd[0]);
+        dup2(fd[1], STDERR_FILENO);
+        adb_close(fd[1]);
+
+        // child process
+        int result = execl(path, "adb", "fork-server", "server", NULL);
+        // this should not return
+        fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+    } else  {
+        // parent side of the fork
+
+        char  temp[3];
+
+        temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+        // wait for the "OK\n" message
+        adb_close(fd[1]);
+        int ret = adb_read(fd[0], temp, 3);
+        adb_close(fd[0]);
+        if (ret < 0) {
+            fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);
+            return -1;
+        }
+        if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+            fprintf(stderr, "ADB server didn't ACK\n" );
+            return -1;
+        }
+
+        setsid();
+    }
+#else
+#error "cannot implement background server start on this platform"
+#endif
+    return 0;
+}
+#endif
+
+int adb_main(int is_daemon)
+{
+#if !ADB_HOST
+    int secure = 0;
+    char value[PROPERTY_VALUE_MAX];
+
+    // prevent the OOM killer from killing us
+    char text[64];
+    snprintf(text, sizeof text, "/proc/%d/oom_adj", (int)getpid());
+    int fd = adb_open(text, O_WRONLY);
+    if (fd >= 0) {
+        // -17 should make us immune to OOM
+        snprintf(text, sizeof text, "%d", -17);
+        adb_write(fd, text, strlen(text));
+        adb_close(fd);
+    } else {
+       D("adb: unable to open %s\n", text);
+    }
+#endif
+
+    atexit(adb_cleanup);
+#ifdef HAVE_WIN32_PROC
+    SetConsoleCtrlHandler( ctrlc_handler, TRUE );
+#elif defined(HAVE_FORKEXEC)
+    signal(SIGCHLD, sigchld_handler);
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+
+#if ADB_HOST
+    HOST = 1;
+    usb_init();
+    local_init();
+
+    if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
+        exit(1);
+    }
+#else
+    /* run adbd in secure mode if ro.secure is set and
+    ** we are not in the emulator
+    */
+    property_get("ro.kernel.qemu", value, "");
+    if (strcmp(value, "1") != 0) {
+        property_get("ro.secure", value, "");
+        if (strcmp(value, "1") == 0)
+            secure = 1;
+    }
+
+    /* don't listen on port 5037 if we are running in secure mode */
+    /* don't run as root if we are running in secure mode */
+    if (secure) {
+        /* add extra groups:
+        ** AID_ADB to access the USB driver
+        ** AID_LOG to read system logs (adb logcat)
+        ** AID_INPUT to diagnose input issues (getevent)
+        ** AID_INET to diagnose network issues (netcfg, ping)
+        ** AID_GRAPHICS to access the frame buffer
+        */
+        gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS };
+        setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+        /* then switch user and group to "shell" */
+        setgid(AID_SHELL);
+        setuid(AID_SHELL);
+
+        D("Local port 5037 disabled\n");
+    } else {
+        if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
+            exit(1);
+        }
+    }
+
+        /* for the device, start the usb transport if the
+        ** android usb device exists, otherwise start the
+        ** network transport.
+        */
+    if(access("/dev/android_adb", F_OK) == 0 ||
+       access("/dev/android", F_OK) == 0) {
+        usb_init();
+    } else {
+        local_init();
+    }
+    init_jdwp();
+#endif
+
+    if (is_daemon)
+    {
+        // inform our parent that we are up and running.
+#ifdef HAVE_WIN32_PROC
+        DWORD  count;
+        WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
+#elif defined(HAVE_FORKEXEC)
+        fprintf(stderr, "OK\n");
+#endif
+        start_logging();
+    }
+
+    fdevent_loop();
+
+    usb_cleanup();
+
+    return 0;
+}
+
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
+{
+    atransport *transport = NULL;
+    char buf[4096];
+
+    if(!strcmp(service, "kill")) {
+        fprintf(stderr,"adb server killed by remote request\n");
+        fflush(stdout);
+        adb_write(reply_fd, "OKAY", 4);
+        usb_cleanup();
+        exit(0);
+    }
+
+#if ADB_HOST
+    // "transport:" is used for switching transport with a specified serial number
+    // "transport-usb:" is used for switching transport to the only USB transport
+    // "transport-local:" is used for switching transport to the only local transport
+    // "transport-any:" is used for switching transport to the only transport
+    if (!strncmp(service, "transport", strlen("transport"))) {
+        char* error_string = "unknown failure";
+        transport_type type = kTransportAny;
+
+        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+            type = kTransportUsb;
+        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
+            type = kTransportLocal;
+        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
+            type = kTransportAny;
+        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
+            service += strlen("transport:");
+            serial = strdup(service);
+        }
+
+        transport = acquire_one_transport(CS_ANY, type, serial, &error_string);
+
+        if (transport) {
+            s->transport = transport;
+            adb_write(reply_fd, "OKAY", 4);
+        } else {
+            sendfailmsg(reply_fd, error_string);
+        }
+        return 1;
+    }
+
+    // return a list of all connected devices
+    if (!strcmp(service, "devices")) {
+        char buffer[4096];
+        memset(buf, 0, sizeof(buf));
+        memset(buffer, 0, sizeof(buffer));
+        D("Getting device list \n");
+        list_transports(buffer, sizeof(buffer));
+        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+        D("Wrote device list \n");
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    // returns our value for ADB_SERVER_VERSION
+    if (!strcmp(service, "version")) {
+        char version[12];
+        snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
+        snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+
+    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
+        char *out = "unknown";
+         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+       if (transport && transport->serial) {
+            out = transport->serial;
+        }
+        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+    // indicates a new emulator instance has started
+    if (!strncmp(service,"emulator:",9)) {
+        int  port = atoi(service+9);
+        local_connect(port);
+        /* we don't even need to send a reply */
+        return 0;
+    }
+#endif // ADB_HOST
+
+    if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) {
+        char *local, *remote, *err;
+        int r;
+        atransport *transport;
+
+        int createForward = strncmp(service,"kill",4);
+
+        local = service + (createForward ? 8 : 12);
+        remote = strchr(local,';');
+        if(remote == 0) {
+            sendfailmsg(reply_fd, "malformed forward spec");
+            return 0;
+        }
+
+        *remote++ = 0;
+        if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
+            sendfailmsg(reply_fd, "malformed forward spec");
+            return 0;
+        }
+
+        transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
+        if (!transport) {
+            sendfailmsg(reply_fd, err);
+            return 0;
+        }
+
+        if (createForward) {
+            r = install_listener(local, remote, transport);
+        } else {
+            r = remove_listener(local, remote, transport);
+        }
+        if(r == 0) {
+                /* 1st OKAY is connect, 2nd OKAY is status */
+            writex(reply_fd, "OKAYOKAY", 8);
+            return 0;
+        }
+
+        if (createForward) {
+            sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket");
+        } else {
+            sendfailmsg(reply_fd, "cannot remove listener");
+        }
+        return 0;
+    }
+
+    if(!strncmp(service,"get-state",strlen("get-state"))) {
+        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+        char *state = connection_state_name(transport);
+        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
+        writex(reply_fd, buf, strlen(buf));
+        return 0;
+    }
+    return -1;
+}
+
+#if !ADB_HOST
+int recovery_mode = 0;
+#endif
+
+int main(int argc, char **argv)
+{
+    adb_trace_init();
+#if ADB_HOST
+    adb_sysdeps_init();
+    return adb_commandline(argc - 1, argv + 1);
+#else
+    if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
+        adb_device_banner = "recovery";
+        recovery_mode = 1;
+    }
+#if ADB_DEVICE_LOG
+    start_device_log();
+#endif
+    return adb_main(0);
+#endif
+}
+
diff --git a/adb/adb.h b/adb/adb.h
new file mode 100644
index 0000000..a17c8dd
--- /dev/null
+++ b/adb/adb.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_H
+#define __ADB_H
+
+#include <limits.h>
+
+#define MAX_PAYLOAD 4096
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+#define A_VERSION 0x01000000        // ADB protocol version
+
+#define ADB_VERSION_MAJOR 1         // Used for help/version information
+#define ADB_VERSION_MINOR 0         // Used for help/version information
+
+#define ADB_SERVER_VERSION    20    // Increment this when we want to force users to start a new adb server
+
+typedef struct amessage amessage;
+typedef struct apacket apacket;
+typedef struct asocket asocket;
+typedef struct alistener alistener;
+typedef struct aservice aservice;
+typedef struct atransport atransport;
+typedef struct adisconnect  adisconnect;
+typedef struct usb_handle usb_handle;
+
+struct amessage {
+    unsigned command;       /* command identifier constant      */
+    unsigned arg0;          /* first argument                   */
+    unsigned arg1;          /* second argument                  */
+    unsigned data_length;   /* length of payload (0 is allowed) */
+    unsigned data_check;    /* checksum of data payload         */
+    unsigned magic;         /* command ^ 0xffffffff             */
+};
+
+struct apacket
+{
+    apacket *next;
+
+    unsigned len;
+    unsigned char *ptr;
+
+    amessage msg;
+    unsigned char data[MAX_PAYLOAD];
+};
+
+/* An asocket represents one half of a connection between a local and
+** remote entity.  A local asocket is bound to a file descriptor.  A
+** remote asocket is bound to the protocol engine.
+*/
+struct asocket {
+        /* chain pointers for the local/remote list of
+        ** asockets that this asocket lives in
+        */
+    asocket *next;
+    asocket *prev;
+
+        /* the unique identifier for this asocket
+        */
+    unsigned id;
+
+        /* flag: set when the socket's peer has closed
+        ** but packets are still queued for delivery
+        */
+    int    closing;
+
+        /* the asocket we are connected to
+        */
+
+    asocket *peer;
+
+        /* For local asockets, the fde is used to bind
+        ** us to our fd event system.  For remote asockets
+        ** these fields are not used.
+        */
+    fdevent fde;
+    int fd;
+
+        /* queue of apackets waiting to be written
+        */
+    apacket *pkt_first;
+    apacket *pkt_last;
+
+        /* enqueue is called by our peer when it has data
+        ** for us.  It should return 0 if we can accept more
+        ** data or 1 if not.  If we return 1, we must call
+        ** peer->ready() when we once again are ready to
+        ** receive data.
+        */
+    int (*enqueue)(asocket *s, apacket *pkt);
+
+        /* ready is called by the peer when it is ready for
+        ** us to send data via enqueue again
+        */
+    void (*ready)(asocket *s);
+
+        /* close is called by the peer when it has gone away.
+        ** we are not allowed to make any further calls on the
+        ** peer once our close method is called.
+        */
+    void (*close)(asocket *s);
+
+        /* socket-type-specific extradata */
+    void *extra;
+
+    	/* A socket is bound to atransport */
+    atransport *transport;
+};
+
+
+/* the adisconnect structure is used to record a callback that
+** will be called whenever a transport is disconnected (e.g. by the user)
+** this should be used to cleanup objects that depend on the
+** transport (e.g. remote sockets, listeners, etc...)
+*/
+struct  adisconnect
+{
+    void        (*func)(void*  opaque, atransport*  t);
+    void*         opaque;
+    adisconnect*  next;
+    adisconnect*  prev;
+};
+
+
+/* a transport object models the connection to a remote device or emulator
+** there is one transport per connected device/emulator. a "local transport"
+** connects through TCP (for the emulator), while a "usb transport" through
+** USB (for real devices)
+**
+** note that kTransportHost doesn't really correspond to a real transport
+** object, it's a special value used to indicate that a client wants to
+** connect to a service implemented within the ADB server itself.
+*/
+typedef enum transport_type {
+        kTransportUsb,
+        kTransportLocal,
+        kTransportAny,
+        kTransportHost,
+} transport_type;
+
+struct atransport
+{
+    atransport *next;
+    atransport *prev;
+
+    int (*read_from_remote)(apacket *p, atransport *t);
+    int (*write_to_remote)(apacket *p, atransport *t);
+    void (*close)(atransport *t);
+    void (*kick)(atransport *t);
+
+    int fd;
+    int transport_socket;
+    fdevent transport_fde;
+    int ref_count;
+    unsigned sync_token;
+    int connection_state;
+    transport_type type;
+
+        /* usb handle or socket fd as needed */
+    usb_handle *usb;
+    int sfd;
+
+        /* used to identify transports for clients */
+    char *serial;
+    char *product;
+
+        /* a list of adisconnect callbacks called when the transport is kicked */
+    int          kicked;
+    adisconnect  disconnects;
+};
+
+
+/* A listener is an entity which binds to a local port
+** and, upon receiving a connection on that port, creates
+** an asocket to connect the new local connection to a
+** specific remote service.
+**
+** TODO: some listeners read from the new connection to
+** determine what exact service to connect to on the far
+** side.
+*/
+struct alistener
+{
+    alistener *next;
+    alistener *prev;
+
+    fdevent fde;
+    int fd;
+
+    const char *local_name;
+    const char *connect_to;
+    atransport *transport;
+    adisconnect  disconnect;
+};
+
+
+void print_packet(const char *label, apacket *p);
+
+asocket *find_local_socket(unsigned id);
+void install_local_socket(asocket *s);
+void remove_socket(asocket *s);
+void close_all_sockets(atransport *t);
+
+#define  LOCAL_CLIENT_PREFIX  "emulator-"
+
+asocket *create_local_socket(int fd);
+asocket *create_local_service_socket(const char *destination);
+
+asocket *create_remote_socket(unsigned id, atransport *t);
+void connect_to_remote(asocket *s, const char *destination);
+void connect_to_smartsocket(asocket *s);
+
+void fatal(const char *fmt, ...);
+void fatal_errno(const char *fmt, ...);
+
+void handle_packet(apacket *p, atransport *t);
+void send_packet(apacket *p, atransport *t);
+
+void get_my_path(char s[PATH_MAX]);
+int launch_server();
+int adb_main(int is_daemon);
+
+
+/* transports are ref-counted
+** get_device_transport does an acquire on your behalf before returning
+*/
+void init_transport_registration(void);
+int  list_transports(char *buf, size_t  bufsize);
+void update_transports(void);
+
+asocket*  create_device_tracker(void);
+
+/* Obtain a transport from the available transports.
+** If state is != CS_ANY, only transports in that state are considered.
+** If serial is non-NULL then only the device with that serial will be chosen.
+** If no suitable transport is found, error is set.
+*/
+atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out);
+void   add_transport_disconnect( atransport*  t, adisconnect*  dis );
+void   remove_transport_disconnect( atransport*  t, adisconnect*  dis );
+void   run_transport_disconnects( atransport*  t );
+void   kick_transport( atransport*  t );
+
+/* initialize a transport object's func pointers and state */
+int  init_socket_transport(atransport *t, int s, int port);
+void init_usb_transport(atransport *t, usb_handle *usb);
+
+/* for MacOS X cleanup */
+void close_usb_devices();
+
+/* cause new transports to be init'd and added to the list */
+void register_socket_transport(int s, const char *serial, int  port);
+void register_usb_transport(usb_handle *h, const char *serial);
+
+int service_to_fd(const char *name);
+#if ADB_HOST
+asocket *host_service_to_socket(const char*  name, const char *serial);
+#endif
+
+#if !ADB_HOST
+int       init_jdwp(void);
+asocket*  create_jdwp_service_socket();
+asocket*  create_jdwp_tracker_service_socket();
+int       create_jdwp_connection_fd(int  jdwp_pid);
+#endif
+
+#if !ADB_HOST
+void framebuffer_service(int fd, void *cookie);
+void log_service(int fd, void *cookie);
+void remount_service(int fd, void *cookie);
+char * get_log_file_path(const char * log_name);
+#endif
+
+/* packet allocator */
+apacket *get_apacket(void);
+void put_apacket(apacket *p);
+
+int check_header(apacket *p);
+int check_data(apacket *p);
+
+/* convenience wrappers around read/write that will retry on
+** EINTR and/or short read/write.  Returns 0 on success, -1
+** on error or EOF.
+*/
+int readx(int fd, void *ptr, size_t len);
+int writex(int fd, const void *ptr, size_t len);
+
+/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
+
+#define  ADB_TRACE    1
+
+/* IMPORTANT: if you change the following list, don't
+ * forget to update the corresponding 'tags' table in
+ * the adb_trace_init() function implemented in adb.c
+ */
+typedef enum {
+    TRACE_ADB = 0,
+    TRACE_SOCKETS,
+    TRACE_PACKETS,
+    TRACE_TRANSPORT,
+    TRACE_RWX,
+    TRACE_USB,
+    TRACE_SYNC,
+    TRACE_SYSDEPS,
+    TRACE_JDWP,
+} AdbTrace;
+
+#if ADB_TRACE
+
+  int     adb_trace_mask;
+
+  void    adb_trace_init(void);
+
+#  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
+
+  /* you must define TRACE_TAG before using this macro */
+  #define  D(...)                                      \
+        do {                                           \
+            if (ADB_TRACING)                           \
+                fprintf(stderr, __VA_ARGS__ );         \
+        } while (0)
+#else
+#  define  D(...)          ((void)0)
+#  define  ADB_TRACING     0
+#endif
+
+
+/* set this to log to /data/adb/adb_<time>.txt on the device.
+ * has no effect if the /data/adb/ directory does not exist.
+ */
+#define ADB_DEVICE_LOG 0
+
+#if !TRACE_PACKETS
+#define print_packet(tag,p) do {} while (0)
+#endif
+
+#define ADB_PORT 5037
+#define ADB_LOCAL_TRANSPORT_PORT 5555
+
+// Google's USB Vendor ID
+#define VENDOR_ID_GOOGLE        0x18d1
+// HTC's USB Vendor ID
+#define VENDOR_ID_HTC           0x0bb4
+
+// products for VENDOR_ID_GOOGLE
+#define PRODUCT_ID_SOONER       0xd00d  // Sooner bootloader
+#define PRODUCT_ID_SOONER_COMP  0xdeed  // Sooner composite device
+
+// products for VENDOR_ID_HTC
+#define PRODUCT_ID_DREAM        0x0c01  // Dream bootloader
+#define PRODUCT_ID_DREAM_COMP   0x0c02  // Dream composite device
+
+void local_init();
+int  local_connect(int  port);
+
+/* usb host/client interface */
+void usb_init();
+void usb_cleanup();
+int usb_write(usb_handle *h, const void *data, int len);
+int usb_read(usb_handle *h, void *data, int len);
+int usb_close(usb_handle *h);
+void usb_kick(usb_handle *h);
+
+/* used for USB device detection */
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
+
+unsigned host_to_le32(unsigned n);
+int adb_commandline(int argc, char **argv);
+
+int connection_state(atransport *t);
+
+#define CS_ANY       -1
+#define CS_OFFLINE    0
+#define CS_BOOTLOADER 1
+#define CS_DEVICE     2
+#define CS_HOST       3
+#define CS_RECOVERY   4
+#define CS_ERROR      5
+
+extern int HOST;
+
+#define CHUNK_SIZE (64*1024)
+
+int sendfailmsg(int fd, const char *reason);
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
+
+#endif
diff --git a/adb/adb_client.c b/adb/adb_client.c
new file mode 100644
index 0000000..5868744
--- /dev/null
+++ b/adb/adb_client.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <zipfile/zipfile.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb_client.h"
+
+static transport_type __adb_transport = kTransportAny;
+static const char* __adb_serial = NULL;
+
+void adb_set_transport(transport_type type, const char* serial)
+{
+    __adb_transport = type;
+    __adb_serial = serial;
+}
+
+int  adb_get_emulator_console_port(void)
+{
+    const char*   serial = __adb_serial;
+    int           port;
+
+    if (serial == NULL) {
+        /* if no specific device was specified, we need to look at */
+        /* the list of connected devices, and extract an emulator  */
+        /* name from it. two emulators is an error                 */
+        char*  tmp = adb_query("host:devices");
+        char*  p   = tmp;
+        if(!tmp) {
+            printf("no emulator connected\n");
+            return -1;
+        }
+        while (*p) {
+            char*  q = strchr(p, '\n');
+            if (q != NULL)
+                *q++ = 0;
+            else
+                q = p + strlen(p);
+
+            if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) {
+                if (serial != NULL) {  /* more than one emulator listed */
+                    free(tmp);
+                    return -2;
+                }
+                serial = p;
+            }
+
+            p = q;
+        }
+        free(tmp);
+
+        if (serial == NULL)
+            return -1;  /* no emulator found */
+    }
+    else {
+        if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0)
+            return -1;  /* not an emulator */
+    }
+
+    serial += sizeof(LOCAL_CLIENT_PREFIX)-1;
+    port    = strtol(serial, NULL, 10);
+    return port;
+}
+
+static char __adb_error[256] = { 0 };
+
+const char *adb_error(void)
+{
+    return __adb_error;
+}
+
+static int switch_socket_transport(int fd)
+{
+    char service[64];
+    char tmp[5];
+    int len;
+
+    if (__adb_serial)
+        snprintf(service, sizeof service, "host:transport:%s", __adb_serial);
+    else {
+        char* transport_type = "???";
+
+         switch (__adb_transport) {
+            case kTransportUsb:
+                transport_type = "transport-usb";
+                break;
+            case kTransportLocal:
+                transport_type = "transport-local";
+                break;
+            case kTransportAny:
+                transport_type = "transport-any";
+                break;
+            case kTransportHost:
+                // no switch necessary
+                return 0;
+                break;
+        }
+
+        snprintf(service, sizeof service, "host:%s", transport_type);
+    }
+    len = strlen(service);
+    snprintf(tmp, sizeof tmp, "%04x", len);
+
+    if(writex(fd, tmp, 4) || writex(fd, service, len)) {
+        strcpy(__adb_error, "write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+    D("Switch transport in progress\n");
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        D("Switch transport failed\n");
+        return -1;
+    }
+    D("Switch transport success\n");
+    return 0;
+}
+
+int adb_status(int fd)
+{
+    unsigned char buf[5];
+    unsigned len;
+
+    if(readx(fd, buf, 4)) {
+        strcpy(__adb_error, "protocol fault (no status)");
+        return -1;
+    }
+
+    if(!memcmp(buf, "OKAY", 4)) {
+        return 0;
+    }
+
+    if(memcmp(buf, "FAIL", 4)) {
+        sprintf(__adb_error,
+                "protocol fault (status %02x %02x %02x %02x?!)",
+                buf[0], buf[1], buf[2], buf[3]);
+        return -1;
+    }
+
+    if(readx(fd, buf, 4)) {
+        strcpy(__adb_error, "protocol fault (status len)");
+        return -1;
+    }
+    buf[4] = 0;
+    len = strtoul((char*)buf, 0, 16);
+    if(len > 255) len = 255;
+    if(readx(fd, __adb_error, len)) {
+        strcpy(__adb_error, "protocol fault (status read)");
+        return -1;
+    }
+    __adb_error[len] = 0;
+    return -1;
+}
+
+int _adb_connect(const char *service)
+{
+    char tmp[5];
+    int len;
+    int fd;
+
+    D("_adb_connect: %s\n", service);
+    len = strlen(service);
+    if((len < 1) || (len > 1024)) {
+        strcpy(__adb_error, "service name too long");
+        return -1;
+    }
+    snprintf(tmp, sizeof tmp, "%04x", len);
+
+    fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
+    if(fd < 0) {
+        strcpy(__adb_error, "cannot connect to daemon");
+        return -2;
+    }
+
+    if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) {
+        return -1;
+    }
+
+    if(writex(fd, tmp, 4) || writex(fd, service, len)) {
+        strcpy(__adb_error, "write failure during connection");
+        adb_close(fd);
+        return -1;
+    }
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+int adb_connect(const char *service)
+{
+    // first query the adb server's version
+    int fd = _adb_connect("host:version");
+
+    if(fd == -2) {
+        fprintf(stdout,"* daemon not running. starting it now *\n");
+    start_server:
+        if(launch_server(0)) {
+            fprintf(stderr,"* failed to start daemon *\n");
+            return -1;
+        } else {
+            fprintf(stdout,"* daemon started successfully *\n");
+        }
+        /* give the server some time to start properly and detect devices */
+        adb_sleep_ms(2000);
+        // fall through to _adb_connect
+    } else {
+        // if server was running, check its version to make sure it is not out of date
+        char buf[100];
+        int n;
+        int version = ADB_SERVER_VERSION - 1;
+
+        // if we have a file descriptor, then parse version result
+        if(fd >= 0) {
+            if(readx(fd, buf, 4)) goto error;
+
+            buf[4] = 0;
+            n = strtoul(buf, 0, 16);
+            if(n > (int)sizeof(buf)) goto error;
+            if(readx(fd, buf, n)) goto error;
+            adb_close(fd);
+
+            if (sscanf(buf, "%04x", &version) != 1) goto error;
+        } else {
+            // if fd is -1, then check for "unknown host service",
+            // which would indicate a version of adb that does not support the version command
+            if (strcmp(__adb_error, "unknown host service") != 0)
+                return fd;
+        }
+
+        if(version != ADB_SERVER_VERSION) {
+            printf("adb server is out of date.  killing...\n");
+            fd = _adb_connect("host:kill");
+            adb_close(fd);
+
+            /* XXX can we better detect its death? */
+            adb_sleep_ms(2000);
+            goto start_server;
+        }
+    }
+
+    // if the command is start-server, we are done.
+    if (!strcmp(service, "host:start-server"))
+        return 0;
+
+    fd = _adb_connect(service);
+    if(fd == -2) {
+        fprintf(stderr,"** daemon still not running");
+    }
+
+    return fd;
+error:
+    adb_close(fd);
+    return -1;
+}
+
+
+int adb_command(const char *service)
+{
+    int fd = adb_connect(service);
+    if(fd < 0) {
+        return -1;
+    }
+
+    if(adb_status(fd)) {
+        adb_close(fd);
+        return -1;
+    }
+
+    return 0;
+}
+
+char *adb_query(const char *service)
+{
+    char buf[5];
+    unsigned n;
+    char *tmp;
+
+    D("adb_query: %s\n", service);
+    int fd = adb_connect(service);
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", __adb_error);
+        return 0;
+    }
+
+    if(readx(fd, buf, 4)) goto oops;
+
+    buf[4] = 0;
+    n = strtoul(buf, 0, 16);
+    if(n > 1024) goto oops;
+
+    tmp = malloc(n + 1);
+    if(tmp == 0) goto oops;
+
+    if(readx(fd, tmp, n) == 0) {
+        tmp[n] = 0;
+        adb_close(fd);
+        return tmp;
+    }
+    free(tmp);
+
+oops:
+    adb_close(fd);
+    return 0;
+}
+
+
diff --git a/adb/adb_client.h b/adb/adb_client.h
new file mode 100644
index 0000000..8061579
--- /dev/null
+++ b/adb/adb_client.h
@@ -0,0 +1,49 @@
+#ifndef _ADB_CLIENT_H_
+#define _ADB_CLIENT_H_
+
+#include "adb.h"
+
+/* connect to adb, connect to the named service, and return
+** a valid fd for interacting with that service upon success
+** or a negative number on failure
+*/
+int adb_connect(const char *service);
+int _adb_connect(const char *service);
+
+/* connect to adb, connect to the named service, return 0 if
+** the connection succeeded AND the service returned OKAY
+*/
+int adb_command(const char *service);
+
+/* connect to adb, connect to the named service, return
+** a malloc'd string of its response upon success or NULL
+** on failure.
+*/
+char *adb_query(const char *service);
+
+/* Set the preferred transport to connect to.
+*/
+void adb_set_transport(transport_type type, const char* serial);
+
+/* Return the console port of the currently connected emulator (if any)
+ * of -1 if there is no emulator, and -2 if there is more than one.
+ * assumes adb_set_transport() was alled previously...
+ */
+int  adb_get_emulator_console_port(void);
+
+/* send commands to the current emulator instance. will fail if there
+ * is zero, or more than one emulator connected (or if you use -s <serial>
+ * with a <serial> that does not designate an emulator)
+ */
+int  adb_send_emulator_command(int  argc, char**  argv);
+
+/* return verbose error string from last operation */
+const char *adb_error(void);
+
+/* read a standard adb status response (OKAY|FAIL) and
+** return 0 in the event of OKAY, -1 in the event of FAIL
+** or protocol error
+*/
+int adb_status(int fd);
+
+#endif
diff --git a/adb/commandline.c b/adb/commandline.c
new file mode 100644
index 0000000..be596ce
--- /dev/null
+++ b/adb/commandline.c
@@ -0,0 +1,1249 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "sysdeps.h"
+
+#ifdef HAVE_TERMIO_H
+#include <termios.h>
+#endif
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+#include "adb_client.h"
+#include "file_sync_service.h"
+
+#ifdef SH_HISTORY
+#include "shlist.h"
+#include "history.h"
+#endif
+
+enum {
+    IGNORE_DATA,
+    WIPE_DATA,
+    FLASH_DATA
+};
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...);
+
+void get_my_path(char s[PATH_MAX]);
+int find_sync_dirs(const char *srcarg,
+        char **android_srcdir_out, char **data_srcdir_out);
+int install_app(transport_type transport, char* serial, int argc, char** argv);
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
+
+static const char *gProductOutPath = NULL;
+
+static char *product_file(const char *extra)
+{
+    int n;
+    char *x;
+
+    if (gProductOutPath == NULL) {
+        fprintf(stderr, "adb: Product directory not specified; "
+                "use -p or define ANDROID_PRODUCT_OUT\n");
+        exit(1);
+    }
+
+    n = strlen(gProductOutPath) + strlen(extra) + 2;
+    x = malloc(n);
+    if (x == 0) {
+        fprintf(stderr, "adb: Out of memory (product_file())\n");
+        exit(1);
+    }
+
+    snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra);
+    return x;
+}
+
+void version(FILE * out) {
+    fprintf(out, "Android Debug Bridge version %d.%d.%d\n",
+         ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION);
+}
+
+void help()
+{
+    version(stderr);
+
+    fprintf(stderr,
+        "\n"
+        " -d                            - directs command to the only connected USB device\n"
+        "                                 returns an error if more than one USB device is present.\n"
+        " -e                            - directs command to the only running emulator.\n"
+        "                                 returns an error if more than one emulator is running.\n"
+        " -s <serial number>            - directs command to the USB device or emulator with\n"
+        "                                 the given serial number\n"
+        " -p <product name or path>     - simple product name like 'sooner', or\n"
+        "                                 a relative/absolute path to a product\n"
+        "                                 out directory like 'out/target/product/sooner'.\n"
+        "                                 If -p is not specified, the ANDROID_PRODUCT_OUT\n"
+        "                                 environment variable is used, which must\n"
+        "                                 be an absolute path.\n"
+        " devices                       - list all connected devices\n"
+        "\n"
+        "device commands:\n"
+        "  adb push <local> <remote>    - copy file/dir to device\n"
+        "  adb pull <remote> <local>    - copy file/dir from device\n"
+        "  adb sync [ <directory> ]     - copy host->device only if changed\n"
+        "                                 (see 'adb help all')\n"
+        "  adb shell                    - run remote shell interactively\n"
+        "  adb shell <command>          - run remote shell command\n"
+        "  adb emu <command>            - run emulator console command\n"
+        "  adb logcat [ <filter-spec> ] - View device log\n"
+        "  adb forward <local> <remote> - forward socket connections\n"
+        "                                 forward specs are one of: \n"
+        "                                   tcp:<port>\n"
+        "                                   localabstract:<unix domain socket name>\n"
+        "                                   localreserved:<unix domain socket name>\n"
+        "                                   localfilesystem:<unix domain socket name>\n"
+        "                                   dev:<character device name>\n"
+        "                                   jdwp:<process pid> (remote only)\n"
+        "  adb jdwp                     - list PIDs of processes hosting a JDWP transport\n"
+        "  adb install [-l] [-r] <file> - push this package file to the device and install it\n"
+        "                                 ('-l' means forward-lock the app)\n"
+        "                                 ('-r' means reinstall the app, keeping its data)\n"
+        "  adb uninstall [-k] <package> - remove this app package from the device\n"
+        "                                 ('-k' means keep the data and cache directories)\n"
+        "  adb bugreport                - return all information from the device\n"
+        "                                 that should be included in a bug report.\n"
+        "\n"
+        "  adb help                     - show this help message\n"
+        "  adb version                  - show version num\n"
+        "\n"
+        "DATAOPTS:\n"
+        " (no option)                   - don't touch the data partition\n"
+        "  -w                           - wipe the data partition\n"
+        "  -d                           - flash the data partition\n"
+        "\n"
+        "scripting:\n"
+        "  adb wait-for-device          - block until device is online\n"
+        "  adb start-server             - ensure that there is a server running\n"
+        "  adb kill-server              - kill the server if it is running\n"
+        "  adb get-state                - prints: offline | bootloader | device\n"
+        "  adb get-serialno             - prints: <serial-number>\n"
+        "  adb status-window            - continuously print device status for a specified device\n"
+        "  adb remount                  - remounts the /system partition on the device read-write\n"
+        "\n"
+        "networking:\n"
+        "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
+        " Note: you should not automatically start a PDP connection.\n"
+        " <tty> refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n"
+        " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n"
+        "\n"
+        "adb sync notes: adb sync [ <directory> ]\n"
+        "  <localdir> can be interpreted in several ways:\n"
+        "\n"
+        "  - If <directory> is not specified, both /system and /data partitions will be updated.\n"
+        "\n"
+        "  - If it is \"system\" or \"data\", only the corresponding partition\n"
+        "    is updated.\n"
+        );
+}
+
+int usage()
+{
+    help();
+    return 1;
+}
+
+#ifdef HAVE_TERMIO_H
+static struct termios tio_save;
+
+static void stdin_raw_init(int fd)
+{
+    struct termios tio;
+
+    if(tcgetattr(fd, &tio)) return;
+    if(tcgetattr(fd, &tio_save)) return;
+
+    tio.c_lflag = 0; /* disable CANON, ECHO*, etc */
+
+        /* no timeout but request at least one character per read */
+    tio.c_cc[VTIME] = 0;
+    tio.c_cc[VMIN] = 1;
+
+    tcsetattr(fd, TCSANOW, &tio);
+    tcflush(fd, TCIFLUSH);
+}
+
+static void stdin_raw_restore(int fd)
+{
+    tcsetattr(fd, TCSANOW, &tio_save);
+    tcflush(fd, TCIFLUSH);
+}
+#endif
+
+static void read_and_dump(int fd)
+{
+    char buf[4096];
+    int len;
+
+    while(fd >= 0) {
+        len = adb_read(fd, buf, 4096);
+        if(len == 0) {
+            break;
+        }
+
+        if(len < 0) {
+            if(errno == EINTR) continue;
+            break;
+        }
+        /* we want to output to stdout, so no adb_write here !! */
+        unix_write(1, buf, len);
+    }
+}
+
+#ifdef SH_HISTORY
+int shItemCmp( void *val, void *idata )
+{
+    return( (strcmp( val, idata ) == 0) );
+}
+#endif
+
+static void *stdin_read_thread(void *x)
+{
+    int fd, fdi;
+    unsigned char buf[1024];
+#ifdef SH_HISTORY
+    unsigned char realbuf[1024], *buf_ptr;
+    SHLIST history;
+    SHLIST *item = &history;
+    int cmdlen = 0, ins_flag = 0;
+#endif
+    int r, n;
+    int state = 0;
+
+    int *fds = (int*) x;
+    fd = fds[0];
+    fdi = fds[1];
+    free(fds);
+
+#ifdef SH_HISTORY
+    shListInitList( &history );
+#endif
+    for(;;) {
+        /* fdi is really the client's stdin, so use read, not adb_read here */
+        r = unix_read(fdi, buf, 1024);
+        if(r == 0) break;
+        if(r < 0) {
+            if(errno == EINTR) continue;
+            break;
+        }
+#ifdef SH_HISTORY
+        if( (r == 3) &&                                       /* Arrow processing */
+            (memcmp( (void *)buf, SH_ARROW_ANY, 2 ) == 0) ) {
+            switch( buf[2] ) {
+                case SH_ARROW_UP:
+                    item = shListGetNextItem( &history, item );
+                    break;
+                case SH_ARROW_DOWN:
+                    item = shListGetPrevItem( &history, item );
+                    break;
+                default:
+                    item = NULL;
+                    break;
+            }
+            memset( buf, SH_DEL_CHAR, cmdlen );
+            if( item != NULL ) {
+                n = snprintf( (char *)(&buf[cmdlen]), sizeof buf - cmdlen, "%s", (char *)(item->data) );
+                memcpy( realbuf, item->data, n );
+            }
+            else { /* Clean buffer */
+                item = &history;
+                n = 0;
+            }
+            r = n + cmdlen;
+            cmdlen = n;
+            ins_flag = 0;
+            if( r == 0 )
+                continue;
+        }
+        else {
+#endif
+            for(n = 0; n < r; n++){
+                switch(buf[n]) {
+                case '\n':
+#ifdef SH_HISTORY
+                    if( ins_flag && (SH_BLANK_CHAR <= realbuf[0]) ) {
+                        buf_ptr = malloc(cmdlen + 1);
+                        if( buf_ptr != NULL ) {
+                            memcpy( buf_ptr, realbuf, cmdlen );
+                            buf_ptr[cmdlen] = '\0';
+                            if( (item = shListFindItem( &history, (void *)buf_ptr, shItemCmp )) == NULL ) {
+                                shListInsFirstItem( &history, (void *)buf_ptr );
+                                item = &history;
+                            }
+                        }
+                    }
+                    cmdlen = 0;
+                    ins_flag = 0;
+#endif
+                    state = 1;
+                    break;
+                case '\r':
+                    state = 1;
+                    break;
+                case '~':
+                    if(state == 1) state++;
+                    break;
+                case '.':
+                    if(state == 2) {
+                        fprintf(stderr,"\n* disconnect *\n");
+    #ifdef HAVE_TERMIO_H
+                        stdin_raw_restore(fdi);
+    #endif
+                        exit(0);
+                    }
+                default:
+#ifdef SH_HISTORY
+                    if( buf[n] == SH_DEL_CHAR ) {
+                        if( cmdlen > 0 )
+                            cmdlen--;
+                    }
+                    else {
+                        realbuf[cmdlen] = buf[n];
+                        cmdlen++;
+                    }
+                    ins_flag = 1;
+#endif
+                    state = 0;
+                }
+            }
+#ifdef SH_HISTORY
+        }
+#endif
+        r = adb_write(fd, buf, r);
+        if(r <= 0) {
+            break;
+        }
+    }
+#ifdef SH_HISTORY
+    shListDelAllItems( &history, (shListFree)free );
+#endif
+    return 0;
+}
+
+int interactive_shell(void)
+{
+    adb_thread_t thr;
+    int fdi, fd;
+    int *fds;
+
+    fd = adb_connect("shell:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+    fdi = 0; //dup(0);
+
+    fds = malloc(sizeof(int) * 2);
+    fds[0] = fd;
+    fds[1] = fdi;
+
+#ifdef HAVE_TERMIO_H
+    stdin_raw_init(fdi);
+#endif
+    adb_thread_create(&thr, stdin_read_thread, fds);
+    read_and_dump(fd);
+#ifdef HAVE_TERMIO_H
+    stdin_raw_restore(fdi);
+#endif
+    return 0;
+}
+
+
+static void format_host_command(char* buffer, size_t  buflen, const char* command, transport_type ttype, const char* serial)
+{
+    if (serial) {
+        snprintf(buffer, buflen, "host-serial:%s:%s", serial, command);
+    } else {
+        const char* prefix = "host";
+        if (ttype == kTransportUsb)
+            prefix = "host-usb";
+        else if (ttype == kTransportLocal)
+            prefix = "host-local";
+
+        snprintf(buffer, buflen, "%s:%s", prefix, command);
+    }
+}
+
+static void status_window(transport_type ttype, const char* serial)
+{
+    char command[4096];
+    char *state = 0;
+    char *laststate = 0;
+
+        /* silence stderr */
+#ifdef _WIN32
+    /* XXX: TODO */
+#else
+    int  fd;
+    fd = unix_open("/dev/null", O_WRONLY);
+    dup2(fd, 2);
+    adb_close(fd);
+#endif
+
+    format_host_command(command, sizeof command, "get-state", ttype, serial);
+
+    for(;;) {
+        adb_sleep_ms(250);
+
+        if(state) {
+            free(state);
+            state = 0;
+        }
+
+        state = adb_query(command);
+
+        if(state) {
+            if(laststate && !strcmp(state,laststate)){
+                continue;
+            } else {
+                if(laststate) free(laststate);
+                laststate = strdup(state);
+            }
+        }
+
+        printf("%c[2J%c[2H", 27, 27);
+        printf("Android Debug Bridge\n");
+        printf("State: %s\n", state ? state : "offline");
+        fflush(stdout);
+    }
+}
+
+/** duplicate string and quote all \ " ( ) chars + space character. */
+static char *
+dupAndQuote(const char *s)
+{
+    const char *ts;
+    size_t alloc_len;
+    char *ret;
+    char *dest;
+
+    ts = s;
+
+    alloc_len = 0;
+
+    for( ;*ts != '\0'; ts++) {
+        alloc_len++;
+        if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+            alloc_len++;
+        }
+    }
+
+    ret = (char *)malloc(alloc_len + 1);
+
+    ts = s;
+    dest = ret;
+
+    for ( ;*ts != '\0'; ts++) {
+        if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+            *dest++ = '\\';
+        }
+
+        *dest++ = *ts;
+    }
+
+    *dest++ = '\0';
+
+    return ret;
+}
+
+/**
+ * Run ppp in "notty" mode against a resource listed as the first parameter
+ * eg:
+ *
+ * ppp dev:/dev/omap_csmi_tty0 <ppp options>
+ *
+ */
+int ppp(int argc, char **argv)
+{
+#ifdef HAVE_WIN32_PROC
+    fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
+    return -1;
+#else
+    char *adb_service_name;
+    pid_t pid;
+    int fd;
+
+    if (argc < 2) {
+        fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
+                argv[0]);
+
+        return 1;
+    }
+
+    adb_service_name = argv[1];
+
+    fd = adb_connect(adb_service_name);
+
+    if(fd < 0) {
+        fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
+                adb_service_name, adb_error());
+        return 1;
+    }
+
+    pid = fork();
+
+    if (pid < 0) {
+        perror("from fork()");
+        return 1;
+    } else if (pid == 0) {
+        int err;
+        int i;
+        const char **ppp_args;
+
+        // copy args
+        ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+        ppp_args[0] = "pppd";
+        for (i = 2 ; i < argc ; i++) {
+            //argv[2] and beyond become ppp_args[1] and beyond
+            ppp_args[i - 1] = argv[i];
+        }
+        ppp_args[i-1] = NULL;
+
+        // child side
+
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        adb_close(STDERR_FILENO);
+        adb_close(fd);
+
+        err = execvp("pppd", (char * const *)ppp_args);
+
+        if (err < 0) {
+            perror("execing pppd");
+        }
+        exit(-1);
+    } else {
+        // parent side
+
+        adb_close(fd);
+        return 0;
+    }
+#endif /* !HAVE_WIN32_PROC */
+}
+
+static int send_shellcommand(transport_type transport, char* serial, char* buf)
+{
+    int fd, ret;
+
+    for(;;) {
+        fd = adb_connect(buf);
+        if(fd >= 0)
+            break;
+        fprintf(stderr,"- waiting for device -\n");
+        adb_sleep_ms(1000);
+        do_cmd(transport, serial, "wait-for-device", 0);
+    }
+
+    read_and_dump(fd);
+    ret = adb_close(fd);
+    if (ret)
+        perror("close");
+
+    return ret;
+}
+
+static int logcat(transport_type transport, char* serial, int argc, char **argv)
+{
+    char buf[4096];
+
+    char *log_tags;
+    char *quoted_log_tags;
+
+    log_tags = getenv("ANDROID_LOG_TAGS");
+    quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags);
+
+    snprintf(buf, sizeof(buf),
+        "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat",
+        quoted_log_tags);
+
+    free(quoted_log_tags);
+
+    argc -= 1;
+    argv += 1;
+    while(argc-- > 0) {
+        char *quoted;
+
+        quoted = dupAndQuote (*argv++);
+
+        strncat(buf, " ", sizeof(buf)-1);
+        strncat(buf, quoted, sizeof(buf)-1);
+        free(quoted);
+    }
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make"
+static int top_works(const char *top)
+{
+    if (top != NULL && adb_is_absolute_host_path(top)) {
+        char path_buf[PATH_MAX];
+        snprintf(path_buf, sizeof(path_buf),
+                "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top);
+        return access(path_buf, F_OK) == 0;
+    }
+    return 0;
+}
+
+static char *find_top_from(const char *indir, char path_buf[PATH_MAX])
+{
+    strcpy(path_buf, indir);
+    while (1) {
+        if (top_works(path_buf)) {
+            return path_buf;
+        }
+        char *s = adb_dirstop(path_buf);
+        if (s != NULL) {
+            *s = '\0';
+        } else {
+            path_buf[0] = '\0';
+            return NULL;
+        }
+    }
+}
+
+static char *find_top(char path_buf[PATH_MAX])
+{
+    char *top = getenv("ANDROID_BUILD_TOP");
+    if (top != NULL && top[0] != '\0') {
+        if (!top_works(top)) {
+            fprintf(stderr, "adb: bad ANDROID_BUILD_TOP value \"%s\"\n", top);
+            return NULL;
+        }
+    } else {
+        top = getenv("TOP");
+        if (top != NULL && top[0] != '\0') {
+            if (!top_works(top)) {
+                fprintf(stderr, "adb: bad TOP value \"%s\"\n", top);
+                return NULL;
+            }
+        } else {
+            top = NULL;
+        }
+    }
+
+    if (top != NULL) {
+        /* The environment pointed to a top directory that works.
+         */
+        strcpy(path_buf, top);
+        return path_buf;
+    }
+
+    /* The environment didn't help.  Walk up the tree from the CWD
+     * to see if we can find the top.
+     */
+    char dir[PATH_MAX];
+    top = find_top_from(getcwd(dir, sizeof(dir)), path_buf);
+    if (top == NULL) {
+        /* If the CWD isn't under a good-looking top, see if the
+         * executable is.
+         */
+        get_my_path(dir);
+        top = find_top_from(dir, path_buf);
+    }
+    return top;
+}
+
+/* <hint> may be:
+ * - A simple product name
+ *   e.g., "sooner"
+TODO: debug?  sooner-debug, sooner:debug?
+ * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
+ *   e.g., "out/target/product/sooner"
+ * - An absolute path to the PRODUCT_OUT dir
+ *   e.g., "/src/device/out/target/product/sooner"
+ *
+ * Given <hint>, try to construct an absolute path to the
+ * ANDROID_PRODUCT_OUT dir.
+ */
+static const char *find_product_out_path(const char *hint)
+{
+    static char path_buf[PATH_MAX];
+
+    if (hint == NULL || hint[0] == '\0') {
+        return NULL;
+    }
+
+    /* If it's already absolute, don't bother doing any work.
+     */
+    if (adb_is_absolute_host_path(hint)) {
+        strcpy(path_buf, hint);
+        return path_buf;
+    }
+
+    /* If there are any slashes in it, assume it's a relative path;
+     * make it absolute.
+     */
+    if (adb_dirstart(hint) != NULL) {
+        if (getcwd(path_buf, sizeof(path_buf)) == NULL) {
+            fprintf(stderr, "adb: Couldn't get CWD: %s\n", strerror(errno));
+            return NULL;
+        }
+        if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) {
+            fprintf(stderr, "adb: Couldn't assemble path\n");
+            return NULL;
+        }
+        strcat(path_buf, OS_PATH_SEPARATOR_STR);
+        strcat(path_buf, hint);
+        return path_buf;
+    }
+
+    /* It's a string without any slashes.  Try to do something with it.
+     *
+     * Try to find the root of the build tree, and build a PRODUCT_OUT
+     * path from there.
+     */
+    char top_buf[PATH_MAX];
+    const char *top = find_top(top_buf);
+    if (top == NULL) {
+        fprintf(stderr, "adb: Couldn't find top of build tree\n");
+        return NULL;
+    }
+//TODO: if we have a way to indicate debug, look in out/debug/target/...
+    snprintf(path_buf, sizeof(path_buf),
+            "%s" OS_PATH_SEPARATOR_STR
+            "out" OS_PATH_SEPARATOR_STR
+            "target" OS_PATH_SEPARATOR_STR
+            "product" OS_PATH_SEPARATOR_STR
+            "%s", top_buf, hint);
+    if (access(path_buf, F_OK) < 0) {
+        fprintf(stderr, "adb: Couldn't find a product dir "
+                "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf);
+        return NULL;
+    }
+    return path_buf;
+}
+
+int adb_commandline(int argc, char **argv)
+{
+    char buf[4096];
+    int no_daemon = 0;
+    int is_daemon = 0;
+    int persist = 0;
+    int r;
+    int quote;
+    transport_type ttype = kTransportAny;
+    char* serial = NULL;
+
+        /* If defined, this should be an absolute path to
+         * the directory containing all of the various system images
+         * for a particular product.  If not defined, and the adb
+         * command requires this information, then the user must
+         * specify the path using "-p".
+         */
+    gProductOutPath = getenv("ANDROID_PRODUCT_OUT");
+    if (gProductOutPath == NULL || gProductOutPath[0] == '\0') {
+        gProductOutPath = NULL;
+    }
+    // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
+
+        /* modifiers and flags */
+    while(argc > 0) {
+        if(!strcmp(argv[0],"nodaemon")) {
+            no_daemon = 1;
+        } else if (!strcmp(argv[0], "fork-server")) {
+            /* this is a special flag used only when the ADB client launches the ADB Server */
+            is_daemon = 1;
+        } else if(!strcmp(argv[0],"persist")) {
+            persist = 1;
+        } else if(!strncmp(argv[0], "-p", 2)) {
+            const char *product = NULL;
+            if (argv[0][2] == '\0') {
+                if (argc < 2) return usage();
+                product = argv[1];
+                argc--;
+                argv++;
+            } else {
+                product = argv[1] + 2;
+            }
+            gProductOutPath = find_product_out_path(product);
+            if (gProductOutPath == NULL) {
+                fprintf(stderr, "adb: could not resolve \"-p %s\"\n",
+                        product);
+                return usage();
+            }
+        } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+            if (isdigit(argv[0][2])) {
+                serial = argv[0] + 2;
+            } else {
+                if(argc < 2) return usage();
+                serial = argv[1];
+                argc--;
+                argv++;
+            }
+        } else if (!strcmp(argv[0],"-d")) {
+            ttype = kTransportUsb;
+        } else if (!strcmp(argv[0],"-e")) {
+            ttype = kTransportLocal;
+        } else {
+                /* out of recognized modifiers and flags */
+            break;
+        }
+        argc--;
+        argv++;
+    }
+
+    adb_set_transport(ttype, serial);
+
+    if ((argc > 0) && (!strcmp(argv[0],"server"))) {
+        if (no_daemon || is_daemon) {
+            r = adb_main(is_daemon);
+        } else {
+            r = launch_server();
+        }
+        if(r) {
+            fprintf(stderr,"* could not start server *\n");
+        }
+        return r;
+    }
+
+top:
+    if(argc == 0) {
+        return usage();
+    }
+
+    /* adb_connect() commands */
+
+    if(!strcmp(argv[0], "devices")) {
+        char *tmp;
+        snprintf(buf, sizeof buf, "host:%s", argv[0]);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("List of devices attached \n");
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    if (!strcmp(argv[0], "emu")) {
+        return adb_send_emulator_command(argc, argv);
+    }
+
+    if(!strcmp(argv[0], "shell")) {
+        int r;
+        int fd;
+
+        if(argc < 2) {
+            return interactive_shell();
+        }
+
+        snprintf(buf, sizeof buf, "shell:%s", argv[1]);
+        argc -= 2;
+        argv += 2;
+        while(argc-- > 0) {
+            strcat(buf, " ");
+
+            /* quote empty strings and strings with spaces */
+            quote = (**argv == 0 || strchr(*argv, ' '));
+            if (quote)
+            	strcat(buf, "\"");
+            strcat(buf, *argv++);
+            if (quote)
+            	strcat(buf, "\"");
+        }
+
+        for(;;) {
+            fd = adb_connect(buf);
+            if(fd >= 0) {
+                read_and_dump(fd);
+                adb_close(fd);
+                r = 0;
+            } else {
+                fprintf(stderr,"error: %s\n", adb_error());
+                r = -1;
+            }
+
+            if(persist) {
+                fprintf(stderr,"\n- waiting for device -\n");
+                adb_sleep_ms(1000);
+                do_cmd(ttype, serial, "wait-for-device", 0);
+            } else {
+                return r;
+            }
+        }
+    }
+
+    if(!strcmp(argv[0], "kill-server")) {
+        int fd;
+        fd = _adb_connect("host:kill");
+        if(fd == -1) {
+            fprintf(stderr,"* server not running *\n");
+            return 1;
+        }
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "remount")) {
+        int fd = adb_connect("remount:");
+        if(fd >= 0) {
+            read_and_dump(fd);
+            adb_close(fd);
+            return 0;
+        }
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(!strcmp(argv[0], "bugreport")) {
+        if (argc != 1) {
+            return 1;
+        }
+        do_cmd(ttype, serial, "shell", "dumpstate", "-", 0);
+        return 0;
+    }
+
+    /* adb_command() wrapper commands */
+
+    if(!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
+        char* service = argv[0];
+        if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) {
+            if (ttype == kTransportUsb) {
+                service = "wait-for-usb";
+            } else if (ttype == kTransportLocal) {
+                service = "wait-for-local";
+            } else {
+                service = "wait-for-any";
+            }
+        }
+
+        format_host_command(buf, sizeof buf, service, ttype, serial);
+
+        if (adb_command(buf)) {
+            D("failure: %s *\n",adb_error());
+            fprintf(stderr,"error: %s\n", adb_error());
+            return 1;
+        }
+
+        /* Allow a command to be run after wait-for-device,
+            * e.g. 'adb wait-for-device shell'.
+            */
+        if(argc > 1) {
+            argc--;
+            argv++;
+            goto top;
+        }
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "forward")) {
+        if(argc != 3) return usage();
+        if (serial) {
+            snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial,argv[1],argv[2]);
+        } else {
+            snprintf(buf, sizeof buf, "host:forward:%s;%s",argv[1],argv[2]);
+        }
+        if(adb_command(buf)) {
+            fprintf(stderr,"error: %s\n", adb_error());
+            return 1;
+        }
+        return 0;
+    }
+
+    /* do_sync_*() commands */
+
+    if(!strcmp(argv[0], "ls")) {
+        if(argc != 2) return usage();
+        return do_sync_ls(argv[1]);
+    }
+
+    if(!strcmp(argv[0], "push")) {
+        if(argc != 3) return usage();
+        return do_sync_push(argv[1], argv[2], 0 /* no verify APK */);
+    }
+
+    if(!strcmp(argv[0], "pull")) {
+        if(argc != 3) return usage();
+        return do_sync_pull(argv[1], argv[2]);
+    }
+
+    if(!strcmp(argv[0], "install")) {
+        if (argc < 2) return usage();
+        return install_app(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0], "uninstall")) {
+        if (argc < 2) return usage();
+        return uninstall_app(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0], "sync")) {
+        char *srcarg, *android_srcpath, *data_srcpath;
+        int ret;
+        if(argc < 2) {
+            /* No local path was specified. */
+            srcarg = NULL;
+        } else if(argc == 2) {
+            /* A local path or "android"/"data" arg was specified. */
+            srcarg = argv[1];
+        } else {
+            return usage();
+        }
+        ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath);
+        if(ret != 0) return usage();
+
+        if(android_srcpath != NULL)
+            ret = do_sync_sync(android_srcpath, "/system");
+        if(ret == 0 && data_srcpath != NULL)
+            ret = do_sync_sync(data_srcpath, "/data");
+
+        free(android_srcpath);
+        free(data_srcpath);
+        return ret;
+    }
+
+    /* passthrough commands */
+
+    if(!strcmp(argv[0],"get-state") ||
+        !strcmp(argv[0],"get-serialno"))
+    {
+        char *tmp;
+
+        format_host_command(buf, sizeof buf, argv[0], ttype, serial);
+        tmp = adb_query(buf);
+        if(tmp) {
+            printf("%s\n", tmp);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    /* other commands */
+
+    if(!strcmp(argv[0],"status-window")) {
+        status_window(ttype, serial);
+        return 0;
+    }
+
+    if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat")) {
+        return logcat(ttype, serial, argc, argv);
+    }
+
+    if(!strcmp(argv[0],"ppp")) {
+        return ppp(argc, argv);
+    }
+
+    if (!strcmp(argv[0], "start-server")) {
+        return adb_connect("host:start-server");
+    }
+
+    if (!strcmp(argv[0], "jdwp")) {
+        int  fd = adb_connect("jdwp");
+        if (fd >= 0) {
+            read_and_dump(fd);
+            adb_close(fd);
+            return 0;
+        } else {
+            fprintf(stderr, "error: %s\n", adb_error());
+            return -1;
+        }
+    }
+
+    /* "adb /?" is a common idiom under Windows */
+    if(!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+        help();
+        return 0;
+    }
+
+    if(!strcmp(argv[0], "version")) {
+        version(stdout);
+        return 0;
+    }
+
+    usage();
+    return 1;
+}
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...)
+{
+    char *argv[16];
+    int argc;
+    va_list ap;
+
+    va_start(ap, cmd);
+    argc = 0;
+
+    if (serial) {
+        argv[argc++] = "-s";
+        argv[argc++] = serial;
+    } else if (ttype == kTransportUsb) {
+        argv[argc++] = "-d";
+    } else if (ttype == kTransportLocal) {
+        argv[argc++] = "-e";
+    }
+
+    argv[argc++] = cmd;
+    while((argv[argc] = va_arg(ap, char*)) != 0) argc++;
+    va_end(ap);
+
+#if 0
+    int n;
+    fprintf(stderr,"argc = %d\n",argc);
+    for(n = 0; n < argc; n++) {
+        fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]);
+    }
+#endif
+
+    return adb_commandline(argc, argv);
+}
+
+int find_sync_dirs(const char *srcarg,
+        char **android_srcdir_out, char **data_srcdir_out)
+{
+    char *android_srcdir, *data_srcdir;
+
+    if(srcarg == NULL) {
+        android_srcdir = product_file("system");
+        data_srcdir = product_file("data");
+    } else {
+        /* srcarg may be "data", "system" or NULL.
+         * if srcarg is NULL, then both data and system are synced
+         */
+        if(strcmp(srcarg, "system") == 0) {
+            android_srcdir = product_file("system");
+            data_srcdir = NULL;
+        } else if(strcmp(srcarg, "data") == 0) {
+            android_srcdir = NULL;
+            data_srcdir = product_file("data");
+        } else {
+            /* It's not "system" or "data".
+             */
+            return 1;
+        }
+    }
+
+    if(android_srcdir_out != NULL)
+        *android_srcdir_out = android_srcdir;
+    else
+        free(android_srcdir);
+
+    if(data_srcdir_out != NULL)
+        *data_srcdir_out = data_srcdir;
+    else
+        free(data_srcdir);
+
+    return 0;
+}
+
+static int pm_command(transport_type transport, char* serial,
+                      int argc, char** argv)
+{
+    char buf[4096];
+
+    snprintf(buf, sizeof(buf), "shell:pm");
+
+    while(argc-- > 0) {
+        char *quoted;
+
+        quoted = dupAndQuote(*argv++);
+
+        strncat(buf, " ", sizeof(buf)-1);
+        strncat(buf, quoted, sizeof(buf)-1);
+        free(quoted);
+    }
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv)
+{
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    if (argc == 3 && strcmp(argv[1], "-k") == 0)
+    {
+        printf(
+            "The -k option uninstalls the application while retaining the data/cache.\n"
+            "At the moment, there is no way to remove the remaining data.\n"
+            "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+            "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
+        return -1;
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(transport_type transport, char* serial, char* filename)
+{
+    char buf[4096];
+    char* quoted;
+
+    snprintf(buf, sizeof(buf), "shell:rm ");
+    quoted = dupAndQuote(filename);
+    strncat(buf, quoted, sizeof(buf)-1);
+    free(quoted);
+
+    send_shellcommand(transport, serial, buf);
+    return 0;
+}
+
+int install_app(transport_type transport, char* serial, int argc, char** argv)
+{
+    struct stat st;
+    int err;
+    const char *const WHERE = "/data/local/tmp/%s";
+    char to[PATH_MAX];
+    char* filename = argv[argc - 1];
+    const char* p;
+
+    p = adb_dirstop(filename);
+    if (p) {
+        p++;
+        snprintf(to, sizeof to, WHERE, p);
+    } else {
+        snprintf(to, sizeof to, WHERE, filename);
+    }
+    if (p[0] == '\0') {
+    }
+
+    err = stat(filename, &st);
+    if (err != 0) {
+        fprintf(stderr, "can't find '%s' to install\n", filename);
+        return 1;
+    }
+    if (!S_ISREG(st.st_mode)) {
+        fprintf(stderr, "can't install '%s' because it's not a file\n",
+                filename);
+        return 1;
+    }
+
+    if (!(err = do_sync_push(filename, to, 1 /* verify APK */))) {
+        /* file in place; tell the Package Manager to install it */
+        argv[argc - 1] = to;       /* destination name, not source location */
+        pm_command(transport, serial, argc, argv);
+        delete_file(transport, serial, to);
+    }
+
+    return err;
+}
diff --git a/adb/console.c b/adb/console.c
new file mode 100644
index 0000000..b813d33
--- /dev/null
+++ b/adb/console.c
@@ -0,0 +1,45 @@
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_client.h"
+#include <stdio.h>
+
+static int  connect_to_console(void)
+{
+    int  fd, port;
+
+    port = adb_get_emulator_console_port();
+    if (port < 0) {
+        if (port == -2)
+            fprintf(stderr, "error: more than one emulator detected. use -s option\n");
+        else
+            fprintf(stderr, "error: no emulator detected\n");
+        return -1;
+    }
+    fd = socket_loopback_client( port, SOCK_STREAM );
+    if (fd < 0) {
+        fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+        return -1;
+    }
+    return  fd;
+}
+
+
+int  adb_send_emulator_command(int  argc, char**  argv)
+{
+    int   fd, nn;
+
+    fd = connect_to_console();
+    if (fd < 0)
+        return 1;
+
+#define  QUIT  "quit\n"
+
+    for (nn = 1; nn < argc; nn++) {
+        adb_write( fd, argv[nn], strlen(argv[nn]) );
+        adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
+    }
+    adb_write( fd, QUIT, sizeof(QUIT)-1 );
+    adb_close(fd);
+
+    return 0;
+}
diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c
new file mode 100644
index 0000000..4e6d385
--- /dev/null
+++ b/adb/file_sync_client.c
@@ -0,0 +1,1022 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <zipfile/zipfile.h>
+
+#include "sysdeps.h"
+#include "adb.h"
+#include "adb_client.h"
+#include "file_sync_service.h"
+
+
+static unsigned total_bytes;
+static long long start_time;
+
+static long long NOW()
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return ((long long) tv.tv_usec) +
+        1000000LL * ((long long) tv.tv_sec);
+}
+
+static void BEGIN()
+{
+    total_bytes = 0;
+    start_time = NOW();
+}
+
+static void END()
+{
+    long long t = NOW() - start_time;
+    if(total_bytes == 0) return;
+
+    if (t == 0)  /* prevent division by 0 :-) */
+        t = 1000000;
+
+    fprintf(stderr,"%lld KB/s (%d bytes in %lld.%03llds)\n",
+            ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
+            total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+}
+
+void sync_quit(int fd)
+{
+    syncmsg msg;
+
+    msg.req.id = ID_QUIT;
+    msg.req.namelen = 0;
+
+    writex(fd, &msg.req, sizeof(msg.req));
+}
+
+typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
+
+int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
+{
+    syncmsg msg;
+    char buf[257];
+    int len;
+
+    len = strlen(path);
+    if(len > 1024) goto fail;
+
+    msg.req.id = ID_LIST;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        goto fail;
+    }
+
+    for(;;) {
+        if(readx(fd, &msg.dent, sizeof(msg.dent))) break;
+        if(msg.dent.id == ID_DONE) return 0;
+        if(msg.dent.id != ID_DENT) break;
+
+        len = ltohl(msg.dent.namelen);
+        if(len > 256) break;
+
+        if(readx(fd, buf, len)) break;
+        buf[len] = 0;
+
+        func(ltohl(msg.dent.mode),
+             ltohl(msg.dent.size),
+             ltohl(msg.dent.time),
+             buf, cookie);
+    }
+
+fail:
+    adb_close(fd);
+    return -1;
+}
+
+typedef struct syncsendbuf syncsendbuf;
+
+struct syncsendbuf {
+    unsigned id;
+    unsigned size;
+    char data[SYNC_DATA_MAX];
+};
+
+static syncsendbuf send_buffer;
+
+int sync_readtime(int fd, const char *path, unsigned *timestamp)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+        return -1;
+    }
+
+    if(msg.stat.id != ID_STAT) {
+        return -1;
+    }
+
+    *timestamp = ltohl(msg.stat.time);
+    return 0;
+}
+
+static int sync_start_readtime(int fd, const char *path)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int sync_finish_readtime(int fd, unsigned int *timestamp,
+				unsigned int *mode, unsigned int *size)
+{
+    syncmsg msg;
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat)))
+        return -1;
+
+    if(msg.stat.id != ID_STAT)
+        return -1;
+
+    *timestamp = ltohl(msg.stat.time);
+    *mode = ltohl(msg.stat.mode);
+    *size = ltohl(msg.stat.size);
+
+    return 0;
+}
+
+int sync_readmode(int fd, const char *path, unsigned *mode)
+{
+    syncmsg msg;
+    int len = strlen(path);
+
+    msg.req.id = ID_STAT;
+    msg.req.namelen = htoll(len);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, path, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+        return -1;
+    }
+
+    if(msg.stat.id != ID_STAT) {
+        return -1;
+    }
+
+    *mode = ltohl(msg.stat.mode);
+    return 0;
+}
+
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
+{
+    int lfd, err = 0;
+
+    lfd = adb_open(path, O_RDONLY);
+    if(lfd < 0) {
+        fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
+        return -1;
+    }
+
+    sbuf->id = ID_DATA;
+    for(;;) {
+        int ret;
+
+        ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
+        if(!ret)
+            break;
+
+        if(ret < 0) {
+            if(errno == EINTR)
+                continue;
+            fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+            break;
+        }
+
+        sbuf->size = htoll(ret);
+        if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){
+            err = -1;
+            break;
+        }
+        total_bytes += ret;
+    }
+
+    adb_close(lfd);
+    return err;
+}
+
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
+{
+    int err = 0;
+    int total = 0;
+
+    sbuf->id = ID_DATA;
+    while (total < size) {
+        int count = size - total;
+        if (count > SYNC_DATA_MAX) {
+            count = SYNC_DATA_MAX;
+        }
+
+        memcpy(sbuf->data, &file_buffer[total], count);
+        sbuf->size = htoll(count);
+        if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){
+            err = -1;
+            break;
+        }
+        total += count;
+        total_bytes += count;
+    }
+
+    return err;
+}
+
+#ifdef HAVE_SYMLINKS
+static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
+{
+    int len, ret;
+
+    len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
+    if(len < 0) {
+        fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
+        return -1;
+    }
+    sbuf->data[len] = '\0';
+
+    sbuf->size = htoll(len + 1);
+    sbuf->id = ID_DATA;
+
+    ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
+    if(ret)
+        return -1;
+
+    total_bytes += len + 1;
+
+    return 0;
+}
+#endif
+
+static int sync_send(int fd, const char *lpath, const char *rpath,
+                     unsigned mtime, mode_t mode, int verifyApk)
+{
+    syncmsg msg;
+    int len, r;
+    syncsendbuf *sbuf = &send_buffer;
+    char* file_buffer = NULL;
+    int size = 0;
+    char tmp[64];
+
+    len = strlen(rpath);
+    if(len > 1024) goto fail;
+
+    snprintf(tmp, sizeof(tmp), ",%d", mode);
+    r = strlen(tmp);
+
+    if (verifyApk) {
+        int lfd;
+        zipfile_t zip;
+        zipentry_t entry;
+        int amt;
+
+        // if we are transferring an APK file, then sanity check to make sure
+        // we have a real zip file that contains an AndroidManifest.xml
+        // this requires that we read the entire file into memory.
+        lfd = adb_open(lpath, O_RDONLY);
+        if(lfd < 0) {
+            fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+            return -1;
+        }
+
+        size = adb_lseek(lfd, 0, SEEK_END);
+        if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) {
+            fprintf(stderr, "error seeking in file '%s'\n", lpath);
+            adb_close(lfd);
+            return 1;
+        }
+
+        file_buffer = (char *)malloc(size);
+        if (file_buffer == NULL) {
+            fprintf(stderr, "could not allocate buffer for '%s'\n",
+                    lpath);
+            adb_close(lfd);
+            return 1;
+        }
+        amt = adb_read(lfd, file_buffer, size);
+        if (amt != size) {
+            fprintf(stderr, "error reading from file: '%s'\n", lpath);
+            adb_close(lfd);
+            free(file_buffer);
+            return 1;
+        }
+
+        adb_close(lfd);
+
+        zip = init_zipfile(file_buffer, size);
+        if (zip == NULL) {
+            fprintf(stderr, "file '%s' is not a valid zip file\n",
+                    lpath);
+            free(file_buffer);
+            return 1;
+        }
+
+        entry = lookup_zipentry(zip, "AndroidManifest.xml");
+        release_zipfile(zip);
+        if (entry == NULL) {
+            fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
+                    lpath);
+            free(file_buffer);
+            return 1;
+        }
+    }
+
+    msg.req.id = ID_SEND;
+    msg.req.namelen = htoll(len + r);
+
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, rpath, len) || writex(fd, tmp, r)) {
+        free(file_buffer);
+        goto fail;
+    }
+
+    if (file_buffer) {
+        write_data_buffer(fd, file_buffer, size, sbuf);
+        free(file_buffer);
+    } else if (S_ISREG(mode))
+        write_data_file(fd, lpath, sbuf);
+#ifdef HAVE_SYMLINKS
+    else if (S_ISLNK(mode))
+        write_data_link(fd, lpath, sbuf);
+#endif
+    else
+        goto fail;
+
+    msg.data.id = ID_DONE;
+    msg.data.size = htoll(mtime);
+    if(writex(fd, &msg.data, sizeof(msg.data)))
+        goto fail;
+
+    if(readx(fd, &msg.status, sizeof(msg.status)))
+        return -1;
+
+    if(msg.status.id != ID_OKAY) {
+        if(msg.status.id == ID_FAIL) {
+            len = ltohl(msg.status.msglen);
+            if(len > 256) len = 256;
+            if(readx(fd, sbuf->data, len)) {
+                return -1;
+            }
+            sbuf->data[len] = 0;
+        } else
+            strcpy(sbuf->data, "unknown reason");
+
+        fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+        return -1;
+    }
+
+    return 0;
+
+fail:
+    fprintf(stderr,"protocol failure\n");
+    adb_close(fd);
+    return -1;
+}
+
+static int mkdirs(char *name)
+{
+    int ret;
+    char *x = name + 1;
+
+    for(;;) {
+        x = adb_dirstart(x);
+        if(x == 0) return 0;
+        *x = 0;
+        ret = adb_mkdir(name, 0775);
+        *x = OS_PATH_SEPARATOR;
+        if((ret < 0) && (errno != EEXIST)) {
+            return ret;
+        }
+        x++;
+    }
+    return 0;
+}
+
+int sync_recv(int fd, const char *rpath, const char *lpath)
+{
+    syncmsg msg;
+    int len;
+    int lfd = -1;
+    char *buffer = send_buffer.data;
+    unsigned id;
+
+    len = strlen(rpath);
+    if(len > 1024) return -1;
+
+    msg.req.id = ID_RECV;
+    msg.req.namelen = htoll(len);
+    if(writex(fd, &msg.req, sizeof(msg.req)) ||
+       writex(fd, rpath, len)) {
+        return -1;
+    }
+
+    if(readx(fd, &msg.data, sizeof(msg.data))) {
+        return -1;
+    }
+    id = msg.data.id;
+
+    if((id == ID_DATA) || (id == ID_DONE)) {
+        adb_unlink(lpath);
+        mkdirs((char *)lpath);
+        lfd = adb_creat(lpath, 0644);
+        if(lfd < 0) {
+            fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
+            return -1;
+        }
+        goto handle_data;
+    } else {
+        goto remote_error;
+    }
+
+    for(;;) {
+        if(readx(fd, &msg.data, sizeof(msg.data))) {
+            return -1;
+        }
+        id = msg.data.id;
+
+    handle_data:
+        len = ltohl(msg.data.size);
+        if(id == ID_DONE) break;
+        if(id != ID_DATA) goto remote_error;
+        if(len > SYNC_DATA_MAX) {
+            fprintf(stderr,"data overrun\n");
+            adb_close(lfd);
+            return -1;
+        }
+
+        if(readx(fd, buffer, len)) {
+            adb_close(lfd);
+            return -1;
+        }
+
+        if(writex(lfd, buffer, len)) {
+            fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+            adb_close(lfd);
+            return -1;
+        }
+
+        total_bytes += len;
+    }
+
+    adb_close(lfd);
+    return 0;
+
+remote_error:
+    adb_close(lfd);
+    adb_unlink(lpath);
+
+    if(id == ID_FAIL) {
+        len = ltohl(msg.data.size);
+        if(len > 256) len = 256;
+        if(readx(fd, buffer, len)) {
+            return -1;
+        }
+        buffer[len] = 0;
+    } else {
+        memcpy(buffer, &id, 4);
+        buffer[4] = 0;
+//        strcpy(buffer,"unknown reason");
+    }
+    fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
+    return 0;
+}
+
+
+
+/* --- */
+
+
+static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
+                          const char *name, void *cookie)
+{
+    printf("%08x %08x %08x %s\n", mode, size, time, name);
+}
+
+int do_sync_ls(const char *path)
+{
+    int fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
+        return 1;
+    } else {
+        sync_quit(fd);
+        return 0;
+    }
+}
+
+typedef struct copyinfo copyinfo;
+
+struct copyinfo
+{
+    copyinfo *next;
+    const char *src;
+    const char *dst;
+    unsigned int time;
+    unsigned int mode;
+    unsigned int size;
+    int flag;
+    //char data[0];
+};
+
+copyinfo *mkcopyinfo(const char *spath, const char *dpath,
+                     const char *name, int isdir)
+{
+    int slen = strlen(spath);
+    int dlen = strlen(dpath);
+    int nlen = strlen(name);
+    int ssize = slen + nlen + 2;
+    int dsize = dlen + nlen + 2;
+
+    copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize);
+    if(ci == 0) {
+        fprintf(stderr,"out of memory\n");
+        abort();
+    }
+
+    ci->next = 0;
+    ci->time = 0;
+    ci->mode = 0;
+    ci->size = 0;
+    ci->flag = 0;
+    ci->src = (const char*)(ci + 1);
+    ci->dst = ci->src + ssize;
+    snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
+    snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
+
+//    fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst);
+    return ci;
+}
+
+
+static int local_build_list(copyinfo **filelist,
+                            const char *lpath, const char *rpath)
+{
+    DIR *d;
+    struct dirent *de;
+    struct stat st;
+    copyinfo *dirlist = 0;
+    copyinfo *ci, *next;
+
+//    fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath);
+
+    d = opendir(lpath);
+    if(d == 0) {
+        fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+        return -1;
+    }
+
+    while((de = readdir(d))) {
+        char stat_path[PATH_MAX];
+        char *name = de->d_name;
+
+        if(name[0] == '.') {
+            if(name[1] == 0) continue;
+            if((name[1] == '.') && (name[2] == 0)) continue;
+        }
+
+        /*
+         * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
+         * always returns DT_UNKNOWN, so we just use stat() for all cases.
+         */
+        if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
+            continue;
+        strcpy(stat_path, lpath);
+        strcat(stat_path, de->d_name);
+        stat(stat_path, &st);
+
+        if (S_ISDIR(st.st_mode)) {
+            ci = mkcopyinfo(lpath, rpath, name, 1);
+            ci->next = dirlist;
+            dirlist = ci;
+        } else {
+            ci = mkcopyinfo(lpath, rpath, name, 0);
+            if(lstat(ci->src, &st)) {
+                closedir(d);
+                fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
+                return -1;
+            }
+            if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                fprintf(stderr, "skipping special file '%s'\n", ci->src);
+                free(ci);
+            } else {
+                ci->time = st.st_mtime;
+                ci->mode = st.st_mode;
+                ci->size = st.st_size;
+                ci->next = *filelist;
+                *filelist = ci;
+            }
+        }
+    }
+
+    closedir(d);
+
+    for(ci = dirlist; ci != 0; ci = next) {
+        next = ci->next;
+        local_build_list(filelist, ci->src, ci->dst);
+        free(ci);
+    }
+
+    return 0;
+}
+
+
+static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps)
+{
+    copyinfo *filelist = 0;
+    copyinfo *ci, *next;
+    int pushed = 0;
+    int skipped = 0;
+
+    if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
+    if(lpath[strlen(lpath) - 1] != '/') {
+        int  tmplen = strlen(lpath)+2;
+        char *tmp = malloc(tmplen);
+        if(tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/",lpath);
+        lpath = tmp;
+    }
+    if(rpath[strlen(rpath) - 1] != '/') {
+        int tmplen = strlen(rpath)+2;
+        char *tmp = malloc(tmplen);
+        if(tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/",rpath);
+        rpath = tmp;
+    }
+
+    if(local_build_list(&filelist, lpath, rpath)) {
+        return -1;
+    }
+
+    if(checktimestamps){
+        for(ci = filelist; ci != 0; ci = ci->next) {
+            if(sync_start_readtime(fd, ci->dst)) {
+                return 1;
+            }
+        }
+        for(ci = filelist; ci != 0; ci = ci->next) {
+            unsigned int timestamp, mode, size;
+            if(sync_finish_readtime(fd, &timestamp, &mode, &size))
+                return 1;
+            if(size == ci->size) {
+                /* for links, we cannot update the atime/mtime */
+                if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+                    (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                    ci->flag = 1;
+            }
+        }
+    }
+    for(ci = filelist; ci != 0; ci = next) {
+        next = ci->next;
+        if(ci->flag == 0) {
+            fprintf(stderr,"push: %s -> %s\n", ci->src, ci->dst);
+            if(sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
+                return 1;
+            }
+            pushed++;
+        } else {
+            skipped++;
+        }
+        free(ci);
+    }
+
+    fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
+            pushed, (pushed == 1) ? "" : "s",
+            skipped, (skipped == 1) ? "" : "s");
+
+    return 0;
+}
+
+
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk)
+{
+    struct stat st;
+    unsigned mode;
+    int fd;
+
+    fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(stat(lpath, &st)) {
+        fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
+        sync_quit(fd);
+        return 1;
+    }
+
+    if(S_ISDIR(st.st_mode)) {
+        BEGIN();
+        if(copy_local_dir_remote(fd, lpath, rpath, 0)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+        }
+    } else {
+        if(sync_readmode(fd, rpath, &mode)) {
+            return 1;
+        }
+        if((mode != 0) && S_ISDIR(mode)) {
+                /* if we're copying a local file to a remote directory,
+                ** we *really* want to copy to remotedir + "/" + localfilename
+                */
+            const char *name = adb_dirstop(lpath);
+            if(name == 0) {
+                name = lpath;
+            } else {
+                name++;
+            }
+            int  tmplen = strlen(name) + strlen(rpath) + 2;
+            char *tmp = malloc(strlen(name) + strlen(rpath) + 2);
+            if(tmp == 0) return 1;
+            snprintf(tmp, tmplen, "%s/%s", rpath, name);
+            rpath = tmp;
+        }
+        BEGIN();
+        if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+
+typedef struct {
+    copyinfo **filelist;
+    copyinfo **dirlist;
+    const char *rpath;
+    const char *lpath;
+} sync_ls_build_list_cb_args;
+
+void
+sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+                      const char *name, void *cookie)
+{
+    sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
+    copyinfo *ci;
+
+    if (S_ISDIR(mode)) {
+        copyinfo **dirlist = args->dirlist;
+
+        /* Don't try recursing down "." or ".." */
+        if (name[0] == '.') {
+            if (name[1] == '\0') return;
+            if ((name[1] == '.') && (name[2] == '\0')) return;
+        }
+
+        ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
+        ci->next = *dirlist;
+        *dirlist = ci;
+    } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+        copyinfo **filelist = args->filelist;
+
+        ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
+        ci->time = time;
+        ci->mode = mode;
+        ci->size = size;
+        ci->next = *filelist;
+        *filelist = ci;
+    } else {
+        fprintf(stderr, "skipping special file '%s'\n", name);
+    }
+}
+
+static int remote_build_list(int syncfd, copyinfo **filelist,
+                             const char *rpath, const char *lpath)
+{
+    copyinfo *dirlist = NULL;
+    sync_ls_build_list_cb_args args;
+
+    args.filelist = filelist;
+    args.dirlist = &dirlist;
+    args.rpath = rpath;
+    args.lpath = lpath;
+
+    /* Put the files/dirs in rpath on the lists. */
+    if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+        return 1;
+    }
+
+    /* Recurse into each directory we found. */
+    while (dirlist != NULL) {
+        copyinfo *next = dirlist->next;
+        if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+            return 1;
+        }
+        free(dirlist);
+        dirlist = next;
+    }
+
+    return 0;
+}
+
+static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
+                                 int checktimestamps)
+{
+    copyinfo *filelist = 0;
+    copyinfo *ci, *next;
+    int pulled = 0;
+    int skipped = 0;
+
+    /* Make sure that both directory paths end in a slash. */
+    if (rpath[0] == 0 || lpath[0] == 0) return -1;
+    if (rpath[strlen(rpath) - 1] != '/') {
+        int  tmplen = strlen(rpath) + 2;
+        char *tmp = malloc(tmplen);
+        if (tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/", rpath);
+        rpath = tmp;
+    }
+    if (lpath[strlen(lpath) - 1] != '/') {
+        int  tmplen = strlen(lpath) + 2;
+        char *tmp = malloc(tmplen);
+        if (tmp == 0) return -1;
+        snprintf(tmp, tmplen, "%s/", lpath);
+        lpath = tmp;
+    }
+
+    fprintf(stderr, "pull: building file list...\n");
+    /* Recursively build the list of files to copy. */
+    if (remote_build_list(fd, &filelist, rpath, lpath)) {
+        return -1;
+    }
+
+#if 0
+    if (checktimestamps) {
+        for (ci = filelist; ci != 0; ci = ci->next) {
+            if (sync_start_readtime(fd, ci->dst)) {
+                return 1;
+            }
+        }
+        for (ci = filelist; ci != 0; ci = ci->next) {
+            unsigned int timestamp, mode, size;
+            if (sync_finish_readtime(fd, &timestamp, &mode, &size))
+                return 1;
+	    if (size == ci->size) {
+                /* for links, we cannot update the atime/mtime */
+                if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+		   (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+                    ci->flag = 1;
+	    }
+        }
+    }
+#endif
+    for (ci = filelist; ci != 0; ci = next) {
+        next = ci->next;
+        if (ci->flag == 0) {
+            fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
+            if (sync_recv(fd, ci->src, ci->dst)) {
+                return 1;
+            }
+            pulled++;
+        } else {
+            skipped++;
+        }
+        free(ci);
+    }
+
+    fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
+            pulled, (pulled == 1) ? "" : "s",
+            skipped, (skipped == 1) ? "" : "s");
+
+    return 0;
+}
+
+int do_sync_pull(const char *rpath, const char *lpath)
+{
+    unsigned mode;
+    struct stat st;
+
+    int fd;
+
+    fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    if(sync_readmode(fd, rpath, &mode)) {
+        return 1;
+    }
+    if(mode == 0) {
+        fprintf(stderr,"remote object '%s' does not exist\n", rpath);
+        return 1;
+    }
+
+    if(S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+        if(stat(lpath, &st) == 0) {
+            if(S_ISDIR(st.st_mode)) {
+                    /* if we're copying a remote file to a local directory,
+                    ** we *really* want to copy to localdir + "/" + remotefilename
+                    */
+                const char *name = adb_dirstop(rpath);
+                if(name == 0) {
+                    name = rpath;
+                } else {
+                    name++;
+                }
+                int  tmplen = strlen(name) + strlen(lpath) + 2;
+                char *tmp = malloc(tmplen);
+                if(tmp == 0) return 1;
+                snprintf(tmp, tmplen, "%s/%s", lpath, name);
+                lpath = tmp;
+            }
+        }
+        BEGIN();
+        if(sync_recv(fd, rpath, lpath)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    } else if(S_ISDIR(mode)) {
+        BEGIN();
+        if (copy_remote_dir_local(fd, rpath, lpath, 0)) {
+            return 1;
+        } else {
+            END();
+            sync_quit(fd);
+            return 0;
+        }
+    } else {
+        fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
+        return 1;
+    }
+}
+
+int do_sync_sync(const char *lpath, const char *rpath)
+{
+    fprintf(stderr,"syncing %s...\n",rpath);
+
+    int fd = adb_connect("sync:");
+    if(fd < 0) {
+        fprintf(stderr,"error: %s\n", adb_error());
+        return 1;
+    }
+
+    BEGIN();
+    if(copy_local_dir_remote(fd, lpath, rpath, 1)){
+        return 1;
+    } else {
+        END();
+        sync_quit(fd);
+        return 0;
+    }
+}
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c
new file mode 100644
index 0000000..a231e93
--- /dev/null
+++ b/adb/file_sync_service.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <utime.h>
+
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG  TRACE_SYNC
+#include "adb.h"
+#include "file_sync_service.h"
+
+static int mkdirs(char *name)
+{
+    int ret;
+    char *x = name + 1;
+
+    if(name[0] != '/') return -1;
+
+    for(;;) {
+        x = adb_dirstart(x);
+        if(x == 0) return 0;
+        *x = 0;
+        ret = adb_mkdir(name, 0775);
+        if((ret < 0) && (errno != EEXIST)) {
+            D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
+            *x = '/';
+            return ret;
+        }
+        *x++ = '/';
+    }
+    return 0;
+}
+
+static int do_stat(int s, const char *path)
+{
+    syncmsg msg;
+    struct stat st;
+
+    msg.stat.id = ID_STAT;
+
+    if(lstat(path, &st)) {
+        msg.stat.mode = 0;
+        msg.stat.size = 0;
+        msg.stat.time = 0;
+    } else {
+        msg.stat.mode = htoll(st.st_mode);
+        msg.stat.size = htoll(st.st_size);
+        msg.stat.time = htoll(st.st_mtime);
+    }
+
+    return writex(s, &msg.stat, sizeof(msg.stat));
+}
+
+static int do_list(int s, const char *path)
+{
+    DIR *d;
+    struct dirent *de;
+    struct stat st;
+    syncmsg msg;
+    int len;
+
+    char tmp[1024 + 256 + 1];
+    char *fname;
+
+    len = strlen(path);
+    memcpy(tmp, path, len);
+    tmp[len] = '/';
+    fname = tmp + len + 1;
+
+    msg.dent.id = ID_DENT;
+
+    d = opendir(path);
+    if(d == 0) goto done;
+
+    while((de = readdir(d))) {
+        int len = strlen(de->d_name);
+
+            /* not supposed to be possible, but
+               if it does happen, let's not buffer overrun */
+        if(len > 256) continue;
+
+        strcpy(fname, de->d_name);
+        if(lstat(tmp, &st) == 0) {
+            msg.dent.mode = htoll(st.st_mode);
+            msg.dent.size = htoll(st.st_size);
+            msg.dent.time = htoll(st.st_mtime);
+            msg.dent.namelen = htoll(len);
+
+            if(writex(s, &msg.dent, sizeof(msg.dent)) ||
+               writex(s, de->d_name, len)) {
+                return -1;
+            }
+        }
+    }
+
+    closedir(d);
+
+done:
+    msg.dent.id = ID_DONE;
+    msg.dent.mode = 0;
+    msg.dent.size = 0;
+    msg.dent.time = 0;
+    msg.dent.namelen = 0;
+    return writex(s, &msg.dent, sizeof(msg.dent));
+}
+
+static int fail_message(int s, const char *reason)
+{
+    syncmsg msg;
+    int len = strlen(reason);
+
+    D("sync: failure: %s\n", reason);
+
+    msg.data.id = ID_FAIL;
+    msg.data.size = htoll(len);
+    if(writex(s, &msg.data, sizeof(msg.data)) ||
+       writex(s, reason, len)) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int fail_errno(int s)
+{
+    return fail_message(s, strerror(errno));
+}
+
+static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
+{
+    syncmsg msg;
+    unsigned int timestamp = 0;
+    int fd;
+
+    fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+    if(fd < 0 && errno == ENOENT) {
+        mkdirs(path);
+        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+    }
+    if(fd < 0 && errno == EEXIST) {
+        fd = adb_open_mode(path, O_WRONLY, mode);
+    }
+    if(fd < 0) {
+        if(fail_errno(s))
+            return -1;
+        fd = -1;
+    }
+
+    for(;;) {
+        unsigned int len;
+
+        if(readx(s, &msg.data, sizeof(msg.data)))
+            goto fail;
+
+        if(msg.data.id != ID_DATA) {
+            if(msg.data.id == ID_DONE) {
+                timestamp = ltohl(msg.data.size);
+                break;
+            }
+            fail_message(s, "invalid data message");
+            goto fail;
+        }
+        len = ltohl(msg.data.size);
+        if(len > SYNC_DATA_MAX) {
+            fail_message(s, "oversize data message");
+            goto fail;
+        }
+        if(readx(s, buffer, len))
+            goto fail;
+
+        if(fd < 0)
+            continue;
+        if(writex(fd, buffer, len)) {
+            adb_close(fd);
+            adb_unlink(path);
+            fd = -1;
+            if(fail_errno(s)) return -1;
+        }
+    }
+
+    if(fd >= 0) {
+        struct utimbuf u;
+        adb_close(fd);
+        u.actime = timestamp;
+        u.modtime = timestamp;
+        utime(path, &u);
+
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        if(writex(s, &msg.status, sizeof(msg.status)))
+            return -1;
+    }
+    return 0;
+
+fail:
+    if(fd >= 0)
+        adb_close(fd);
+    adb_unlink(path);
+    return -1;
+}
+
+#ifdef HAVE_SYMLINKS
+static int handle_send_link(int s, char *path, char *buffer)
+{
+    syncmsg msg;
+    unsigned int len;
+    int ret;
+
+    if(readx(s, &msg.data, sizeof(msg.data)))
+        return -1;
+
+    if(msg.data.id != ID_DATA) {
+        fail_message(s, "invalid data message: expected ID_DATA");
+        return -1;
+    }
+
+    len = ltohl(msg.data.size);
+    if(len > SYNC_DATA_MAX) {
+        fail_message(s, "oversize data message");
+        return -1;
+    }
+    if(readx(s, buffer, len))
+        return -1;
+
+    ret = symlink(buffer, path);
+    if(ret && errno == ENOENT) {
+        mkdirs(path);
+        ret = symlink(buffer, path);
+    }
+    if(ret) {
+        fail_errno(s);
+        return -1;
+    }
+
+    if(readx(s, &msg.data, sizeof(msg.data)))
+        return -1;
+
+    if(msg.data.id == ID_DONE) {
+        msg.status.id = ID_OKAY;
+        msg.status.msglen = 0;
+        if(writex(s, &msg.status, sizeof(msg.status)))
+            return -1;
+    } else {
+        fail_message(s, "invalid data message: expected ID_DONE");
+        return -1;
+    }
+
+    return 0;
+}
+#endif /* HAVE_SYMLINKS */
+
+static int do_send(int s, char *path, char *buffer)
+{
+    char *tmp;
+    mode_t mode;
+    int is_link, ret;
+
+    tmp = strrchr(path,',');
+    if(tmp) {
+        *tmp = 0;
+        errno = 0;
+        mode = strtoul(tmp + 1, NULL, 0);
+#ifndef HAVE_SYMLINKS
+        is_link = 0;
+#else
+        is_link = S_ISLNK(mode);
+#endif
+        mode &= 0777;
+    }
+    if(!tmp || errno) {
+        mode = 0644;
+        is_link = 0;
+    }
+
+    adb_unlink(path);
+
+
+#ifdef HAVE_SYMLINKS
+    if(is_link)
+        ret = handle_send_link(s, path, buffer);
+    else {
+#else
+    {
+#endif
+        /* copy user permission bits to "group" and "other" permissions */
+        mode |= ((mode >> 3) & 0070);
+        mode |= ((mode >> 3) & 0007);
+
+        ret = handle_send_file(s, path, mode, buffer);
+    }
+
+    return ret;
+}
+
+static int do_recv(int s, const char *path, char *buffer)
+{
+    syncmsg msg;
+    int fd, r;
+
+    fd = adb_open(path, O_RDONLY);
+    if(fd < 0) {
+        if(fail_errno(s)) return -1;
+        return 0;
+    }
+
+    msg.data.id = ID_DATA;
+    for(;;) {
+        r = adb_read(fd, buffer, SYNC_DATA_MAX);
+        if(r <= 0) {
+            if(r == 0) break;
+            if(errno == EINTR) continue;
+            r = fail_errno(s);
+            adb_close(fd);
+            return r;
+        }
+        msg.data.size = htoll(r);
+        if(writex(s, &msg.data, sizeof(msg.data)) ||
+           writex(s, buffer, r)) {
+            adb_close(fd);
+            return -1;
+        }
+    }
+
+    adb_close(fd);
+
+    msg.data.id = ID_DONE;
+    msg.data.size = 0;
+    if(writex(s, &msg.data, sizeof(msg.data))) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void file_sync_service(int fd, void *cookie)
+{
+    syncmsg msg;
+    char name[1025];
+    unsigned namelen;
+
+    char *buffer = malloc(SYNC_DATA_MAX);
+    if(buffer == 0) goto fail;
+
+    for(;;) {
+        D("sync: waiting for command\n");
+
+        if(readx(fd, &msg.req, sizeof(msg.req))) {
+            fail_message(fd, "command read failure");
+            break;
+        }
+        namelen = ltohl(msg.req.namelen);
+        if(namelen > 1024) {
+            fail_message(fd, "invalid namelen");
+            break;
+        }
+        if(readx(fd, name, namelen)) {
+            fail_message(fd, "filename read failure");
+            break;
+        }
+        name[namelen] = 0;
+
+        msg.req.namelen = 0;
+        D("sync: '%s' '%s'\n", (char*) &msg.req, name);
+
+        switch(msg.req.id) {
+        case ID_STAT:
+            if(do_stat(fd, name)) goto fail;
+            break;
+        case ID_LIST:
+            if(do_list(fd, name)) goto fail;
+            break;
+        case ID_SEND:
+            if(do_send(fd, name, buffer)) goto fail;
+            break;
+        case ID_RECV:
+            if(do_recv(fd, name, buffer)) goto fail;
+            break;
+        case ID_QUIT:
+            goto fail;
+        default:
+            fail_message(fd, "unknown command");
+            goto fail;
+        }
+    }
+
+fail:
+    if(buffer != 0) free(buffer);
+    D("sync: done\n");
+    adb_close(fd);
+}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
new file mode 100644
index 0000000..4ee40ba
--- /dev/null
+++ b/adb/file_sync_service.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FILE_SYNC_SERVICE_H_
+#define _FILE_SYNC_SERVICE_H_
+
+#ifdef __ppc__
+static inline unsigned __swap_uint32(unsigned x) 
+{
+    return (((x) & 0xFF000000) >> 24)
+        | (((x) & 0x00FF0000) >> 8)
+        | (((x) & 0x0000FF00) << 8)
+        | (((x) & 0x000000FF) << 24);
+}
+#define htoll(x) __swap_uint32(x)
+#define ltohl(x) __swap_uint32(x)
+#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))
+#else
+#define htoll(x) (x)
+#define ltohl(x) (x)
+#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
+#endif
+
+#define ID_STAT MKID('S','T','A','T')
+#define ID_LIST MKID('L','I','S','T')
+#define ID_ULNK MKID('U','L','N','K')
+#define ID_SEND MKID('S','E','N','D')
+#define ID_RECV MKID('R','E','C','V')
+#define ID_DENT MKID('D','E','N','T')
+#define ID_DONE MKID('D','O','N','E')
+#define ID_DATA MKID('D','A','T','A')
+#define ID_OKAY MKID('O','K','A','Y')
+#define ID_FAIL MKID('F','A','I','L')
+#define ID_QUIT MKID('Q','U','I','T')
+
+typedef union {
+    unsigned id;
+    struct {
+        unsigned id;
+        unsigned namelen;
+    } req;
+    struct {
+        unsigned id;
+        unsigned mode;
+        unsigned size;
+        unsigned time;
+    } stat;
+    struct {
+        unsigned id;
+        unsigned mode;
+        unsigned size;
+        unsigned time;
+        unsigned namelen;
+    } dent;
+    struct {
+        unsigned id;
+        unsigned size;
+    } data;
+    struct {
+        unsigned id;
+        unsigned msglen;
+    } status;    
+} syncmsg;
+
+
+void file_sync_service(int fd, void *cookie);
+int do_sync_ls(const char *path);
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
+int do_sync_sync(const char *lpath, const char *rpath);
+int do_sync_pull(const char *rpath, const char *lpath);
+
+#define SYNC_DATA_MAX (64*1024)
+
+#endif
diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c
new file mode 100644
index 0000000..0de0dd5
--- /dev/null
+++ b/adb/framebuffer_service.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <cutils/fdevent.h>
+#include "adb.h"
+
+#include <linux/fb.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+/* TODO:
+** - grab the current buffer, not the first buffer
+** - sync with vsync to avoid tearing
+*/
+
+void framebuffer_service(int fd, void *cookie)
+{
+    struct fb_var_screeninfo vinfo;
+    int fb;
+    void *ptr = MAP_FAILED;
+    char x;
+
+    unsigned fbinfo[4];
+
+    fb = open("/dev/graphics/fb0", O_RDONLY);
+    if(fb < 0) goto done;
+
+    if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
+    fcntl(fb, F_SETFD, FD_CLOEXEC);
+
+    fbinfo[0] = 16;
+    fbinfo[1] = vinfo.xres * vinfo.yres * 2;
+    fbinfo[2] = vinfo.xres;
+    fbinfo[3] = vinfo.yres;
+
+    ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0);
+    if(ptr == MAP_FAILED) goto done;
+
+    if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done;
+
+    for(;;) {
+        if(readx(fd, &x, 1)) goto done;
+        if(writex(fd, ptr, fbinfo[1])) goto done;
+    }
+
+done:
+    if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]);
+    if(fb >= 0) close(fb);
+    close(fd);
+}
+
diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c
new file mode 100644
index 0000000..00dfee4
--- /dev/null
+++ b/adb/get_my_path_darwin.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/executablepath.h>
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void get_my_path(char s[PATH_MAX])
+{
+    ProcessSerialNumber psn;
+    GetCurrentProcess(&psn);
+    CFDictionaryRef dict;
+    dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+    CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
+                CFSTR("CFBundleExecutable"));
+    CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
+}
+
diff --git a/adb/get_my_path_linux.c b/adb/get_my_path_linux.c
new file mode 100644
index 0000000..f516e59
--- /dev/null
+++ b/adb/get_my_path_linux.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+
+void get_my_path(char exe[PATH_MAX])
+{
+    char proc[64];
+    snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
+    int err = readlink(proc, exe, PATH_MAX - 1);
+    if(err > 0) {
+        exe[err] = 0;
+    } else {
+        exe[0] = 0;
+    }
+}
+
diff --git a/adb/get_my_path_windows.c b/adb/get_my_path_windows.c
new file mode 100644
index 0000000..fc7143c
--- /dev/null
+++ b/adb/get_my_path_windows.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+#include <assert.h>
+#include <windows.h>
+
+void get_my_path(char exe[PATH_MAX])
+{
+    char*  r;
+
+    GetModuleFileName( NULL, exe, PATH_MAX-1 );
+    exe[PATH_MAX-1] = 0;
+    r = strrchr( exe, '\\' );
+    if (r)
+        *r = 0;
+}
+
diff --git a/adb/history.h b/adb/history.h
new file mode 100755
index 0000000..ef86ad9
--- /dev/null
+++ b/adb/history.h
@@ -0,0 +1,13 @@
+#ifndef _HISTORY_H_

+#define _HISTORY_H_

+

+#define SH_ARROW_ANY    "\x1b\x5b"

+#define SH_ARROW_UP     '\x41'

+#define SH_ARROW_DOWN   '\x42'

+#define SH_ARROW_RIGHT  '\x43'

+#define SH_ARROW_LEFT   '\x44'

+#define SH_DEL_CHAR     '\x7F'

+#define SH_BLANK_CHAR   '\x20'

+

+#endif

+

diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c
new file mode 100644
index 0000000..ae7f12d
--- /dev/null
+++ b/adb/jdwp_service.c
@@ -0,0 +1,709 @@
+/* implement the "debug-ports" and "track-debug-ports" device services */
+#include "sysdeps.h"
+#define  TRACE_TAG   TRACE_JDWP
+#include "adb.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* here's how these things work.
+
+   when adbd starts, it creates a unix server socket
+   named @vm-debug-control (@ is a shortcut for "first byte is zero"
+   to use the private namespace instead of the file system)
+
+   when a new JDWP daemon thread starts in a new VM process, it creates
+   a connection to @vm-debug-control to announce its availability.
+
+
+     JDWP thread                             @vm-debug-control
+         |                                         |
+         |------------------------------->         |
+         | hello I'm in process <pid>              |
+         |                                         |
+         |                                         |
+
+    the connection is kept alive. it will be closed automatically if
+    the JDWP process terminates (this allows adbd to detect dead
+    processes).
+
+    adbd thus maintains a list of "active" JDWP processes. it can send
+    its content to clients through the "device:debug-ports" service,
+    or even updates through the "device:track-debug-ports" service.
+
+    when a debugger wants to connect, it simply runs the command
+    equivalent to  "adb forward tcp:<hostport> jdwp:<pid>"
+
+    "jdwp:<pid>" is a new forward destination format used to target
+    a given JDWP process on the device. when sutch a request arrives,
+    adbd does the following:
+
+      - first, it calls socketpair() to create a pair of equivalent
+        sockets.
+
+      - it attaches the first socket in the pair to a local socket
+        which is itself attached to the transport's remote socket:
+
+
+      - it sends the file descriptor of the second socket directly
+        to the JDWP process with the help of sendmsg()
+
+
+     JDWP thread                             @vm-debug-control
+         |                                         |
+         |                  <----------------------|
+         |           OK, try this file descriptor  |
+         |                                         |
+         |                                         |
+
+   then, the JDWP thread uses this new socket descriptor as its
+   pass-through connection to the debugger (and receives the
+   JDWP-Handshake message, answers to it, etc...)
+
+   this gives the following graphics:
+                    ____________________________________
+                   |                                    |
+                   |          ADB Server (host)         |
+                   |                                    |
+        Debugger <---> LocalSocket <----> RemoteSocket  |
+                   |                           ^^       |
+                   |___________________________||_______|
+                                               ||
+                                     Transport ||
+           (TCP for emulator - USB for device) ||
+                                               ||
+                    ___________________________||_______
+                   |                           ||       |
+                   |          ADBD  (device)   ||       |
+                   |                           VV       |
+         JDWP <======> LocalSocket <----> RemoteSocket  |
+                   |                                    |
+                   |____________________________________|
+
+    due to the way adb works, this doesn't need a special socket
+    type or fancy handling of socket termination if either the debugger
+    or the JDWP process closes the connection.
+
+    THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
+    TO HAVE A BETTER IDEA, LET ME KNOW - Digit
+
+**********************************************************************/
+
+/** JDWP PID List Support Code
+ ** for each JDWP process, we record its pid and its connected socket
+ **/
+
+#define  MAX_OUT_FDS   4
+
+#if !ADB_HOST
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+typedef struct JdwpProcess  JdwpProcess;
+struct JdwpProcess {
+    JdwpProcess*  next;
+    JdwpProcess*  prev;
+    int           pid;
+    int           socket;
+    fdevent*      fde;
+
+    char          in_buff[4];  /* input character to read PID */
+    int           in_len;      /* number from JDWP process    */
+
+    int           out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
+    int           out_count;            /* to send to the JDWP process      */
+};
+
+static JdwpProcess  _jdwp_list;
+
+static int
+jdwp_process_list( char*  buffer, int  bufferlen )
+{
+    char*         end  = buffer + bufferlen;
+    char*         p    = buffer;
+    JdwpProcess*  proc = _jdwp_list.next;
+
+    for ( ; proc != &_jdwp_list; proc = proc->next ) {
+        int  len;
+
+        /* skip transient connections */
+        if (proc->pid < 0)
+            continue;
+
+        len = snprintf(p, end-p, "%d\n", proc->pid);
+        if (p + len >= end)
+            break;
+        p += len;
+    }
+    p[0] = 0;
+    return (p - buffer);
+}
+
+
+static int
+jdwp_process_list_msg( char*  buffer, int  bufferlen )
+{
+    char  head[5];
+    int   len = jdwp_process_list( buffer+4, bufferlen-4 );
+    snprintf(head, sizeof head, "%04x", len);
+    memcpy(buffer, head, 4);
+    return len + 4;
+}
+
+
+static void  jdwp_process_list_updated(void);
+
+static void
+jdwp_process_free( JdwpProcess*  proc )
+{
+    if (proc) {
+        int  n;
+
+        proc->prev->next = proc->next;
+        proc->next->prev = proc->prev;
+
+        if (proc->socket >= 0) {
+            shutdown(proc->socket, SHUT_RDWR);
+            adb_close(proc->socket);
+            proc->socket = -1;
+        }
+
+        if (proc->fde != NULL) {
+            fdevent_destroy(proc->fde);
+            proc->fde = NULL;
+        }
+        proc->pid = -1;
+
+        for (n = 0; n < proc->out_count; n++) {
+            adb_close(proc->out_fds[n]);
+        }
+        proc->out_count = 0;
+
+        free(proc);
+
+        jdwp_process_list_updated();
+    }
+}
+
+
+static void  jdwp_process_event(int, unsigned, void*);  /* forward */
+
+
+static JdwpProcess*
+jdwp_process_alloc( int  socket )
+{
+    JdwpProcess*  proc = calloc(1,sizeof(*proc));
+
+    if (proc == NULL) {
+        D("not enough memory to create new JDWP process\n");
+        return NULL;
+    }
+
+    proc->socket = socket;
+    proc->pid    = -1;
+    proc->next   = proc;
+    proc->prev   = proc;
+
+    proc->fde = fdevent_create( socket, jdwp_process_event, proc );
+    if (proc->fde == NULL) {
+        D("could not create fdevent for new JDWP process\n" );
+        free(proc);
+        return NULL;
+    }
+
+    proc->fde->state |= FDE_DONT_CLOSE;
+    proc->in_len      = 0;
+    proc->out_count   = 0;
+
+    /* append to list */
+    proc->next = &_jdwp_list;
+    proc->prev = proc->next->prev;
+
+    proc->prev->next = proc;
+    proc->next->prev = proc;
+
+    /* start by waiting for the PID */
+    fdevent_add(proc->fde, FDE_READ);
+
+    return proc;
+}
+
+
+static void
+jdwp_process_event( int  socket, unsigned  events, void*  _proc )
+{
+    JdwpProcess*  proc = _proc;
+
+    if (events & FDE_READ) {
+        if (proc->pid < 0) {
+            /* read the PID as a 4-hexchar string */
+            char*  p    = proc->in_buff + proc->in_len;
+            int    size = 4 - proc->in_len;
+            char   temp[5];
+            while (size > 0) {
+                int  len = recv( socket, p, size, 0 );
+                if (len < 0) {
+                    if (errno == EINTR)
+                        continue;
+                    if (errno == EAGAIN)
+                        return;
+                    /* this can fail here if the JDWP process crashes very fast */
+                    D("weird unknown JDWP process failure: %s\n",
+                      strerror(errno));
+
+                    goto CloseProcess;
+                }
+                if (len == 0) {  /* end of stream ? */
+                    D("weird end-of-stream from unknown JDWP process\n");
+                    goto CloseProcess;
+                }
+                p            += len;
+                proc->in_len += len;
+                size         -= len;
+            }
+            /* we have read 4 characters, now decode the pid */
+            memcpy(temp, proc->in_buff, 4);
+            temp[4] = 0;
+
+            if (sscanf( temp, "%04x", &proc->pid ) != 1) {
+                D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+                goto CloseProcess;
+            }
+
+            /* all is well, keep reading to detect connection closure */
+            D("Adding pid %d to jdwp process list\n", proc->pid);
+            jdwp_process_list_updated();
+        }
+        else
+        {
+            /* the pid was read, if we get there it's probably because the connection
+             * was closed (e.g. the JDWP process exited or crashed) */
+            char  buf[32];
+
+            for (;;) {
+                int  len = recv(socket, buf, sizeof(buf), 0);
+
+                if (len <= 0) {
+                    if (len < 0 && errno == EINTR)
+                        continue;
+                    if (len < 0 && errno == EAGAIN)
+                        return;
+                    else {
+                        D("terminating JDWP %d connection: %s\n", proc->pid,
+                          strerror(errno));
+                        break;
+                    }
+                }
+                else {
+                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
+                       proc->pid, len );
+                }
+            }
+
+        CloseProcess:
+            if (proc->pid >= 0)
+                D( "remove pid %d to jdwp process list\n", proc->pid );
+            jdwp_process_free(proc);
+            return;
+        }
+    }
+
+    if (events & FDE_WRITE) {
+        D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
+          proc->pid, proc->out_count, proc->out_fds[0]);
+        if (proc->out_count > 0) {
+            int  fd = proc->out_fds[0];
+            int  n, ret;
+            struct cmsghdr*  cmsg;
+            struct msghdr    msg;
+            struct iovec     iov;
+            char             dummy = '!';
+            char             buffer[sizeof(struct cmsghdr) + sizeof(int)];
+
+            iov.iov_base       = &dummy;
+            iov.iov_len        = 1;
+            msg.msg_name       = NULL;
+            msg.msg_namelen    = 0;
+            msg.msg_iov        = &iov;
+            msg.msg_iovlen     = 1;
+            msg.msg_flags      = 0;
+            msg.msg_control    = buffer;
+            msg.msg_controllen = sizeof(buffer);
+
+            cmsg = CMSG_FIRSTHDR(&msg);
+            cmsg->cmsg_len   = msg.msg_controllen;
+            cmsg->cmsg_level = SOL_SOCKET;
+            cmsg->cmsg_type  = SCM_RIGHTS;
+            ((int*)CMSG_DATA(cmsg))[0] = fd;
+
+            for (;;) {
+                ret = sendmsg(proc->socket, &msg, 0);
+                if (ret >= 0)
+                    break;
+                if (errno == EINTR)
+                    continue;
+                D("sending new file descriptor to JDWP %d failed: %s\n",
+                  proc->pid, strerror(errno));
+                goto CloseProcess;
+            }
+
+            D("sent file descriptor %d to JDWP process %d\n",
+              fd, proc->pid);
+
+            for (n = 1; n < proc->out_count; n++)
+                proc->out_fds[n-1] = proc->out_fds[n];
+
+            if (--proc->out_count == 0)
+                fdevent_del( proc->fde, FDE_WRITE );
+        }
+    }
+}
+
+
+int
+create_jdwp_connection_fd(int  pid)
+{
+    JdwpProcess*  proc = _jdwp_list.next;
+
+    D("looking for pid %d in JDWP process list\n", pid);
+    for ( ; proc != &_jdwp_list; proc = proc->next ) {
+        if (proc->pid == pid) {
+            goto FoundIt;
+        }
+    }
+    D("search failed !!\n");
+    return -1;
+
+FoundIt:
+    {
+        int  fds[2];
+
+        if (proc->out_count >= MAX_OUT_FDS) {
+            D("%s: too many pending JDWP connection for pid %d\n",
+              __FUNCTION__, pid);
+            return -1;
+        }
+
+        if (adb_socketpair(fds) < 0) {
+            D("%s: socket pair creation failed: %s\n",
+              __FUNCTION__, strerror(errno));
+            return -1;
+        }
+
+        proc->out_fds[ proc->out_count ] = fds[1];
+        if (++proc->out_count == 1)
+            fdevent_add( proc->fde, FDE_WRITE );
+
+        return fds[0];
+    }
+}
+
+/**  VM DEBUG CONTROL SOCKET
+ **
+ **  we do implement a custom asocket to receive the data
+ **/
+
+/* name of the debug control Unix socket */
+#define  JDWP_CONTROL_NAME      "\0jdwp-control"
+#define  JDWP_CONTROL_NAME_LEN  (sizeof(JDWP_CONTROL_NAME)-1)
+
+typedef struct {
+    int       listen_socket;
+    fdevent*  fde;
+
+} JdwpControl;
+
+
+static void
+jdwp_control_event(int  s, unsigned events, void*  user);
+
+
+static int
+jdwp_control_init( JdwpControl*  control,
+                   const char*   sockname,
+                   int           socknamelen )
+{
+    struct sockaddr_un   addr;
+    socklen_t            addrlen;
+    int                  s;
+    int                  maxpath = sizeof(addr.sun_path);
+    int                  pathlen = socknamelen;
+
+    if (pathlen >= maxpath) {
+        D( "vm debug control socket name too long (%d extra chars)\n",
+           pathlen+1-maxpath );
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    memcpy(addr.sun_path, sockname, socknamelen);
+
+    s = socket( AF_UNIX, SOCK_STREAM, 0 );
+    if (s < 0) {
+        D( "could not create vm debug control socket. %d: %s\n",
+           errno, strerror(errno));
+        return -1;
+    }
+
+    addrlen = (pathlen + sizeof(addr.sun_family));
+
+    if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
+        D( "could not bind vm debug control socket: %d: %s\n",
+           errno, strerror(errno) );
+        adb_close(s);
+        return -1;
+    }
+
+    if ( listen(s, 4) < 0 ) {
+        D("listen failed in jdwp control socket: %d: %s\n",
+          errno, strerror(errno));
+        adb_close(s);
+        return -1;
+    }
+
+    control->listen_socket = s;
+
+    control->fde = fdevent_create(s, jdwp_control_event, control);
+    if (control->fde == NULL) {
+        D( "could not create fdevent for jdwp control socket\n" );
+        adb_close(s);
+        return -1;
+    }
+
+    /* only wait for incoming connections */
+    fdevent_add(control->fde, FDE_READ);
+
+    D("jdwp control socket started (%d)\n", control->listen_socket);
+    return 0;
+}
+
+
+static void
+jdwp_control_event( int  s, unsigned  events, void*  _control )
+{
+    JdwpControl*  control = (JdwpControl*) _control;
+
+    if (events & FDE_READ) {
+        struct sockaddr   addr;
+        socklen_t         addrlen = sizeof(addr);
+        int               s = -1;
+        JdwpProcess*      proc;
+
+        do {
+            s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
+            if (s < 0) {
+                if (errno == EINTR)
+                    continue;
+                if (errno == ECONNABORTED) {
+                    /* oops, the JDWP process died really quick */
+                    D("oops, the JDWP process died really quick\n");
+                    return;
+                }
+                /* the socket is probably closed ? */
+                D( "weird accept() failed on jdwp control socket: %s\n",
+                   strerror(errno) );
+                return;
+            }
+        }
+        while (s < 0);
+
+        proc = jdwp_process_alloc( s );
+        if (proc == NULL)
+            return;
+    }
+}
+
+
+static JdwpControl   _jdwp_control;
+
+/** "jdwp" local service implementation
+ ** this simply returns the list of known JDWP process pids
+ **/
+
+typedef struct {
+    asocket  socket;
+    int      pass;
+} JdwpSocket;
+
+static void
+jdwp_socket_close( asocket*  s )
+{
+    asocket*  peer = s->peer;
+
+    remove_socket(s);
+
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    free(s);
+}
+
+static int
+jdwp_socket_enqueue( asocket*  s, apacket*  p )
+{
+    /* you can't write to this asocket */
+    put_apacket(p);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+
+static void
+jdwp_socket_ready( asocket*  s )
+{
+    JdwpSocket*  jdwp = (JdwpSocket*)s;
+    asocket*     peer = jdwp->socket.peer;
+
+   /* on the first call, send the list of pids,
+    * on the second one, close the connection
+    */
+    if (jdwp->pass == 0) {
+        apacket*  p = get_apacket();
+        p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
+        peer->enqueue(peer, p);
+        jdwp->pass = 1;
+    }
+    else {
+        peer->close(peer);
+    }
+}
+
+asocket*
+create_jdwp_service_socket( void )
+{
+    JdwpSocket*  s = calloc(sizeof(*s),1);
+
+    if (s == NULL)
+        return NULL;
+
+    install_local_socket(&s->socket);
+
+    s->socket.ready   = jdwp_socket_ready;
+    s->socket.enqueue = jdwp_socket_enqueue;
+    s->socket.close   = jdwp_socket_close;
+    s->pass           = 0;
+
+    return &s->socket;
+}
+
+/** "track-jdwp" local service implementation
+ ** this periodically sends the list of known JDWP process pids
+ ** to the client...
+ **/
+
+typedef struct JdwpTracker  JdwpTracker;
+
+struct JdwpTracker {
+    asocket       socket;
+    JdwpTracker*  next;
+    JdwpTracker*  prev;
+    int           need_update;
+};
+
+static JdwpTracker   _jdwp_trackers_list;
+
+
+static void
+jdwp_process_list_updated(void)
+{
+    char             buffer[1024];
+    int              len;
+    JdwpTracker*  t = _jdwp_trackers_list.next;
+
+    len = jdwp_process_list_msg(buffer, sizeof(buffer));
+
+    for ( ; t != &_jdwp_trackers_list; t = t->next ) {
+        apacket*  p    = get_apacket();
+        asocket*  peer = t->socket.peer;
+        memcpy(p->data, buffer, len);
+        p->len = len;
+        peer->enqueue( peer, p );
+    }
+}
+
+static void
+jdwp_tracker_close( asocket*  s )
+{
+    JdwpTracker*  tracker = (JdwpTracker*) s;
+    asocket*      peer    = s->peer;
+
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+
+    remove_socket(s);
+
+    tracker->prev->next = tracker->next;
+    tracker->next->prev = tracker->prev;
+
+    free(s);
+}
+
+static void
+jdwp_tracker_ready( asocket*  s )
+{
+    JdwpTracker*  t = (JdwpTracker*) s;
+
+    if (t->need_update) {
+        apacket*  p = get_apacket();
+        t->need_update = 0;
+        p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
+        s->peer->enqueue(s->peer, p);
+    }
+}
+
+static int
+jdwp_tracker_enqueue( asocket*  s, apacket*  p )
+{
+    /* you can't write to this socket */
+    put_apacket(p);
+    s->peer->close(s->peer);
+    return -1;
+}
+
+
+asocket*
+create_jdwp_tracker_service_socket( void )
+{
+    JdwpTracker*  t = calloc(sizeof(*t),1);
+
+    if (t == NULL)
+        return NULL;
+
+    t->next = &_jdwp_trackers_list;
+    t->prev = t->next->prev;
+
+    t->next->prev = t;
+    t->prev->next = t;
+
+    install_local_socket(&t->socket);
+
+    t->socket.ready   = jdwp_tracker_ready;
+    t->socket.enqueue = jdwp_tracker_enqueue;
+    t->socket.close   = jdwp_tracker_close;
+    t->need_update    = 1;
+
+    return &t->socket;
+}
+
+
+int
+init_jdwp(void)
+{
+    _jdwp_list.next = &_jdwp_list;
+    _jdwp_list.prev = &_jdwp_list;
+
+    _jdwp_trackers_list.next = &_jdwp_trackers_list;
+    _jdwp_trackers_list.prev = &_jdwp_trackers_list;
+
+    return jdwp_control_init( &_jdwp_control,
+                              JDWP_CONTROL_NAME,
+                              JDWP_CONTROL_NAME_LEN );
+}
+
+#endif /* !ADB_HOST */
+
diff --git a/adb/log_service.c b/adb/log_service.c
new file mode 100644
index 0000000..6e9bdee
--- /dev/null
+++ b/adb/log_service.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <cutils/logger.h>
+#include "sysdeps.h"
+#include "adb.h"
+
+#define LOG_FILE_DIR    "/dev/log/"
+
+void write_log_entry(int fd, struct logger_entry *buf);
+
+void log_service(int fd, void *cookie)
+{
+    /* get the name of the log filepath to read */
+    char * log_filepath = cookie;
+
+    /* open the log file. */
+    int logfd = unix_open(log_filepath, O_RDONLY);
+    if (logfd < 0) {
+        goto done;
+    }
+
+    // temp buffer to read the entries
+    unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
+    struct logger_entry *entry = (struct logger_entry *) buf;
+
+    while (1) {
+        int ret;
+
+        ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
+        if (ret < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            // perror("logcat read");
+            goto done;
+        }
+        else if (!ret) {
+            // fprintf(stderr, "read: Unexpected EOF!\n");
+            goto done;
+        }
+
+        /* NOTE: driver guarantees we read exactly one full entry */
+
+        entry->msg[entry->len] = '\0';
+
+        write_log_entry(fd, entry);
+    }
+
+done:
+    unix_close(fd);
+    free(log_filepath);
+}
+
+/* returns the full path to the log file in a newly allocated string */
+char * get_log_file_path(const char * log_name) {
+    char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1);
+
+    strcpy(log_device, LOG_FILE_DIR);
+    strcat(log_device, log_name);
+
+    return log_device;
+}
+
+
+/* prints one log entry into the file descriptor fd */
+void write_log_entry(int fd, struct logger_entry *buf)
+{
+    size_t size = sizeof(struct logger_entry) + buf->len;
+
+    writex(fd, buf, size);
+}
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
new file mode 100644
index 0000000..eebe0df
--- /dev/null
+++ b/adb/mutex_list.h
@@ -0,0 +1,14 @@
+/* the list of mutexes used by addb */
+#ifndef ADB_MUTEX
+#error ADB_MUTEX not defined when including this file
+#endif
+
+ADB_MUTEX(dns_lock)
+ADB_MUTEX(socket_list_lock)
+ADB_MUTEX(transport_lock)
+#if ADB_HOST
+ADB_MUTEX(local_transports_lock)
+#endif
+ADB_MUTEX(usb_lock)
+
+#undef ADB_MUTEX
diff --git a/adb/protocol.txt b/adb/protocol.txt
new file mode 100644
index 0000000..d0f307c
--- /dev/null
+++ b/adb/protocol.txt
@@ -0,0 +1,252 @@
+
+--- a replacement for aproto -------------------------------------------
+
+When it comes down to it, aproto's primary purpose is to forward
+various streams between the host computer and client device (in either
+direction).
+
+This replacement further simplifies the concept, reducing the protocol
+to an extremely straightforward model optimized to accomplish the
+forwarding of these streams and removing additional state or
+complexity.
+
+The host side becomes a simple comms bridge with no "UI", which will 
+be used by either commandline or interactive tools to communicate with 
+a device or emulator that is connected to the bridge.
+
+The protocol is designed to be straightforward and well-defined enough 
+that if it needs to be reimplemented in another environment (Java 
+perhaps), there should not problems ensuring perfect interoperability.
+
+The protocol discards the layering aproto has and should allow the 
+implementation to be much more robust.
+
+
+--- protocol overview and basics ---------------------------------------
+ 
+The transport layer deals in "messages", which consist of a 24 byte
+header followed (optionally) by a payload.  The header consists of 6
+32 bit words which are sent across the wire in little endian format.
+
+struct message {
+    unsigned command;       /* command identifier constant      */
+    unsigned arg0;          /* first argument                   */
+    unsigned arg1;          /* second argument                  */
+    unsigned data_length;   /* length of payload (0 is allowed) */
+    unsigned data_crc32;    /* crc32 of data payload            */
+    unsigned magic;         /* command ^ 0xffffffff             */
+};
+
+Receipt of an invalid message header, corrupt message payload, or an
+unrecognized command MUST result in the closing of the remote
+connection.  The protocol depends on shared state and any break in the
+message stream will result in state getting out of sync.
+
+The following sections describe the six defined message types in
+detail.  Their format is COMMAND(arg0, arg1, payload) where the payload
+is represented by a quoted string or an empty string if none should be
+sent.
+
+The identifiers "local-id" and "remote-id" are always relative to the
+*sender* of the message, so for a receiver, the meanings are effectively
+reversed.
+
+
+
+--- CONNECT(version, maxdata, "system-identity-string") ----------------
+
+The CONNECT message establishes the presence of a remote system.
+The version is used to ensure protocol compatibility and maxdata
+declares the maximum message body size that the remote system
+is willing to accept.
+
+Currently, version=0x01000000 and maxdata=4096
+
+Both sides send a CONNECT message when the connection between them is
+established.  Until a CONNECT message is received no other messages may
+be sent.  Any messages received before a CONNECT message MUST be ignored.
+
+If a CONNECT message is received with an unknown version or insufficiently
+large maxdata value, the connection with the other side must be closed.
+
+The system identity string should be "<systemtype>:<serialno>:<banner>"
+where systemtype is "bootloader", "device", or "host", serialno is some
+kind of unique ID (or empty), and banner is a human-readable version
+or identifier string (informational only).
+
+
+--- OPEN(local-id, 0, "destination") -----------------------------------
+
+The OPEN message informs the recipient that the sender has a stream
+identified by local-id that it wishes to connect to the named
+destination in the message payload.  The local-id may not be zero.
+
+The OPEN message MUST result in either a READY message indicating that
+the connection has been established (and identifying the other end) or
+a CLOSE message, indicating failure.  An OPEN message also implies
+a READY message sent at the same time.
+
+Common destination naming conventions include:
+
+* "tcp:<host>:<port>" - host may be omitted to indicate localhost
+* "udp:<host>:<port>" - host may be omitted to indicate localhost
+* "local-dgram:<identifier>"
+* "local-stream:<identifier>"
+* "shell" - local shell service
+* "upload" - service for pushing files across (like aproto's /sync)
+* "fs-bridge" - FUSE protocol filesystem bridge
+
+
+--- READY(local-id, remote-id, "") -------------------------------------
+
+The READY message informs the recipient that the sender's stream
+identified by local-id is ready for write messages and that it is
+connected to the recipient's stream identified by remote-id.
+
+Neither the local-id nor the remote-id may be zero. 
+
+A READY message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have been
+closed while this message was in-flight.
+
+The local-id is ignored on all but the first READY message (where it
+is used to establish the connection).  Nonetheless, the local-id MUST
+not change on later READY messages sent to the same stream.
+
+
+
+--- WRITE(0, remote-id, "data") ----------------------------------------
+
+The WRITE message sends data to the recipient's stream identified by
+remote-id.  The payload MUST be <= maxdata in length.
+
+A WRITE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have been
+closed while this message was in-flight.
+
+A WRITE message may not be sent until a READY message is received.
+Once a WRITE message is sent, an additional WRITE message may not be
+sent until another READY message has been received.  Recipients of
+a WRITE message that is in violation of this requirement will CLOSE
+the connection.
+
+
+--- CLOSE(local-id, remote-id, "") -------------------------------------
+
+The CLOSE message informs recipient that the connection between the
+sender's stream (local-id) and the recipient's stream (remote-id) is
+broken.  The remote-id MUST not be zero, but the local-id MAY be zero
+if this CLOSE indicates a failed OPEN.
+
+A CLOSE message containing a remote-id which does not map to an open
+stream on the recipient's side is ignored.  The stream may have
+already been closed by the recipient while this message was in-flight.
+
+The recipient should not respond to a CLOSE message in any way.  The
+recipient should cancel pending WRITEs or CLOSEs, but this is not a
+requirement, since they will be ignored.
+
+
+--- SYNC(online, sequence, "") -----------------------------------------
+
+The SYNC message is used by the io pump to make sure that stale
+outbound messages are discarded when the connection to the remote side
+is broken.  It is only used internally to the bridge and never valid
+to send across the wire.  
+
+* when the connection to the remote side goes offline, the io pump 
+  sends a SYNC(0, 0) and starts discarding all messages
+* when the connection to the remote side is established, the io pump
+  sends a SYNC(1, token) and continues to discard messages
+* when the io pump receives a matching SYNC(1, token), it once again
+  starts accepting messages to forward to the remote side
+
+
+--- message command constants ------------------------------------------
+
+#define A_SYNC 0x434e5953
+#define A_CNXN 0x4e584e43
+#define A_OPEN 0x4e45504f
+#define A_OKAY 0x59414b4f
+#define A_CLSE 0x45534c43
+#define A_WRTE 0x45545257
+
+
+
+--- implementation details ---------------------------------------------
+
+The core of the bridge program will use three threads.  One thread
+will be a select/epoll loop to handle io between various inbound and
+outbound connections and the connection to the remote side.
+
+The remote side connection will be implemented as two threads (one for
+reading, one for writing) and a datagram socketpair to provide the
+channel between the main select/epoll thread and the remote connection
+threadpair.  The reason for this is that for usb connections, the
+kernel interface on linux and osx does not allow you to do meaningful
+nonblocking IO.
+
+The endian swapping for the message headers will happen (as needed) in
+the remote connection threadpair and that the rest of the program will
+always treat message header values as native-endian.
+
+The bridge program will be able to have a number of mini-servers
+compiled in.  They will be published under known names (examples
+"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
+service, the bridge program will create a stream socketpair and spawn
+a thread or subprocess to handle the io.
+
+
+--- simplified / embedded implementation -------------------------------
+
+For limited environments, like the bootloader, it is allowable to
+support a smaller, fixed number of channels using pre-assigned channel
+ID numbers such that only one stream may be connected to a bootloader
+endpoint at any given time.  The protocol remains unchanged, but the
+"embedded" version of it is less dynamic.
+
+The bootloader will support two streams.  A "bootloader:debug" stream,
+which may be opened to get debug messages from the bootloader and a 
+"bootloader:control", stream which will support the set of basic 
+bootloader commands.
+
+Example command stream dialogues:  
+  "flash_kernel,2515049,........\n" "okay\n" 
+  "flash_ramdisk,5038,........\n" "fail,flash write error\n" 
+  "bogus_command......" <CLOSE>
+
+
+--- future expansion ---------------------------------------------------
+
+I plan on providing either a message or a special control stream so that
+the client device could ask the host computer to setup inbound socket
+translations on the fly on behalf of the client device.
+
+
+The initial design does handshaking to provide flow control, with a
+message flow that looks like:
+
+  >OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
+
+The far side may choose to issue the READY message as soon as it receives
+a WRITE or it may defer the READY until the write to the local stream
+succeeds.  A future version may want to do some level of windowing where
+multiple WRITEs may be sent without requiring individual READY acks.
+
+------------------------------------------------------------------------
+
+--- smartsockets -------------------------------------------------------
+
+Port 5037 is used for smart sockets which allow a client on the host
+side to request access to a service in the host adb daemon or in the
+remote (device) daemon.  The service is requested by ascii name,
+preceeded by a 4 digit hex length.  Upon successful connection an
+"OKAY" response is sent, otherwise a "FAIL" message is returned.  Once
+connected the client is talking to that (remote or local) service.
+
+client: <hex4> <service-name>
+server: "OKAY"
+
+client: <hex4> <service-name>
+server: "FAIL" <hex4> <reason>
+
diff --git a/adb/remount_service.c b/adb/remount_service.c
new file mode 100644
index 0000000..26bc841
--- /dev/null
+++ b/adb/remount_service.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+
+
+static int system_ro = 1;
+
+/* Returns the mount number of the requested partition from /proc/mtd */
+static int find_mount(const char *findme)
+{
+    int fd;
+    int res;
+    int size;
+    char *token = NULL;
+    const char delims[] = "\n";
+    char buf[1024];
+
+    fd = unix_open("/proc/mtd", O_RDONLY);
+    if (fd < 0)
+        return -errno;
+
+    buf[sizeof(buf) - 1] = '\0';
+    size = adb_read(fd, buf, sizeof(buf) - 1);
+    adb_close(fd);
+
+    token = strtok(buf, delims);
+
+    while (token) {
+        char mtdname[16];
+        int mtdnum, mtdsize, mtderasesize;
+
+        res = sscanf(token, "mtd%d: %x %x %15s",
+                     &mtdnum, &mtdsize, &mtderasesize, mtdname);
+
+        if (res == 4 && !strcmp(mtdname, findme))
+            return mtdnum;
+
+        token = strtok(NULL, delims);
+    }
+    return -1;
+}
+
+/* Init mounts /system as read only, remount to enable writes. */
+static int remount_system()
+{
+    int num;
+    char source[64];
+    if (system_ro == 0) {
+        return 0;
+    }
+    if ((num = find_mount("\"system\"")) < 0)
+        return -1;
+
+    snprintf(source, sizeof source, "/dev/block/mtdblock%d", num);
+    system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL);
+    return system_ro;
+}
+
+static void write_string(int fd, const char* str)
+{
+    writex(fd, str, strlen(str));
+}
+
+void remount_service(int fd, void *cookie)
+{
+    int ret = remount_system();
+
+    if (!ret)
+       write_string(fd, "remount succeeded\n");
+    else {
+        char    buffer[200];
+        snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
+        write_string(fd, buffer);
+    }
+
+    adb_close(fd);
+}
+
diff --git a/adb/services.c b/adb/services.c
new file mode 100644
index 0000000..e686949
--- /dev/null
+++ b/adb/services.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_ADB
+#include "adb.h"
+#include "file_sync_service.h"
+
+#if ADB_HOST
+#  ifndef HAVE_WINSOCK
+#    include <netinet/in.h>
+#    include <netdb.h>
+#  endif
+#endif
+
+typedef struct stinfo stinfo;
+
+struct stinfo {
+    void (*func)(int fd, void *cookie);
+    int fd;
+    void *cookie;
+};
+
+
+void *service_bootstrap_func(void *x)
+{
+    stinfo *sti = x;
+    sti->func(sti->fd, sti->cookie);
+    free(sti);
+    return 0;
+}
+
+#if ADB_HOST
+ADB_MUTEX_DEFINE( dns_lock );
+
+static void dns_service(int fd, void *cookie)
+{
+    char *hostname = cookie;
+    struct hostent *hp;
+    unsigned zero = 0;
+
+    adb_mutex_lock(&dns_lock);
+    hp = gethostbyname(hostname);
+    if(hp == 0) {
+        writex(fd, &zero, 4);
+    } else {
+        writex(fd, hp->h_addr, 4);
+    }
+    adb_mutex_unlock(&dns_lock);
+    adb_close(fd);
+}
+#else
+extern int recovery_mode;
+
+static void recover_service(int s, void *cookie)
+{
+    unsigned char buf[4096];
+    unsigned count = (unsigned) cookie;
+    int fd;
+
+    fd = adb_creat("/tmp/update", 0644);
+    if(fd < 0) {
+        adb_close(s);
+        return;
+    }
+
+    while(count > 0) {
+        unsigned xfer = (count > 4096) ? 4096 : count;
+        if(readx(s, buf, xfer)) break;
+        if(writex(fd, buf, xfer)) break;
+        count -= xfer;
+    }
+
+    if(count == 0) {
+        writex(s, "OKAY", 4);
+    } else {
+        writex(s, "FAIL", 4);
+    }
+    adb_close(fd);
+    adb_close(s);
+
+    fd = adb_creat("/tmp/update.begin", 0644);
+    adb_close(fd);
+}
+
+#endif
+
+#if 0
+static void echo_service(int fd, void *cookie)
+{
+    char buf[4096];
+    int r;
+    char *p;
+    int c;
+
+    for(;;) {
+        r = read(fd, buf, 4096);
+        if(r == 0) goto done;
+        if(r < 0) {
+            if(errno == EINTR) continue;
+            else goto done;
+        }
+
+        c = r;
+        p = buf;
+        while(c > 0) {
+            r = write(fd, p, c);
+            if(r > 0) {
+                c -= r;
+                p += r;
+                continue;
+            }
+            if((r < 0) && (errno == EINTR)) continue;
+            goto done;
+        }
+    }
+done:
+    close(fd);
+}
+#endif
+
+static int create_service_thread(void (*func)(int, void *), void *cookie)
+{
+    stinfo *sti;
+    adb_thread_t t;
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        printf("cannot create service socket pair\n");
+        return -1;
+    }
+
+    sti = malloc(sizeof(stinfo));
+    if(sti == 0) fatal("cannot allocate stinfo");
+    sti->func = func;
+    sti->cookie = cookie;
+    sti->fd = s[1];
+
+    if(adb_thread_create( &t, service_bootstrap_func, sti)){
+        free(sti);
+        adb_close(s[0]);
+        adb_close(s[1]);
+        printf("cannot create service thread\n");
+        return -1;
+    }
+
+    D("service thread started, %d:%d\n",s[0], s[1]);
+    return s[0];
+}
+
+static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
+{
+#ifdef HAVE_WIN32_PROC
+	fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+	return -1;
+#else /* !HAVE_WIN32_PROC */
+    char *devname;
+    int ptm;
+    pid_t pid;
+
+    ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
+    if(ptm < 0){
+        printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
+        return -1;
+    }
+    fcntl(ptm, F_SETFD, FD_CLOEXEC);
+
+    if(grantpt(ptm) || unlockpt(ptm) ||
+       ((devname = (char*) ptsname(ptm)) == 0)){
+        printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
+        return -1;
+    }
+
+    pid = fork();
+    if(pid < 0) {
+        printf("- fork failed: %s -\n", strerror(errno));
+        return -1;
+    }
+
+    if(pid == 0){
+        int pts;
+
+        setsid();
+
+        pts = unix_open(devname, O_RDWR);
+        if(pts < 0) exit(-1);
+
+        dup2(pts, 0);
+        dup2(pts, 1);
+        dup2(pts, 2);
+
+        adb_close(ptm);
+
+        execl(cmd, cmd, arg0, arg1, NULL);
+        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+                cmd, strerror(errno), errno);
+        exit(-1);
+    } else {
+        return ptm;
+    }
+#endif /* !HAVE_WIN32_PROC */
+}
+
+#if ADB_HOST
+#define SHELL_COMMAND "/bin/sh"
+#else
+#define SHELL_COMMAND "/system/bin/sh"
+#endif
+
+int service_to_fd(const char *name)
+{
+    int ret = -1;
+
+    if(!strncmp(name, "tcp:", 4)) {
+        int port = atoi(name + 4);
+        name = strchr(name + 4, ':');
+        if(name == 0) {
+            ret = socket_loopback_client(port, SOCK_STREAM);
+            if (ret >= 0)
+                disable_tcp_nagle(ret);
+        } else {
+#if ADB_HOST
+            adb_mutex_lock(&dns_lock);
+            ret = socket_network_client(name + 1, port, SOCK_STREAM);
+            adb_mutex_unlock(&dns_lock);
+#else
+            return -1;
+#endif
+        }
+#ifndef HAVE_WINSOCK   /* winsock doesn't implement unix domain sockets */
+    } else if(!strncmp(name, "local:", 6)) {
+        ret = socket_local_client(name + 6,
+                ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+    } else if(!strncmp(name, "localreserved:", 14)) {
+        ret = socket_local_client(name + 14,
+                ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+    } else if(!strncmp(name, "localabstract:", 14)) {
+        ret = socket_local_client(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if(!strncmp(name, "localfilesystem:", 16)) {
+        ret = socket_local_client(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+#endif
+#if ADB_HOST
+    } else if(!strncmp("dns:", name, 4)){
+        char *n = strdup(name + 4);
+        if(n == 0) return -1;
+        ret = create_service_thread(dns_service, n);
+#else /* !ADB_HOST */
+    } else if(!strncmp("dev:", name, 4)) {
+        ret = unix_open(name + 4, O_RDWR);
+    } else if(!strncmp(name, "framebuffer:", 12)) {
+        ret = create_service_thread(framebuffer_service, 0);
+    } else if(recovery_mode && !strncmp(name, "recover:", 8)) {
+        ret = create_service_thread(recover_service, (void*) atoi(name + 8));
+    } else if (!strncmp(name, "jdwp:", 5)) {
+        ret = create_jdwp_connection_fd(atoi(name+5));
+    } else if (!strncmp(name, "log:", 4)) {
+        ret = create_service_thread(log_service, get_log_file_path(name + 4));
+#endif
+    } else if(!HOST && !strncmp(name, "shell:", 6)) {
+        if(name[6]) {
+            ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
+        } else {
+            ret = create_subprocess(SHELL_COMMAND, "-", 0);
+        }
+#if !ADB_HOST
+    } else if(!strncmp(name, "sync:", 5)) {
+        ret = create_service_thread(file_sync_service, NULL);
+    } else if(!strncmp(name, "remount:", 8)) {
+        ret = create_service_thread(remount_service, NULL);
+#endif
+#if 0
+    } else if(!strncmp(name, "echo:", 5)){
+        ret = create_service_thread(echo_service, 0);
+#endif
+    }
+    if (ret >= 0) {
+        close_on_exec(ret);
+    }
+    return ret;
+}
+
+#if ADB_HOST
+struct state_info {
+    transport_type transport;
+    char* serial;
+    int state;
+};
+
+static void wait_for_state(int fd, void* cookie)
+{
+    struct state_info* sinfo = cookie;
+    char* err = "unknown error";
+
+    D("wait_for_state %d\n", sinfo->state);
+
+    atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
+    if(t != 0) {
+        writex(fd, "OKAY", 4);
+    } else {
+        sendfailmsg(fd, err);
+    }
+
+    if (sinfo->serial)
+        free(sinfo->serial);
+    free(sinfo);
+    adb_close(fd);
+    D("wait_for_state is done\n");
+}
+#endif
+
+#if ADB_HOST
+asocket*  host_service_to_socket(const char*  name, const char *serial)
+{
+    if (!strcmp(name,"track-devices")) {
+        return create_device_tracker();
+    } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
+        struct state_info* sinfo = malloc(sizeof(struct state_info));
+
+        if (serial)
+            sinfo->serial = strdup(serial);
+        else
+            sinfo->serial = NULL;
+
+        name += strlen("wait-for-");
+
+        if (!strncmp(name, "local", strlen("local"))) {
+            sinfo->transport = kTransportLocal;
+            sinfo->state = CS_DEVICE;
+        } else if (!strncmp(name, "usb", strlen("usb"))) {
+            sinfo->transport = kTransportUsb;
+            sinfo->state = CS_DEVICE;
+        } else if (!strncmp(name, "any", strlen("any"))) {
+            sinfo->transport = kTransportAny;
+            sinfo->state = CS_DEVICE;
+        } else {
+            free(sinfo);
+            return NULL;
+        }
+
+        int fd = create_service_thread(wait_for_state, sinfo);
+        return create_local_socket(fd);
+    }
+    return NULL;
+}
+#endif /* ADB_HOST */
diff --git a/adb/shlist.c b/adb/shlist.c
new file mode 100755
index 0000000..44919ef
--- /dev/null
+++ b/adb/shlist.c
@@ -0,0 +1,185 @@
+/*-------------------------------------------------------------------*/
+/*                         List  Functionality                       */
+/*-------------------------------------------------------------------*/
+/* #define SH_LIST_DEBUG */
+/*-------------------------------------------------------------------*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "shlist.h"
+/*-------------------------------------------------------------------*/
+void shListInitList( SHLIST *listPtr )
+{
+  listPtr->data = (void *)0L;
+  listPtr->next = listPtr;
+  listPtr->prev = listPtr;
+}
+
+SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func )
+{
+  SHLIST *item;
+
+  for(item=head->next;( item != head );item=item->next)
+    if( func ) {
+      if( func( val, item->data ) ) {
+        return( item );
+      }
+    }
+    else {
+      if( item->data == val ) {
+        return( item );
+      }
+    }
+  return( NULL );
+}
+
+SHLIST *shListGetLastItem( SHLIST *head )
+{
+  if( head->prev != head )
+    return( head->prev );
+  return( NULL );
+}
+
+SHLIST *shListGetFirstItem( SHLIST *head )
+{
+  if( head->next != head )
+    return( head->next );
+  return( NULL );
+}
+
+SHLIST *shListGetNItem( SHLIST *head, unsigned long num )
+{
+  SHLIST *item;
+  unsigned long i;
+
+  for(i=0,item=head->next;( (i < num) && (item != head) );i++,item=item->next);
+  if( item != head )
+    return( item );
+  return( NULL );
+}
+
+SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item )
+{
+  if( item == NULL )
+    return( NULL );
+  if( item->next != head )
+    return( item->next );
+  return( NULL );
+}
+
+SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item )
+{
+  if( item == NULL )
+    return( NULL );
+  if( item->prev != head )
+    return( item->prev );
+  return( NULL );
+}
+
+void shListDelItem( SHLIST *head, SHLIST *item, shListFree func )
+{
+  if( item == NULL )
+    return;
+#ifdef SH_LIST_DEBUG
+  fprintf(stderr, "Del %lx\n", (unsigned long)(item->data));
+#endif
+  (item->prev)->next = item->next;
+  (item->next)->prev = item->prev;
+  if( func && item->data ) {
+    func( (void *)(item->data) );
+  }
+  free( item );
+  head->data = (void *)((unsigned long)(head->data) - 1);
+}
+
+void shListInsFirstItem( SHLIST *head, void *val )
+{ /* Insert to the beginning of the list */
+  SHLIST *item;
+
+  item = (SHLIST *)malloc( sizeof(SHLIST) );
+  if( item == NULL )
+    return;
+  item->data = val;
+  item->next = head->next;
+  item->prev = head;
+  (head->next)->prev = item;
+  head->next = item;
+#ifdef SH_LIST_DEBUG
+  fprintf(stderr, "Ins First %lx\n", (unsigned long)(item->data));
+#endif
+  head->data = (void *)((unsigned long)(head->data) + 1);
+}
+
+void shListInsLastItem( SHLIST *head, void *val )
+{ /* Insert to the end of the list */
+  SHLIST *item;
+
+  item = (SHLIST *)malloc( sizeof(SHLIST) );
+  if( item == NULL )
+    return;
+  item->data = val;
+  item->next = head;
+  item->prev = head->prev;
+  (head->prev)->next = item;
+  head->prev = item;
+#ifdef SH_LIST_DEBUG
+  fprintf(stderr, "Ins Last %lx\n", (unsigned long)(item->data));
+#endif
+  head->data = (void *)((unsigned long)(head->data) + 1);
+}
+
+void shListInsBeforeItem( SHLIST *head, void *val, void *etal, 
+                          shListCmp func )
+{
+  SHLIST *item, *iptr;
+
+  if( func == NULL )
+    shListInsFirstItem( head, val );
+  else {
+    item = (SHLIST *)malloc( sizeof(SHLIST) );
+    if( item == NULL )
+      return;
+    item->data = val;
+    for(iptr=head->next;( iptr != head );iptr=iptr->next)
+      if( func( val, iptr->data, etal ) )
+         break;
+    item->next = iptr;
+    item->prev = iptr->prev;
+    (iptr->prev)->next = item;
+    iptr->prev = item;
+#ifdef SH_LIST_DEBUG
+    fprintf(stderr, "Ins Before %lx\n", (unsigned long)(item->data));
+#endif
+    head->data = (void *)((unsigned long)(head->data) + 1);
+  }
+}
+
+void shListDelAllItems( SHLIST *head, shListFree func )
+{
+  SHLIST *item;
+
+  for(item=head->next;( item != head );) {
+    shListDelItem( head, item, func );
+    item = head->next;
+  }
+  head->data = (void *)0L;
+}
+
+void shListPrintAllItems( SHLIST *head, shListPrint func )
+{
+#ifdef SH_LIST_DEBUG
+  SHLIST *item;
+
+  for(item=head->next;( item != head );item=item->next)
+    if( func ) {
+      func(item->data);
+    }
+    else {
+      fprintf(stderr, "Item: %lx\n",(unsigned long)(item->data));
+    }
+#endif
+}
+
+unsigned long shListGetCount( SHLIST *head )
+{
+  return( (unsigned long)(head->data) );
+}
diff --git a/adb/shlist.h b/adb/shlist.h
new file mode 100755
index 0000000..0a9b07b
--- /dev/null
+++ b/adb/shlist.h
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------*/
+/*                         List  Functionality                       */
+/*-------------------------------------------------------------------*/
+#ifndef _SHLIST_H_
+#define _SHLIST_H_
+
+typedef struct SHLIST_STRUC {
+  void *data;
+  struct SHLIST_STRUC *next;
+  struct SHLIST_STRUC *prev;
+} SHLIST;
+
+typedef int (*shListCmp)( void *valo, void *valn, void *etalon );
+typedef int (*shListPrint)( void *val );
+typedef void (*shListFree)( void *val );
+typedef int (*shListEqual)( void *val,  void *idata );
+
+void shListInitList( SHLIST *listPtr );
+SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func );
+SHLIST *shListGetFirstItem( SHLIST *head );
+SHLIST *shListGetNItem( SHLIST *head, unsigned long num );
+SHLIST *shListGetLastItem( SHLIST *head );
+SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item );
+SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item );
+void shListDelItem( SHLIST *head, SHLIST *item, shListFree func );
+void shListInsFirstItem( SHLIST *head, void *val );
+void shListInsBeforeItem( SHLIST *head, void *val, void *etalon, 
+                          shListCmp func );
+void shListInsLastItem( SHLIST *head, void *val );
+void shListDelAllItems( SHLIST *head, shListFree func );
+void shListPrintAllItems( SHLIST *head, shListPrint func );
+unsigned long shListGetCount( SHLIST *head );
+
+#endif
diff --git a/adb/sockets.c b/adb/sockets.c
new file mode 100644
index 0000000..9f1b598
--- /dev/null
+++ b/adb/sockets.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sysdeps.h"
+
+#define  TRACE_TAG  TRACE_SOCKETS
+#include "adb.h"
+
+ADB_MUTEX_DEFINE( socket_list_lock );
+
+static void local_socket_close_locked(asocket *s);
+
+int sendfailmsg(int fd, const char *reason)
+{
+    char buf[9];
+    int len;
+    len = strlen(reason);
+    if(len > 0xffff) len = 0xffff;
+    snprintf(buf, sizeof buf, "FAIL%04x", len);
+    if(writex(fd, buf, 8)) return -1;
+    return writex(fd, reason, len);
+}
+
+//extern int online;
+
+static unsigned local_socket_next_id = 1;
+
+static asocket local_socket_list = {
+    .next = &local_socket_list,
+    .prev = &local_socket_list,
+};
+
+/* the the list of currently closing local sockets.
+** these have no peer anymore, but still packets to
+** write to their fd.
+*/
+static asocket local_socket_closing_list = {
+    .next = &local_socket_closing_list,
+    .prev = &local_socket_closing_list,
+};
+
+asocket *find_local_socket(unsigned id)
+{
+    asocket *s;
+    asocket *result = NULL;
+
+    adb_mutex_lock(&socket_list_lock);
+    for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) {
+        if(s->id == id) result = s;
+    }
+    adb_mutex_unlock(&socket_list_lock);
+
+    return result;
+}
+
+static void
+insert_local_socket(asocket*  s, asocket*  list)
+{
+    s->next       = list;
+    s->prev       = s->next->prev;
+    s->prev->next = s;
+    s->next->prev = s;
+}
+
+
+void install_local_socket(asocket *s)
+{
+    adb_mutex_lock(&socket_list_lock);
+
+    s->id = local_socket_next_id++;
+    insert_local_socket(s, &local_socket_list);
+
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+void remove_socket(asocket *s)
+{
+    // socket_list_lock should already be held
+    if (s->prev && s->next)
+    {
+        s->prev->next = s->next;
+        s->next->prev = s->prev;
+        s->next = 0;
+        s->prev = 0;
+        s->id = 0;
+    }
+}
+
+void close_all_sockets(atransport *t)
+{
+    asocket *s;
+
+        /* this is a little gross, but since s->close() *will* modify
+        ** the list out from under you, your options are limited.
+        */
+    adb_mutex_lock(&socket_list_lock);
+restart:
+    for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
+        if(s->transport == t || (s->peer && s->peer->transport == t)) {
+            local_socket_close_locked(s);
+            goto restart;
+        }
+    }
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+static int local_socket_enqueue(asocket *s, apacket *p)
+{
+    D("LS(%d): enqueue %d\n", s->id, p->len);
+
+    p->ptr = p->data;
+
+        /* if there is already data queue'd, we will receive
+        ** events when it's time to write.  just add this to
+        ** the tail
+        */
+    if(s->pkt_first) {
+        goto enqueue;
+    }
+
+        /* write as much as we can, until we
+        ** would block or there is an error/eof
+        */
+    while(p->len > 0) {
+        int r = adb_write(s->fd, p->ptr, p->len);
+        if(r > 0) {
+            p->len -= r;
+            p->ptr += r;
+            continue;
+        }
+        if((r == 0) || (errno != EAGAIN)) {
+            D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+            s->close(s);
+            return 1; /* not ready (error) */
+        } else {
+            break;
+        }
+    }
+
+    if(p->len == 0) {
+        put_apacket(p);
+        return 0; /* ready for more data */
+    }
+
+enqueue:
+    p->next = 0;
+    if(s->pkt_first) {
+        s->pkt_last->next = p;
+    } else {
+        s->pkt_first = p;
+    }
+    s->pkt_last = p;
+
+        /* make sure we are notified when we can drain the queue */
+    fdevent_add(&s->fde, FDE_WRITE);
+
+    return 1; /* not ready (backlog) */
+}
+
+static void local_socket_ready(asocket *s)
+{
+        /* far side is ready for data, pay attention to
+           readable events */
+    fdevent_add(&s->fde, FDE_READ);
+//    D("LS(%d): ready()\n", s->id);
+}
+
+static void local_socket_close(asocket *s)
+{
+    adb_mutex_lock(&socket_list_lock);
+    local_socket_close_locked(s);
+    adb_mutex_unlock(&socket_list_lock);
+}
+
+// be sure to hold the socket list lock when calling this
+static void local_socket_destroy(asocket  *s)
+{
+    apacket *p, *n;
+
+        /* IMPORTANT: the remove closes the fd
+        ** that belongs to this socket
+        */
+    fdevent_remove(&s->fde);
+
+        /* dispose of any unwritten data */
+    for(p = s->pkt_first; p; p = n) {
+        D("LS(%d): discarding %d bytes\n", s->id, p->len);
+        n = p->next;
+        put_apacket(p);
+    }
+    remove_socket(s);
+    free(s);
+}
+
+
+static void local_socket_close_locked(asocket *s)
+{
+    if(s->peer) {
+        s->peer->peer = 0;
+        // tweak to avoid deadlock
+        if (s->peer->close == local_socket_close)
+            local_socket_close_locked(s->peer);
+        else
+            s->peer->close(s->peer);
+    }
+
+        /* If we are already closing, or if there are no
+        ** pending packets, destroy immediately
+        */
+    if (s->closing || s->pkt_first == NULL) {
+        int   id = s->id;
+        local_socket_destroy(s);
+        D("LS(%d): closed\n", id);
+        return;
+    }
+
+        /* otherwise, put on the closing list
+        */
+    D("LS(%d): closing\n", s->id);
+    s->closing = 1;
+    fdevent_del(&s->fde, FDE_READ);
+    remove_socket(s);
+    insert_local_socket(s, &local_socket_closing_list);
+}
+
+static void local_socket_event_func(int fd, unsigned ev, void *_s)
+{
+    asocket *s = _s;
+
+    /* put the FDE_WRITE processing before the FDE_READ
+    ** in order to simplify the code.
+    */
+    if(ev & FDE_WRITE){
+        apacket *p;
+
+        while((p = s->pkt_first) != 0) {
+            while(p->len > 0) {
+                int r = adb_write(fd, p->ptr, p->len);
+                if(r > 0) {
+                    p->ptr += r;
+                    p->len -= r;
+                    continue;
+                }
+                if(r < 0) {
+                    /* returning here is ok because FDE_READ will
+                    ** be processed in the next iteration loop
+                    */
+                    if(errno == EAGAIN) return;
+                    if(errno == EINTR) continue;
+                }
+                s->close(s);
+                return;
+            }
+
+            if(p->len == 0) {
+                s->pkt_first = p->next;
+                if(s->pkt_first == 0) s->pkt_last = 0;
+                put_apacket(p);
+            }
+        }
+
+            /* if we sent the last packet of a closing socket,
+            ** we can now destroy it.
+            */
+        if (s->closing) {
+            s->close(s);
+            return;
+        }
+
+            /* no more packets queued, so we can ignore
+            ** writable events again and tell our peer
+            ** to resume writing
+            */
+        fdevent_del(&s->fde, FDE_WRITE);
+        s->peer->ready(s->peer);
+    }
+
+
+    if(ev & FDE_READ){
+        apacket *p = get_apacket();
+        unsigned char *x = p->data;
+        size_t avail = MAX_PAYLOAD;
+        int r;
+        int is_eof = 0;
+
+        while(avail > 0) {
+            r = adb_read(fd, x, avail);
+            if(r > 0) {
+                avail -= r;
+                x += r;
+                continue;
+            }
+            if(r < 0) {
+                if(errno == EAGAIN) break;
+                if(errno == EINTR) continue;
+            }
+
+                /* r = 0 or unhandled error */
+            is_eof = 1;
+            break;
+        }
+
+        if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+            put_apacket(p);
+        } else {
+            p->len = MAX_PAYLOAD - avail;
+
+            r = s->peer->enqueue(s->peer, p);
+
+            if(r < 0) {
+                    /* error return means they closed us as a side-effect
+                    ** and we must return immediately.
+                    **
+                    ** note that if we still have buffered packets, the
+                    ** socket will be placed on the closing socket list.
+                    ** this handler function will be called again
+                    ** to process FDE_WRITE events.
+                    */
+                return;
+            }
+
+            if(r > 0) {
+                    /* if the remote cannot accept further events,
+                    ** we disable notification of READs.  They'll
+                    ** be enabled again when we get a call to ready()
+                    */
+                fdevent_del(&s->fde, FDE_READ);
+            }
+        }
+
+        if(is_eof) {
+            s->close(s);
+        }
+    }
+
+    if(ev & FDE_ERROR){
+            /* this should be caught be the next read or write
+            ** catching it here means we may skip the last few
+            ** bytes of readable data.
+            */
+//        s->close(s);
+        return;
+    }
+}
+
+asocket *create_local_socket(int fd)
+{
+    asocket *s = calloc(1, sizeof(asocket));
+    if(s == 0) fatal("cannot allocate socket");
+    install_local_socket(s);
+    s->fd = fd;
+    s->enqueue = local_socket_enqueue;
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+
+    fdevent_install(&s->fde, fd, local_socket_event_func, s);
+/*    fdevent_add(&s->fde, FDE_ERROR); */
+    //fprintf(stderr, "Created local socket in create_local_socket \n");
+    D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+    return s;
+}
+
+asocket *create_local_service_socket(const char *name)
+{
+    asocket *s;
+    int fd;
+
+#if !ADB_HOST
+    if (!strcmp(name,"jdwp")) {
+        return create_jdwp_service_socket();
+    }
+    if (!strcmp(name,"track-jdwp")) {
+        return create_jdwp_tracker_service_socket();
+    }
+#endif
+    fd = service_to_fd(name);
+    if(fd < 0) return 0;
+
+    s = create_local_socket(fd);
+    D("LS(%d): bound to '%s'\n", s->id, name);
+    return s;
+}
+
+#if ADB_HOST
+static asocket *create_host_service_socket(const char *name, const char* serial)
+{
+    asocket *s;
+
+    s = host_service_to_socket(name, serial);
+
+    if (s != NULL) {
+        D("LS(%d) bound to '%s'\n", s->id, name);
+        return s;
+    }
+
+    return s;
+}
+#endif /* ADB_HOST */
+
+/* a Remote socket is used to send/receive data to/from a given transport object
+** it needs to be closed when the transport is forcibly destroyed by the user
+*/
+typedef struct aremotesocket {
+    asocket      socket;
+    adisconnect  disconnect;
+} aremotesocket;
+
+static int remote_socket_enqueue(asocket *s, apacket *p)
+{
+    D("Calling remote_socket_enqueue\n");
+    p->msg.command = A_WRTE;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    p->msg.data_length = p->len;
+    send_packet(p, s->transport);
+    return 1;
+}
+
+static void remote_socket_ready(asocket *s)
+{
+    D("Calling remote_socket_ready\n");
+    apacket *p = get_apacket();
+    p->msg.command = A_OKAY;
+    p->msg.arg0 = s->peer->id;
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+}
+
+static void remote_socket_close(asocket *s)
+{
+    D("Calling remote_socket_close\n");
+    apacket *p = get_apacket();
+    p->msg.command = A_CLSE;
+    if(s->peer) {
+        p->msg.arg0 = s->peer->id;
+        s->peer->peer = 0;
+        s->peer->close(s->peer);
+    }
+    p->msg.arg1 = s->id;
+    send_packet(p, s->transport);
+    D("RS(%d): closed\n", s->id);
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+static void remote_socket_disconnect(void*  _s, atransport*  t)
+{
+    asocket*  s    = _s;
+    asocket*  peer = s->peer;
+
+    D("remote_socket_disconnect RS(%d)\n", s->id);
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    free(s);
+}
+
+asocket *create_remote_socket(unsigned id, atransport *t)
+{
+    asocket *s = calloc(1, sizeof(aremotesocket));
+    adisconnect*  dis = &((aremotesocket*)s)->disconnect;
+
+    if(s == 0) fatal("cannot allocate socket");
+    s->id = id;
+    s->enqueue = remote_socket_enqueue;
+    s->ready = remote_socket_ready;
+    s->close = remote_socket_close;
+    s->transport = t;
+
+    dis->func   = remote_socket_disconnect;
+    dis->opaque = s;
+    add_transport_disconnect( t, dis );
+    D("RS(%d): created\n", s->id);
+    return s;
+}
+
+void connect_to_remote(asocket *s, const char *destination)
+{
+    D("Connect_to_remote call \n");
+    apacket *p = get_apacket();
+    int len = strlen(destination) + 1;
+
+    if(len > (MAX_PAYLOAD-1)) {
+        fatal("destination oversized");
+    }
+
+    D("LS(%d): connect('%s')\n", s->id, destination);
+    p->msg.command = A_OPEN;
+    p->msg.arg0 = s->id;
+    p->msg.data_length = len;
+    strcpy((char*) p->data, destination);
+    send_packet(p, s->transport);
+}
+
+
+/* this is used by magic sockets to rig local sockets to
+   send the go-ahead message when they connect */
+static void local_socket_ready_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    adb_write(s->fd, "OKAY", 4);
+    s->ready(s);
+}
+
+/* this is used by magic sockets to rig local sockets to
+   send the failure message if they are closed before
+   connected (to avoid closing them without a status message) */
+static void local_socket_close_notify(asocket *s)
+{
+    s->ready = local_socket_ready;
+    s->close = local_socket_close;
+    sendfailmsg(s->fd, "closed");
+    s->close(s);
+}
+
+unsigned unhex(unsigned char *s, int len)
+{
+    unsigned n = 0, c;
+
+    while(len-- > 0) {
+        switch((c = *s++)) {
+        case '0': case '1': case '2':
+        case '3': case '4': case '5':
+        case '6': case '7': case '8':
+        case '9':
+            c -= '0';
+            break;
+        case 'a': case 'b': case 'c':
+        case 'd': case 'e': case 'f':
+            c = c - 'a' + 10;
+            break;
+        case 'A': case 'B': case 'C':
+        case 'D': case 'E': case 'F':
+            c = c - 'A' + 10;
+            break;
+        default:
+            return 0xffffffff;
+        }
+
+        n = (n << 4) | c;
+    }
+
+    return n;
+}
+
+static int smart_socket_enqueue(asocket *s, apacket *p)
+{
+    unsigned len;
+#if ADB_HOST
+    char *service = NULL;
+    char* serial = NULL;
+    transport_type ttype = kTransportAny;
+#endif
+
+    D("SS(%d): enqueue %d\n", s->id, p->len);
+
+    if(s->pkt_first == 0) {
+        s->pkt_first = p;
+        s->pkt_last = p;
+    } else {
+        if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
+            D("SS(%d): overflow\n", s->id);
+            put_apacket(p);
+            goto fail;
+        }
+
+        memcpy(s->pkt_first->data + s->pkt_first->len,
+               p->data, p->len);
+        s->pkt_first->len += p->len;
+        put_apacket(p);
+
+        p = s->pkt_first;
+    }
+
+        /* don't bother if we can't decode the length */
+    if(p->len < 4) return 0;
+
+    len = unhex(p->data, 4);
+    if((len < 1) ||  (len > 1024)) {
+        D("SS(%d): bad size (%d)\n", s->id, len);
+        goto fail;
+    }
+
+    D("SS(%d): len is %d\n", s->id, len );
+        /* can't do anything until we have the full header */
+    if((len + 4) > p->len) {
+        D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+        return 0;
+    }
+
+    p->data[len + 4] = 0;
+
+    D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+
+#if ADB_HOST
+    service = (char *)p->data + 4;
+    if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
+        char* serial_end;
+        service += strlen("host-serial:");
+
+        // serial number should follow "host:"
+        serial_end = strchr(service, ':');
+        if (serial_end) {
+            *serial_end = 0; // terminate string
+            serial = service;
+            service = serial_end + 1;
+        }
+    } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+        ttype = kTransportUsb;
+        service += strlen("host-usb:");
+    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+        ttype = kTransportLocal;
+        service += strlen("host-local:");
+    } else if (!strncmp(service, "host:", strlen("host:"))) {
+        ttype = kTransportAny;
+        service += strlen("host:");
+    } else {
+        service = NULL;
+    }
+
+    if (service) {
+        asocket *s2;
+
+            /* some requests are handled immediately -- in that
+            ** case the handle_host_request() routine has sent
+            ** the OKAY or FAIL message and all we have to do
+            ** is clean up.
+            */
+        if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
+                /* XXX fail message? */
+            D( "SS(%d): handled host service '%s'\n", s->id, service );
+            goto fail;
+        }
+        if (!strncmp(service, "transport", strlen("transport"))) {
+            D( "SS(%d): okay transport\n", s->id );
+            p->len = 0;
+            return 0;
+        }
+
+            /* try to find a local service with this name.
+            ** if no such service exists, we'll fail out
+            ** and tear down here.
+            */
+        s2 = create_host_service_socket(service, serial);
+        if(s2 == 0) {
+            D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+            sendfailmsg(s->peer->fd, "unknown host service");
+            goto fail;
+        }
+
+            /* we've connected to a local host service,
+            ** so we make our peer back into a regular
+            ** local socket and bind it to the new local
+            ** service socket, acknowledge the successful
+            ** connection, and close this smart socket now
+            ** that its work is done.
+            */
+        adb_write(s->peer->fd, "OKAY", 4);
+
+        s->peer->ready = local_socket_ready;
+        s->peer->close = local_socket_close;
+        s->peer->peer = s2;
+        s2->peer = s->peer;
+        s->peer = 0;
+        D( "SS(%d): okay\n", s->id );
+        s->close(s);
+
+            /* initial state is "ready" */
+        s2->ready(s2);
+        return 0;
+    }
+#else /* !ADB_HOST */
+    if (s->transport == NULL) {
+        char* error_string = "unknown failure";
+        s->transport = acquire_one_transport (CS_ANY,
+                kTransportAny, NULL, &error_string);
+
+        if (s->transport == NULL) {
+            sendfailmsg(s->peer->fd, error_string);
+            goto fail;
+        }
+    }
+#endif
+
+    if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
+           /* if there's no remote we fail the connection
+            ** right here and terminate it
+            */
+        sendfailmsg(s->peer->fd, "device offline (x)");
+        goto fail;
+    }
+
+
+        /* instrument our peer to pass the success or fail
+        ** message back once it connects or closes, then
+        ** detach from it, request the connection, and
+        ** tear down
+        */
+    s->peer->ready = local_socket_ready_notify;
+    s->peer->close = local_socket_close_notify;
+    s->peer->peer = 0;
+        /* give him our transport and upref it */
+    s->peer->transport = s->transport;
+
+    connect_to_remote(s->peer, (char*) (p->data + 4));
+    s->peer = 0;
+    s->close(s);
+    return 1;
+
+fail:
+        /* we're going to close our peer as a side-effect, so
+        ** return -1 to signal that state to the local socket
+        ** who is enqueueing against us
+        */
+    s->close(s);
+    return -1;
+}
+
+static void smart_socket_ready(asocket *s)
+{
+    D("SS(%d): ready\n", s->id);
+}
+
+static void smart_socket_close(asocket *s)
+{
+    D("SS(%d): closed\n", s->id);
+    if(s->pkt_first){
+        put_apacket(s->pkt_first);
+    }
+    if(s->peer) {
+        s->peer->peer = 0;
+        s->peer->close(s->peer);
+    }
+    free(s);
+}
+
+asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
+{
+    D("Creating smart socket \n");
+    asocket *s = calloc(1, sizeof(asocket));
+    if(s == 0) fatal("cannot allocate socket");
+    s->id = 0;
+    s->enqueue = smart_socket_enqueue;
+    s->ready = smart_socket_ready;
+    s->close = smart_socket_close;
+    s->extra = action_cb;
+
+    D("SS(%d): created %p\n", s->id, action_cb);
+    return s;
+}
+
+void smart_socket_action(asocket *s, const char *act)
+{
+
+}
+
+void connect_to_smartsocket(asocket *s)
+{
+    D("Connecting to smart socket \n");
+    asocket *ss = create_smart_socket(smart_socket_action);
+    s->peer = ss;
+    ss->peer = s;
+    s->ready(s);
+}
diff --git a/adb/sockets.dia b/adb/sockets.dia
new file mode 100644
index 0000000..c626f20
--- /dev/null
+++ b/adb/sockets.dia
Binary files differ
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
new file mode 100644
index 0000000..e5d17a8
--- /dev/null
+++ b/adb/sysdeps.h
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* this file contains system-dependent definitions used by ADB
+ * they're related to threads, sockets and file descriptors
+ */
+#ifndef _ADB_SYSDEPS_H
+#define _ADB_SYSDEPS_H
+
+#ifdef __CYGWIN__
+#  undef _WIN32
+#endif
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <process.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define OS_PATH_SEPARATOR '\\'
+#define OS_PATH_SEPARATOR_STR "\\"
+
+typedef CRITICAL_SECTION          adb_mutex_t;
+
+#define  ADB_MUTEX_DEFINE(x)     adb_mutex_t   x
+
+/* declare all mutexes */
+#define  ADB_MUTEX(x)   extern adb_mutex_t  x;
+#include "mutex_list.h"
+
+extern void  adb_sysdeps_init(void);
+
+static __inline__ void adb_mutex_lock( adb_mutex_t*  lock )
+{
+    EnterCriticalSection( lock );
+}
+
+static __inline__ void  adb_mutex_unlock( adb_mutex_t*  lock )
+{
+    LeaveCriticalSection( lock );
+}
+
+typedef struct { unsigned  tid; }  adb_thread_t;
+
+typedef  void*  (*adb_thread_func_t)(void*  arg);
+
+typedef  void (*win_thread_func_t)(void*  arg);
+
+static __inline__ int  adb_thread_create( adb_thread_t  *thread, adb_thread_func_t  func, void*  arg)
+{
+	thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
+	if (thread->tid == (unsigned)-1L) {
+		return -1;
+	}
+	return 0;
+}
+
+static __inline__ void  close_on_exec(int  fd)
+{
+	/* nothing really */
+}
+
+extern void  disable_tcp_nagle(int  fd);
+
+#define  lstat    stat   /* no symlinks on Win32 */
+
+#define  S_ISLNK(m)   0   /* no symlinks on Win32 */
+
+static __inline__  int    adb_unlink(const char*  path)
+{
+    int  rc = unlink(path);
+
+    if (rc == -1 && errno == EACCES) {
+        /* unlink returns EACCES when the file is read-only, so we first */
+        /* try to make it writable, then unlink again...                  */
+        rc = chmod(path, _S_IREAD|_S_IWRITE );
+        if (rc == 0)
+            rc = unlink(path);
+    }
+    return rc;
+}
+#undef  unlink
+#define unlink  ___xxx_unlink
+
+static __inline__ int  adb_mkdir(const char*  path, int mode)
+{
+	return _mkdir(path);
+}
+#undef   mkdir
+#define  mkdir  ___xxx_mkdir
+
+extern int  adb_open(const char*  path, int  options);
+extern int  adb_creat(const char*  path, int  mode);
+extern int  adb_read(int  fd, void* buf, int len);
+extern int  adb_write(int  fd, const void*  buf, int  len);
+extern int  adb_lseek(int  fd, int  pos, int  where);
+extern int  adb_close(int  fd);
+
+static __inline__ int  unix_close(int fd)
+{
+    return close(fd);
+}
+#undef   close
+#define  close   ____xxx_close
+
+static __inline__  int  unix_read(int  fd, void*  buf, size_t  len)
+{
+    return read(fd, buf, len);
+}
+#undef   read
+#define  read  ___xxx_read
+
+static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
+{
+    return write(fd, buf, len);
+}
+#undef   write
+#define  write  ___xxx_write
+
+static __inline__ int  adb_open_mode(const char* path, int options, int mode)
+{
+	return adb_open(path, options);
+}
+
+static __inline__ int  unix_open(const char*  path, int options,...)
+{
+    if ((options & O_CREAT) == 0)
+    {
+        return  open(path, options);
+    }
+    else
+    {
+        int      mode;
+        va_list  args;
+        va_start( args, options );
+        mode = va_arg( args, int );
+        va_end( args );
+        return open(path, options, mode);
+    }
+}
+#define  open    ___xxx_unix_open
+
+
+/* normally provided by <cutils/misc.h> */
+extern void*  load_file(const char*  pathname, unsigned*  psize);
+
+/* normally provided by <cutils/sockets.h> */
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_loopback_server(int port, int type);
+extern int socket_inaddr_any_server(int port, int type);
+
+/* normally provided by <cutils/fdevent.h> */
+
+#define FDE_READ              0x0001
+#define FDE_WRITE             0x0002
+#define FDE_ERROR             0x0004
+#define FDE_DONT_CLOSE        0x0080
+
+typedef struct fdevent fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+void     fdevent_destroy(fdevent *fde);
+void     fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+void     fdevent_remove(fdevent *item);
+void     fdevent_set(fdevent *fde, unsigned events);
+void     fdevent_add(fdevent *fde, unsigned events);
+void     fdevent_del(fdevent *fde, unsigned events);
+void     fdevent_loop();
+
+struct fdevent {
+    fdevent *next;
+    fdevent *prev;
+
+    int fd;
+    unsigned short state;
+    unsigned short events;
+
+    fd_func func;
+    void *arg;
+};
+
+static __inline__ void  adb_sleep_ms( int  mseconds )
+{
+	Sleep( mseconds );
+}
+
+extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
+
+#undef   accept
+#define  accept  ___xxx_accept
+
+static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+{
+    int opt = bufsize;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
+}
+
+extern int  adb_socketpair( int  sv[2] );
+
+static __inline__  char*  adb_dirstart( const char*  path )
+{
+    char*  p  = strchr(path, '/');
+    char*  p2 = strchr(path, '\\');
+
+    if ( !p )
+        p = p2;
+    else if ( p2 && p2 > p )
+        p = p2;
+
+    return p;
+}
+
+static __inline__  char*  adb_dirstop( const char*  path )
+{
+    char*  p  = strrchr(path, '/');
+    char*  p2 = strrchr(path, '\\');
+
+    if ( !p )
+        p = p2;
+    else if ( p2 && p2 > p )
+        p = p2;
+
+    return p;
+}
+
+static __inline__  int  adb_is_absolute_host_path( const char*  path )
+{
+    return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+}
+
+#else /* !_WIN32 a.k.a. Unix */
+
+#include <cutils/fdevent.h>
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+
+#define OS_PATH_SEPARATOR '/'
+#define OS_PATH_SEPARATOR_STR "/"
+
+typedef  pthread_mutex_t          adb_mutex_t;
+#define  ADB_MUTEX_INITIALIZER    PTHREAD_MUTEX_INITIALIZER
+#define  adb_mutex_init           pthread_mutex_init
+#define  adb_mutex_lock           pthread_mutex_lock
+#define  adb_mutex_unlock         pthread_mutex_unlock
+#define  adb_mutex_destroy        pthread_mutex_destroy
+
+#define  ADB_MUTEX_DEFINE(m)      static adb_mutex_t   m = PTHREAD_MUTEX_INITIALIZER
+
+#define  adb_cond_t               pthread_cond_t
+#define  adb_cond_init            pthread_cond_init
+#define  adb_cond_wait            pthread_cond_wait
+#define  adb_cond_broadcast       pthread_cond_broadcast
+#define  adb_cond_signal          pthread_cond_signal
+#define  adb_cond_destroy         pthread_cond_destroy
+
+static __inline__ void  close_on_exec(int  fd)
+{
+	fcntl( fd, F_SETFD, FD_CLOEXEC );
+}
+
+static __inline__ int  unix_open(const char*  path, int options,...)
+{
+    if ((options & O_CREAT) == 0)
+    {
+        return  open(path, options);
+    }
+    else
+    {
+        int      mode;
+        va_list  args;
+        va_start( args, options );
+        mode = va_arg( args, int );
+        va_end( args );
+        return open(path, options, mode);
+    }
+}
+
+static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
+{
+	return open( pathname, options, mode );
+}
+
+
+static __inline__ int  adb_open( const char*  pathname, int  options )
+{
+    int  fd = open( pathname, options );
+    if (fd < 0)
+        return -1;
+    close_on_exec( fd );
+    return fd;
+}
+#undef   open
+#define  open    ___xxx_open
+
+static __inline__ int  adb_close(int fd)
+{
+    return close(fd);
+}
+#undef   close
+#define  close   ____xxx_close
+
+
+static __inline__  int  adb_read(int  fd, void*  buf, size_t  len)
+{
+    return read(fd, buf, len);
+}
+
+#undef   read
+#define  read  ___xxx_read
+
+static __inline__  int  adb_write(int  fd, const void*  buf, size_t  len)
+{
+    return write(fd, buf, len);
+}
+#undef   write
+#define  write  ___xxx_write
+
+static __inline__ int   adb_lseek(int  fd, int  pos, int  where)
+{
+    return lseek(fd, pos, where);
+}
+#undef   lseek
+#define  lseek   ___xxx_lseek
+
+static __inline__  int    adb_unlink(const char*  path)
+{
+    return  unlink(path);
+}
+#undef  unlink
+#define unlink  ___xxx_unlink
+
+static __inline__  int  adb_creat(const char*  path, int  mode)
+{
+    int  fd = creat(path, mode);
+
+	if ( fd < 0 )
+		return -1;
+
+    close_on_exec(fd);
+	return fd;
+}
+#undef   creat
+#define  creat  ___xxx_creat
+
+static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
+{
+    return  accept( serverfd, addr, addrlen );
+}
+
+#undef   accept
+#define  accept  ___xxx_accept
+
+#define  unix_read   adb_read
+#define  unix_write  adb_write
+#define  unix_close  adb_close
+
+typedef  pthread_t                 adb_thread_t;
+
+typedef void*  (*adb_thread_func_t)( void*  arg );
+
+static __inline__ int  adb_thread_create( adb_thread_t  *pthread, adb_thread_func_t  start, void*  arg )
+{
+    pthread_attr_t   attr;
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+    return pthread_create( pthread, &attr, start, arg );
+}
+
+static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+{
+    int opt = bufsize;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+}
+
+static __inline__ void  disable_tcp_nagle(int fd)
+{
+    int  on = 1;
+    setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
+}
+
+
+static __inline__ int  unix_socketpair( int  d, int  type, int  protocol, int sv[2] )
+{
+    return socketpair( d, type, protocol, sv );
+}
+
+static __inline__ int  adb_socketpair( int  sv[2] )
+{
+    int  rc;
+
+    rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
+    if (rc < 0)
+        return -1;
+
+    close_on_exec( sv[0] );
+    close_on_exec( sv[1] );
+    return 0;
+}
+
+#undef   socketpair
+#define  socketpair   ___xxx_socketpair
+
+static __inline__ void  adb_sleep_ms( int  mseconds )
+{
+	usleep( mseconds*1000 );
+}
+
+static __inline__ int  adb_mkdir(const char*  path, int mode)
+{
+	return mkdir(path, mode);
+}
+#undef   mkdir
+#define  mkdir  ___xxx_mkdir
+
+static __inline__ void  adb_sysdeps_init(void)
+{
+}
+
+static __inline__ char*  adb_dirstart(const char*  path)
+{
+    return strchr(path, '/');
+}
+
+static __inline__ char*  adb_dirstop(const char*  path)
+{
+    return strrchr(path, '/');
+}
+
+static __inline__  int  adb_is_absolute_host_path( const char*  path )
+{
+    return path[0] == '/';
+}
+
+#endif /* !_WIN32 */
+
+#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c
new file mode 100644
index 0000000..c2a9a98
--- /dev/null
+++ b/adb/sysdeps_win32.c
@@ -0,0 +1,1953 @@
+#include "sysdeps.h"
+#include <windows.h>
+#include <winsock2.h>
+#include <stdio.h>
+#include <errno.h>
+#define  TRACE_TAG  TRACE_SYSDEPS
+#include "adb.h"
+
+extern void fatal(const char *fmt, ...);
+
+#define assert(cond)  do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****      replaces libs/cutils/load_file.c                          *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+    HANDLE    file;
+    char     *data;
+    DWORD     file_size;
+
+    file = CreateFile( fn,
+                       GENERIC_READ,
+                       FILE_SHARE_READ,
+                       NULL,
+                       OPEN_EXISTING,
+                       0,
+                       NULL );
+
+    if (file == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    file_size = GetFileSize( file, NULL );
+    data      = NULL;
+
+    if (file_size > 0) {
+        data = (char*) malloc( file_size + 1 );
+        if (data == NULL) {
+            D("load_file: could not allocate %ld bytes\n", file_size );
+            file_size = 0;
+        } else {
+            DWORD  out_bytes;
+
+            if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
+                 out_bytes != file_size )
+            {
+                D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+                free(data);
+                data      = NULL;
+                file_size = 0;
+            }
+        }
+    }
+    CloseHandle( file );
+
+    *_sz = (unsigned) file_size;
+    return  data;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    common file descriptor handling                             *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+typedef const struct FHClassRec_*   FHClass;
+
+typedef struct FHRec_*          FH;
+
+typedef struct EventHookRec_*  EventHook;
+
+typedef struct FHClassRec_
+{
+    void (*_fh_init) ( FH  f );
+    int  (*_fh_close)( FH  f );
+    int  (*_fh_lseek)( FH  f, int  pos, int  origin );
+    int  (*_fh_read) ( FH  f, void*  buf, int  len );
+    int  (*_fh_write)( FH  f, const void*  buf, int  len );
+    void (*_fh_hook) ( FH  f, int  events, EventHook  hook );
+
+} FHClassRec;
+
+/* used to emulate unix-domain socket pairs */
+typedef struct SocketPairRec_*  SocketPair;
+
+typedef struct FHRec_
+{
+    FHClass    clazz;
+    int        used;
+    int        eof;
+    union {
+        HANDLE      handle;
+        SOCKET      socket;
+        SocketPair  pair;
+    } u;
+
+    HANDLE    event;
+    int       mask;
+
+    char  name[32];
+
+} FHRec;
+
+#define  fh_handle  u.handle
+#define  fh_socket  u.socket
+#define  fh_pair    u.pair
+
+#define  WIN32_FH_BASE    100
+
+#define  WIN32_MAX_FHS    128
+
+static adb_mutex_t   _win32_lock;
+static  FHRec        _win32_fhs[ WIN32_MAX_FHS ];
+static  int          _win32_fh_count;
+
+static FH
+_fh_from_int( int   fd )
+{
+    FH  f;
+
+    fd -= WIN32_FH_BASE;
+
+    if (fd < 0 || fd >= _win32_fh_count) {
+        D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+        errno = EBADF;
+        return NULL;
+    }
+
+    f = &_win32_fhs[fd];
+
+    if (f->used == 0) {
+        D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+        errno = EBADF;
+        return NULL;
+    }
+
+    return f;
+}
+
+
+static int
+_fh_to_int( FH  f )
+{
+    if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
+        return (int)(f - _win32_fhs) + WIN32_FH_BASE;
+
+    return -1;
+}
+
+static FH
+_fh_alloc( FHClass  clazz )
+{
+    int  nn;
+    FH   f = NULL;
+
+    adb_mutex_lock( &_win32_lock );
+
+    if (_win32_fh_count < WIN32_MAX_FHS) {
+        f = &_win32_fhs[ _win32_fh_count++ ];
+        goto Exit;
+    }
+
+    for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
+        if ( _win32_fhs[nn].clazz == NULL) {
+            f = &_win32_fhs[nn];
+            goto Exit;
+        }
+    }
+    D( "_fh_alloc: no more free file descriptors\n" );
+Exit:
+    if (f) {
+        f->clazz = clazz;
+        f->used  = 1;
+        f->eof   = 0;
+        clazz->_fh_init(f);
+    }
+    adb_mutex_unlock( &_win32_lock );
+    return f;
+}
+
+
+static int
+_fh_close( FH   f )
+{
+    if ( f->used ) {
+        f->clazz->_fh_close( f );
+        f->used = 0;
+        f->eof  = 0;
+        f->clazz = NULL;
+    }
+    return 0;
+}
+
+/* forward definitions */
+static const FHClassRec   _fh_file_class;
+static const FHClassRec   _fh_socket_class;
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    file-based descriptor handling                              *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_fh_file_init( FH  f )
+{
+    f->fh_handle = INVALID_HANDLE_VALUE;
+}
+
+static int
+_fh_file_close( FH  f )
+{
+    CloseHandle( f->fh_handle );
+    f->fh_handle = INVALID_HANDLE_VALUE;
+    return 0;
+}
+
+static int
+_fh_file_read( FH  f,  void*  buf, int   len )
+{
+    DWORD  read_bytes;
+
+    if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
+        D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+        errno = EIO;
+        return -1;
+    } else if (read_bytes < (DWORD)len) {
+        f->eof = 1;
+    }
+    return (int)read_bytes;
+}
+
+static int
+_fh_file_write( FH  f,  const void*  buf, int   len )
+{
+    DWORD  wrote_bytes;
+
+    if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
+        D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+        errno = EIO;
+        return -1;
+    } else if (wrote_bytes < (DWORD)len) {
+        f->eof = 1;
+    }
+    return  (int)wrote_bytes;
+}
+
+static int
+_fh_file_lseek( FH  f, int  pos, int  origin )
+{
+    DWORD  method;
+    DWORD  result;
+
+    switch (origin)
+    {
+        case SEEK_SET:  method = FILE_BEGIN; break;
+        case SEEK_CUR:  method = FILE_CURRENT; break;
+        case SEEK_END:  method = FILE_END; break;
+        default:
+            errno = EINVAL;
+            return -1;
+    }
+
+    result = SetFilePointer( f->fh_handle, pos, NULL, method );
+    if (result == INVALID_SET_FILE_POINTER) {
+        errno = EIO;
+        return -1;
+    } else {
+        f->eof = 0;
+    }
+    return (int)result;
+}
+
+static void  _fh_file_hook( FH  f, int  event, EventHook  eventhook );  /* forward */
+
+static const FHClassRec  _fh_file_class =
+{
+    _fh_file_init,
+    _fh_file_close,
+    _fh_file_lseek,
+    _fh_file_read,
+    _fh_file_write,
+    _fh_file_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    file-based descriptor handling                              *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+int  adb_open(const char*  path, int  options)
+{
+    FH  f;
+
+    DWORD  desiredAccess       = 0;
+    DWORD  shareMode           = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+    switch (options) {
+        case O_RDONLY:
+            desiredAccess = GENERIC_READ;
+            break;
+        case O_WRONLY:
+            desiredAccess = GENERIC_WRITE;
+            break;
+        case O_RDWR:
+            desiredAccess = GENERIC_READ | GENERIC_WRITE;
+            break;
+        default:
+            D("adb_open: invalid options (0x%0x)\n", options);
+            errno = EINVAL;
+            return -1;
+    }
+
+    f = _fh_alloc( &_fh_file_class );
+    if ( !f ) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
+                               0, NULL );
+
+    if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        _fh_close(f);
+        D( "adb_open: could not open '%s':", path );
+        switch (GetLastError()) {
+            case ERROR_FILE_NOT_FOUND:
+                D( "file not found\n" );
+                errno = ENOENT;
+                return -1;
+
+            case ERROR_PATH_NOT_FOUND:
+                D( "path not found\n" );
+                errno = ENOTDIR;
+                return -1;
+
+            default:
+                D( "unknown error\n" );
+                errno = ENOENT;
+                return -1;
+        }
+    }
+    
+    snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+    D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+/* ignore mode on Win32 */
+int  adb_creat(const char*  path, int  mode)
+{
+    FH  f;
+
+    f = _fh_alloc( &_fh_file_class );
+    if ( !f ) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                               NULL );
+
+    if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        _fh_close(f);
+        D( "adb_creat: could not open '%s':", path );
+        switch (GetLastError()) {
+            case ERROR_FILE_NOT_FOUND:
+                D( "file not found\n" );
+                errno = ENOENT;
+                return -1;
+
+            case ERROR_PATH_NOT_FOUND:
+                D( "path not found\n" );
+                errno = ENOTDIR;
+                return -1;
+
+            default:
+                D( "unknown error\n" );
+                errno = ENOENT;
+                return -1;
+        }
+    }
+    snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+    D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+
+int  adb_read(int  fd, void* buf, int len)
+{
+    FH     f = _fh_from_int(fd);
+
+    if (f == NULL) {
+        return -1;
+    }
+
+    return f->clazz->_fh_read( f, buf, len );
+}
+
+
+int  adb_write(int  fd, const void*  buf, int  len)
+{
+    FH     f = _fh_from_int(fd);
+
+    if (f == NULL) {
+        return -1;
+    }
+
+    return f->clazz->_fh_write(f, buf, len);
+}
+
+
+int  adb_lseek(int  fd, int  pos, int  where)
+{
+    FH     f = _fh_from_int(fd);
+
+    if (!f) {
+        return -1;
+    }
+
+    return f->clazz->_fh_lseek(f, pos, where);
+}
+
+
+int  adb_close(int  fd)
+{
+    FH   f = _fh_from_int(fd);
+
+    if (!f) {
+        return -1;
+    }
+
+    D( "adb_close: %s\n", f->name);
+    _fh_close(f);
+    return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    socket-based file descriptors                               *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_socket_set_errno( void )
+{
+    switch (WSAGetLastError()) {
+    case 0:              errno = 0; break;
+    case WSAEWOULDBLOCK: errno = EAGAIN; break;
+    case WSAEINTR:       errno = EINTR; break;
+    default:
+        D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
+        errno = EINVAL;
+    }
+}
+
+static void
+_fh_socket_init( FH  f )
+{
+    f->fh_socket = INVALID_SOCKET;
+    f->event     = WSACreateEvent();
+    f->mask      = 0;
+}
+
+static int
+_fh_socket_close( FH  f )
+{
+    /* gently tell any peer that we're closing the socket */
+    shutdown( f->fh_socket, SD_BOTH );
+    closesocket( f->fh_socket );
+    f->fh_socket = INVALID_SOCKET;
+    CloseHandle( f->event );
+    f->mask = 0;
+    return 0;
+}
+
+static int
+_fh_socket_lseek( FH  f, int pos, int origin )
+{
+    errno = EPIPE;
+    return -1;
+}
+
+static int
+_fh_socket_read( FH  f, void*  buf, int  len )
+{
+    int  result = recv( f->fh_socket, buf, len, 0 );
+    if (result == SOCKET_ERROR) {
+        _socket_set_errno();
+        result = -1;
+    }
+    return  result;
+}
+
+static int
+_fh_socket_write( FH  f, const void*  buf, int  len )
+{
+    int  result = send( f->fh_socket, buf, len, 0 );
+    if (result == SOCKET_ERROR) {
+        _socket_set_errno();
+        result = -1;
+    }
+    return result;
+}
+
+static void  _fh_socket_hook( FH  f, int  event, EventHook  hook );  /* forward */
+
+static const FHClassRec  _fh_socket_class =
+{
+    _fh_socket_init,
+    _fh_socket_close,
+    _fh_socket_lseek,
+    _fh_socket_read,
+    _fh_socket_write,
+    _fh_socket_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    replacement for libs/cutils/socket_xxxx.c                   *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#include <winsock2.h>
+
+static int  _winsock_init;
+
+static void
+_cleanup_winsock( void )
+{
+    WSACleanup();
+}
+
+static void
+_init_winsock( void )
+{
+    if (!_winsock_init) {
+        WSADATA  wsaData;
+        int      rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+        if (rc != 0) {
+            fatal( "adb: could not initialize Winsock\n" );
+        }
+        atexit( _cleanup_winsock );
+        _winsock_init = 1;
+    }
+}
+
+int socket_loopback_client(int port, int type)
+{
+    FH  f = _fh_alloc( &_fh_socket_class );
+    struct sockaddr_in addr;
+    SOCKET  s;
+
+    if (!f)
+        return -1;
+
+    if (!_winsock_init)
+        _init_winsock();
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket(AF_INET, type, 0);
+    if(s == INVALID_SOCKET) {
+        D("socket_loopback_client: could not create socket\n" );
+        _fh_close(f);
+        return -1;
+    }
+
+    f->fh_socket = s;
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
+        _fh_close(f);
+        return -1;
+    }
+    snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+    D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+#define LISTEN_BACKLOG 4
+
+int socket_loopback_server(int port, int type)
+{
+    FH   f = _fh_alloc( &_fh_socket_class );
+    struct sockaddr_in addr;
+    SOCKET  s;
+    int  n;
+
+    if (!f) {
+        return -1;
+    }
+
+    if (!_winsock_init)
+        _init_winsock();
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket(AF_INET, type, 0);
+    if(s == INVALID_SOCKET) return -1;
+
+    f->fh_socket = s;
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        _fh_close(f);
+        return -1;
+    }
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+        if (ret < 0) {
+            _fh_close(f);
+            return -1;
+        }
+    }
+    snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+    D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+
+int socket_network_client(const char *host, int port, int type)
+{
+    FH  f = _fh_alloc( &_fh_socket_class );
+    struct hostent *hp;
+    struct sockaddr_in addr;
+    SOCKET s;
+
+    if (!f)
+        return -1;
+
+    if (!_winsock_init)
+        _init_winsock();
+
+    hp = gethostbyname(host);
+    if(hp == 0) {
+        _fh_close(f);
+        return -1;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = hp->h_addrtype;
+    addr.sin_port = htons(port);
+    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+    s = socket(hp->h_addrtype, type, 0);
+    if(s == INVALID_SOCKET) {
+        _fh_close(f);
+        return -1;
+    }
+    f->fh_socket = s;
+
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        _fh_close(f);
+        return -1;
+    }
+
+    snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+    D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+
+int socket_inaddr_any_server(int port, int type)
+{
+    FH  f = _fh_alloc( &_fh_socket_class );
+    struct sockaddr_in addr;
+    SOCKET  s;
+    int n;
+
+    if (!f)
+        return -1;
+
+    if (!_winsock_init)
+        _init_winsock();
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    s = socket(AF_INET, type, 0);
+    if(s == INVALID_SOCKET) {
+        _fh_close(f);
+        return -1;
+    }
+
+    f->fh_socket = s;
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        _fh_close(f);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+        if (ret < 0) {
+            _fh_close(f);
+            return -1;
+        }
+    }
+    snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+    D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+    return _fh_to_int(f);
+}
+
+#undef accept
+int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
+{
+    FH   serverfh = _fh_from_int(serverfd);
+    FH   fh;
+    
+    if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+        D( "adb_socket_accept: invalid fd %d\n", serverfd );
+        return -1;
+    }
+    
+    fh = _fh_alloc( &_fh_socket_class );
+    if (!fh) {
+        D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+        return -1;
+    }
+
+    fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+    if (fh->fh_socket == INVALID_SOCKET) {
+        _fh_close( fh );
+        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+        return -1;
+    }
+    
+    snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
+    D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
+    return  _fh_to_int(fh);
+}
+
+
+void  disable_tcp_nagle(int fd)
+{
+    FH   fh = _fh_from_int(fd);
+    int  on;
+    
+    if ( !fh || fh->clazz != &_fh_socket_class )
+        return;
+
+    setsockopt( fh->fh_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on) );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    emulated socketpairs                                       *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* we implement socketpairs directly in use space for the following reasons:
+ *   - it avoids copying data from/to the Nt kernel
+ *   - it allows us to implement fdevent hooks easily and cheaply, something
+ *     that is not possible with standard Win32 pipes !!
+ *
+ * basically, we use two circular buffers, each one corresponding to a given
+ * direction.
+ *
+ * each buffer is implemented as two regions:
+ *
+ *   region A which is (a_start,a_end)
+ *   region B which is (0, b_end)  with b_end <= a_start
+ *
+ * an empty buffer has:  a_start = a_end = b_end = 0
+ *
+ * a_start is the pointer where we start reading data
+ * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
+ * then you start writing at b_end
+ *
+ * the buffer is full when  b_end == a_start && a_end == BUFFER_SIZE
+ *
+ * there is room when b_end < a_start || a_end < BUFER_SIZE
+ *
+ * when reading, a_start is incremented, it a_start meets a_end, then
+ * we do:  a_start = 0, a_end = b_end, b_end = 0, and keep going on..
+ */
+
+#define  BIP_BUFFER_SIZE   4096
+
+#if 0
+#include <stdio.h>
+#  define  BIPD(x)      D x
+#  define  BIPDUMP   bip_dump_hex
+
+static void  bip_dump_hex( const unsigned char*  ptr, size_t  len )
+{
+    int  nn, len2 = len;
+
+    if (len2 > 8) len2 = 8;
+
+    for (nn = 0; nn < len2; nn++) 
+        printf("%02x", ptr[nn]);
+    printf("  ");
+
+    for (nn = 0; nn < len2; nn++) {
+        int  c = ptr[nn];
+        if (c < 32 || c > 127)
+            c = '.';
+        printf("%c", c);
+    }
+    printf("\n");
+    fflush(stdout);
+}
+
+#else
+#  define  BIPD(x)        do {} while (0)
+#  define  BIPDUMP(p,l)   BIPD(p)
+#endif
+
+typedef struct BipBufferRec_
+{
+    int                a_start;
+    int                a_end;
+    int                b_end;
+    int                fdin;
+    int                fdout;
+    int                closed;
+    int                can_write;  /* boolean */
+    HANDLE             evt_write;  /* event signaled when one can write to a buffer  */
+    int                can_read;   /* boolean */
+    HANDLE             evt_read;   /* event signaled when one can read from a buffer */
+    CRITICAL_SECTION  lock;
+    unsigned char      buff[ BIP_BUFFER_SIZE ];
+
+} BipBufferRec, *BipBuffer;
+
+static void
+bip_buffer_init( BipBuffer  buffer )
+{
+    D( "bit_buffer_init %p\n", buffer );
+    buffer->a_start   = 0;
+    buffer->a_end     = 0;
+    buffer->b_end     = 0;
+    buffer->can_write = 1;
+    buffer->can_read  = 0;
+    buffer->fdin      = 0;
+    buffer->fdout     = 0;
+    buffer->closed    = 0;
+    buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
+    buffer->evt_read  = CreateEvent( NULL, TRUE, FALSE, NULL );
+    InitializeCriticalSection( &buffer->lock );
+}
+
+static void
+bip_buffer_close( BipBuffer  bip )
+{
+    bip->closed = 1;
+
+    if (!bip->can_read) {
+        SetEvent( bip->evt_read );
+    }
+    if (!bip->can_write) {
+        SetEvent( bip->evt_write );
+    }
+}
+
+static void
+bip_buffer_done( BipBuffer  bip )
+{
+    BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+    CloseHandle( bip->evt_read );
+    CloseHandle( bip->evt_write );
+    DeleteCriticalSection( &bip->lock );
+}
+
+static int
+bip_buffer_write( BipBuffer  bip, const void* src, int  len )
+{
+    int  avail, count = 0;
+
+    if (len <= 0)
+        return 0;
+
+    BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPDUMP( src, len );
+
+    EnterCriticalSection( &bip->lock );
+
+    while (!bip->can_write) {
+        int  ret;
+        LeaveCriticalSection( &bip->lock );
+
+        if (bip->closed) {
+            errno = EPIPE;
+            return -1;
+        }
+        /* spinlocking here is probably unfair, but let's live with it */
+        ret = WaitForSingleObject( bip->evt_write, INFINITE );
+        if (ret != WAIT_OBJECT_0) {  /* buffer probably closed */
+            D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+            return 0;
+        }
+        if (bip->closed) {
+            errno = EPIPE;
+            return -1;
+        }
+        EnterCriticalSection( &bip->lock );
+    }
+
+    BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+    avail = BIP_BUFFER_SIZE - bip->a_end;
+    if (avail > 0)
+    {
+        /* we can append to region A */
+        if (avail > len)
+            avail = len;
+
+        memcpy( bip->buff + bip->a_end, src, avail );
+        src   += avail;
+        count += avail;
+        len   -= avail;
+
+        bip->a_end += avail;
+        if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
+            bip->can_write = 0;
+            ResetEvent( bip->evt_write );
+            goto Exit;
+        }
+    }
+
+    if (len == 0)
+        goto Exit;
+
+    avail = bip->a_start - bip->b_end;
+    assert( avail > 0 );  /* since can_write is TRUE */
+
+    if (avail > len)
+        avail = len;
+
+    memcpy( bip->buff + bip->b_end, src, avail );
+    count += avail;
+    bip->b_end += avail;
+
+    if (bip->b_end == bip->a_start) {
+        bip->can_write = 0;
+        ResetEvent( bip->evt_write );
+    }
+
+Exit:
+    assert( count > 0 );
+
+    if ( !bip->can_read ) {
+        bip->can_read = 1;
+        SetEvent( bip->evt_read );
+    }
+
+    BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", 
+            bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+    LeaveCriticalSection( &bip->lock );
+
+    return count;
+ }
+
+static int
+bip_buffer_read( BipBuffer  bip, void*  dst, int  len )
+{
+    int  avail, count = 0;
+
+    if (len <= 0)
+        return 0;
+
+    BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+    EnterCriticalSection( &bip->lock );
+    while ( !bip->can_read )
+    {
+#if 0
+        LeaveCriticalSection( &bip->lock );
+        errno = EAGAIN;
+        return -1;
+#else    
+        int  ret;
+        LeaveCriticalSection( &bip->lock );
+
+        if (bip->closed) {
+            errno = EPIPE;
+            return -1;
+        }
+
+        ret = WaitForSingleObject( bip->evt_read, INFINITE );
+        if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
+            D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+            return 0;
+        }
+        if (bip->closed) {
+            errno = EPIPE;
+            return -1;
+        }
+        EnterCriticalSection( &bip->lock );
+#endif
+    }
+
+    BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+    avail = bip->a_end - bip->a_start;
+    assert( avail > 0 );  /* since can_read is TRUE */
+
+    if (avail > len)
+        avail = len;
+
+    memcpy( dst, bip->buff + bip->a_start, avail );
+    dst   += avail;
+    count += avail;
+    len   -= avail;
+
+    bip->a_start += avail;
+    if (bip->a_start < bip->a_end)
+        goto Exit;
+
+    bip->a_start = 0;
+    bip->a_end   = bip->b_end;
+    bip->b_end   = 0;
+
+    avail = bip->a_end;
+    if (avail > 0) {
+        if (avail > len)
+            avail = len;
+        memcpy( dst, bip->buff, avail );
+        count += avail;
+        bip->a_start += avail;
+
+        if ( bip->a_start < bip->a_end )
+            goto Exit;
+
+        bip->a_start = bip->a_end = 0;
+    }
+
+    bip->can_read = 0;
+    ResetEvent( bip->evt_read );
+
+Exit:
+    assert( count > 0 );
+
+    if (!bip->can_write ) {
+        bip->can_write = 1;
+        SetEvent( bip->evt_write );
+    }
+
+    BIPDUMP( (const unsigned char*)dst - count, count );
+    BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n", 
+            bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+    LeaveCriticalSection( &bip->lock );
+
+    return count;
+}
+
+typedef struct SocketPairRec_ 
+{
+    BipBufferRec  a2b_bip;
+    BipBufferRec  b2a_bip;
+    FH            a_fd;
+    int           used;
+
+} SocketPairRec;
+
+void _fh_socketpair_init( FH  f )
+{
+    f->fh_pair = NULL;
+}
+
+static int
+_fh_socketpair_close( FH  f )
+{
+    if ( f->fh_pair ) {
+        SocketPair  pair = f->fh_pair;
+
+        if ( f == pair->a_fd ) {
+            pair->a_fd = NULL;
+        }
+
+        bip_buffer_close( &pair->b2a_bip );
+        bip_buffer_close( &pair->a2b_bip );
+
+        if ( --pair->used == 0 ) {
+            bip_buffer_done( &pair->b2a_bip );
+            bip_buffer_done( &pair->a2b_bip );
+            free( pair );
+        }
+        f->fh_pair = NULL;
+    }
+    return 0;
+}
+
+static int
+_fh_socketpair_lseek( FH  f, int pos, int  origin )
+{
+    errno = ESPIPE;
+    return -1;
+}
+
+static int
+_fh_socketpair_read( FH  f, void* buf, int  len )
+{
+    SocketPair  pair = f->fh_pair;
+    BipBuffer   bip;
+
+    if (!pair)
+        return -1;
+
+    if ( f == pair->a_fd )
+        bip = &pair->b2a_bip;
+    else
+        bip = &pair->a2b_bip;
+
+    return bip_buffer_read( bip, buf, len );
+}
+
+static int
+_fh_socketpair_write( FH  f, const void*  buf, int  len )
+{
+    SocketPair  pair = f->fh_pair;
+    BipBuffer   bip;
+
+    if (!pair)
+        return -1;
+
+    if ( f == pair->a_fd )
+        bip = &pair->a2b_bip;
+    else
+        bip = &pair->b2a_bip;
+
+    return bip_buffer_write( bip, buf, len );
+}
+
+
+static void  _fh_socketpair_hook( FH  f, int  event, EventHook  hook );  /* forward */
+
+static const FHClassRec  _fh_socketpair_class =
+{
+    _fh_socketpair_init,
+    _fh_socketpair_close,
+    _fh_socketpair_lseek,
+    _fh_socketpair_read,
+    _fh_socketpair_write,
+    _fh_socketpair_hook
+};
+
+
+int  adb_socketpair( int  sv[2] )
+{
+    FH          fa, fb;
+    SocketPair  pair;
+
+    fa = _fh_alloc( &_fh_socketpair_class );
+    fb = _fh_alloc( &_fh_socketpair_class );
+
+    if (!fa || !fb)
+        goto Fail;
+
+    pair = malloc( sizeof(*pair) );
+    if (pair == NULL) {
+        D("adb_socketpair: not enough memory to allocate pipes\n" );
+        goto Fail;
+    }
+
+    bip_buffer_init( &pair->a2b_bip );
+    bip_buffer_init( &pair->b2a_bip );
+
+    fa->fh_pair = pair;
+    fb->fh_pair = pair;
+    pair->used  = 2;
+    pair->a_fd  = fa;
+
+    sv[0] = _fh_to_int(fa);
+    sv[1] = _fh_to_int(fb);
+
+    pair->a2b_bip.fdin  = sv[0];
+    pair->a2b_bip.fdout = sv[1];
+    pair->b2a_bip.fdin  = sv[1];
+    pair->b2a_bip.fdout = sv[0];
+
+    snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
+    snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
+    D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+    return 0;
+
+Fail:
+    _fh_close(fb);
+    _fh_close(fa);
+    return -1;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****    fdevents emulation                                          *****/
+/*****                                                                *****/
+/*****   this is a very simple implementation, we rely on the fact    *****/
+/*****   that ADB doesn't use FDE_ERROR.                              *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+            fde->state & FDE_READ ? 'R' : ' ',
+            fde->state & FDE_WRITE ? 'W' : ' ',
+            fde->state & FDE_ERROR ? 'E' : ' ',
+            info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK  0x00ff
+#define FDE_STATEMASK  0xff00
+
+#define FDE_ACTIVE     0x0100
+#define FDE_PENDING    0x0200
+#define FDE_CREATED    0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+    .next = &list_pending,
+    .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int       fd_table_max = 0;
+
+typedef struct EventLooperRec_*  EventLooper;
+
+typedef struct EventHookRec_
+{
+    EventHook    next;
+    FH           fh;
+    HANDLE       h;
+    int          wanted;   /* wanted event flags */
+    int          ready;    /* ready event flags  */
+    void*        aux;
+    void        (*prepare)( EventHook  hook );
+    int         (*start)  ( EventHook  hook );
+    void        (*stop)   ( EventHook  hook );
+    int         (*check)  ( EventHook  hook );
+    int         (*peek)   ( EventHook  hook );
+} EventHookRec;
+
+static EventHook  _free_hooks;
+
+static EventHook
+event_hook_alloc( FH  fh )
+{
+    EventHook  hook = _free_hooks;
+    if (hook != NULL)
+        _free_hooks = hook->next;
+    else {
+        hook = malloc( sizeof(*hook) );
+        if (hook == NULL)
+            fatal( "could not allocate event hook\n" );
+    }
+    hook->next   = NULL;
+    hook->fh     = fh;
+    hook->wanted = 0;
+    hook->ready  = 0;
+    hook->h      = INVALID_HANDLE_VALUE;
+    hook->aux    = NULL;
+
+    hook->prepare = NULL;
+    hook->start   = NULL;
+    hook->stop    = NULL;
+    hook->check   = NULL;
+    hook->peek    = NULL;
+
+    return hook;
+}
+
+static void
+event_hook_free( EventHook  hook )
+{
+    hook->fh     = NULL;
+    hook->wanted = 0;
+    hook->ready  = 0;
+    hook->next   = _free_hooks;
+    _free_hooks  = hook;
+}
+
+
+static void
+event_hook_signal( EventHook  hook )
+{
+    FH        f   = hook->fh;
+    int       fd  = _fh_to_int(f);
+    fdevent*  fde = fd_table[ fd - WIN32_FH_BASE ];
+
+    if (fde != NULL && fde->fd == fd) {
+        if ((fde->state & FDE_PENDING) == 0) {
+            fde->state |= FDE_PENDING;
+            fdevent_plist_enqueue( fde );
+        }
+        fde->events |= hook->wanted;
+    }
+}
+
+
+#define  MAX_LOOPER_HANDLES  WIN32_MAX_FHS
+
+typedef struct EventLooperRec_
+{
+    EventHook    hooks;
+    HANDLE       htab[ MAX_LOOPER_HANDLES ];
+    int          htab_count;
+
+} EventLooperRec;
+
+static EventHook*
+event_looper_find_p( EventLooper  looper, FH  fh )
+{
+    EventHook  *pnode = &looper->hooks;
+    EventHook   node  = *pnode;
+    for (;;) {
+        if ( node == NULL || node->fh == fh )
+            break;
+        pnode = &node->next;
+        node  = *pnode;
+    }
+    return  pnode;
+}
+
+static void
+event_looper_hook( EventLooper  looper, int  fd, int  events )
+{
+    FH          f = _fh_from_int(fd);
+    EventHook  *pnode;
+    EventHook   node;
+
+    if (f == NULL)  /* invalid arg */ {
+        D("event_looper_hook: invalid fd=%d\n", fd);
+        return;
+    }
+
+    pnode = event_looper_find_p( looper, f );
+    node  = *pnode;
+    if ( node == NULL ) {
+        node       = event_hook_alloc( f );
+        node->next = *pnode;
+        *pnode     = node;
+    }
+
+    if ( (node->wanted & events) != events ) {
+        /* this should update start/stop/check/peek */
+        D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+           fd, node->wanted, events);
+        f->clazz->_fh_hook( f, events & ~node->wanted, node );
+        node->wanted |= events;
+    } else {
+        D("event_looper_hook: ignoring events %x for %d wanted=%x)\n", 
+           events, fd, node->wanted);
+    }
+}
+
+static void
+event_looper_unhook( EventLooper  looper, int  fd, int  events )
+{
+    FH          fh    = _fh_from_int(fd);
+    EventHook  *pnode = event_looper_find_p( looper, fh );
+    EventHook   node  = *pnode;
+
+    if (node != NULL) {
+        int  events2 = events & node->wanted;
+        if ( events2 == 0 ) {
+            D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+            return;
+        }
+        node->wanted &= ~events2;
+        if (!node->wanted) {
+            *pnode = node->next;
+            event_hook_free( node );
+        }
+    }
+}
+
+static EventLooperRec  win32_looper;
+
+static void fdevent_init(void)
+{
+    win32_looper.htab_count = 0;
+    win32_looper.hooks      = NULL;
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+    EventLooper  looper = &win32_looper;
+    int          events = fde->state & FDE_EVENTMASK;
+
+    if (events != 0)
+        event_looper_hook( looper, fde->fd, events );
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+    EventLooper  looper = &win32_looper;
+    int          events = fde->state & FDE_EVENTMASK;
+
+    if (events != 0)
+        event_looper_unhook( looper, fde->fd, events );
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+    EventLooper  looper  = &win32_looper;
+    unsigned     events0 = fde->state & FDE_EVENTMASK;
+
+    if (events != events0) {
+        int  removes = events0 & ~events;
+        int  adds    = events  & ~events0;
+        if (removes) {
+            D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+            event_looper_unhook( looper, fde->fd, removes );
+        }
+        if (adds) {
+            D("fdevent_update: add %x to %d\n", adds, fde->fd);
+            event_looper_hook  ( looper, fde->fd, adds );
+        }
+    }
+}
+
+static void fdevent_process()
+{
+    EventLooper  looper = &win32_looper;
+    EventHook    hook;
+    int          gotone = 0;
+
+    /* if we have at least one ready hook, execute it/them */
+    for (hook = looper->hooks; hook; hook = hook->next) {
+        hook->ready = 0;
+        if (hook->prepare) {
+            hook->prepare(hook);
+            if (hook->ready != 0) {
+                event_hook_signal( hook );
+                gotone = 1;
+            }
+        }
+    }
+
+    /* nothing's ready yet, so wait for something to happen */
+    if (!gotone)
+    {
+        looper->htab_count = 0;
+
+        for (hook = looper->hooks; hook; hook = hook->next) 
+        {
+            if (hook->start && !hook->start(hook)) {
+                D( "fdevent_process: error when starting a hook\n" );
+                return;
+            }
+            if (hook->h != INVALID_HANDLE_VALUE) {
+                int  nn;
+
+                for (nn = 0; nn < looper->htab_count; nn++)
+                {
+                    if ( looper->htab[nn] == hook->h )
+                        goto DontAdd;
+                }
+                looper->htab[ looper->htab_count++ ] = hook->h;
+            DontAdd:
+                ;
+            }
+        }
+
+        if (looper->htab_count == 0) {
+            D( "fdevent_process: nothing to wait for !!\n" );
+            return;
+        }
+
+        do
+        {
+            int   wait_ret;
+
+            D( "adb_win32: waiting for %d events\n", looper->htab_count );
+            if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
+                D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count);
+                abort();
+            }
+            wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
+            if (wait_ret == (int)WAIT_FAILED) {
+                D( "adb_win32: wait failed, error %ld\n", GetLastError() );
+            } else {
+                D( "adb_win32: got one (index %d)\n", wait_ret );
+
+                /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
+                 * like mouse movements. we need to filter these with the "check" function
+                 */
+                if ((unsigned)wait_ret < (unsigned)looper->htab_count)
+                {
+                    for (hook = looper->hooks; hook; hook = hook->next)
+                    {
+                        if ( looper->htab[wait_ret] == hook->h       &&
+                         (!hook->check || hook->check(hook)) )
+                        {
+                            D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+                            event_hook_signal( hook );
+                            gotone = 1;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        while (!gotone);
+
+        for (hook = looper->hooks; hook; hook = hook->next) {
+            if (hook->stop)
+                hook->stop( hook );
+        }
+    }
+
+    for (hook = looper->hooks; hook; hook = hook->next) {
+        if (hook->peek && hook->peek(hook))
+                event_hook_signal( hook );
+    }
+}
+
+
+static void fdevent_register(fdevent *fde)
+{
+    int  fd = fde->fd - WIN32_FH_BASE;
+
+    if(fd < 0) {
+        FATAL("bogus negative fd (%d)\n", fde->fd);
+    }
+
+    if(fd >= fd_table_max) {
+        int oldmax = fd_table_max;
+        if(fde->fd > 32000) {
+            FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+        }
+        if(fd_table_max == 0) {
+            fdevent_init();
+            fd_table_max = 256;
+        }
+        while(fd_table_max <= fd) {
+            fd_table_max *= 2;
+        }
+        fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+        if(fd_table == 0) {
+            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+        }
+        memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+    }
+
+    fd_table[fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+    int  fd = fde->fd - WIN32_FH_BASE;
+
+    if((fd < 0) || (fd >= fd_table_max)) {
+        FATAL("fd out of range (%d)\n", fde->fd);
+    }
+
+    if(fd_table[fd] != fde) {
+        FATAL("fd_table out of sync");
+    }
+
+    fd_table[fd] = 0;
+
+    if(!(fde->state & FDE_DONT_CLOSE)) {
+        dump_fde(fde, "close");
+        adb_close(fde->fd);
+    }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+    fdevent *list = &list_pending;
+
+    node->next = list;
+    node->prev = list->prev;
+    node->prev->next = node;
+    list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+    node->prev->next = node->next;
+    node->next->prev = node->prev;
+    node->next = 0;
+    node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+    fdevent *list = &list_pending;
+    fdevent *node = list->next;
+
+    if(node == list) return 0;
+
+    list->next = node->next;
+    list->next->prev = list;
+    node->next = 0;
+    node->prev = 0;
+
+    return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+    if(fde == 0) return 0;
+    fdevent_install(fde, fd, func, arg);
+    fde->state |= FDE_CREATED;
+    return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+    if(fde == 0) return;
+    if(!(fde->state & FDE_CREATED)) {
+        FATAL("fde %p not created by fdevent_create()\n", fde);
+    }
+    fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) 
+{
+    memset(fde, 0, sizeof(fdevent));
+    fde->state = FDE_ACTIVE;
+    fde->fd = fd;
+    fde->func = func;
+    fde->arg = arg;
+
+    fdevent_register(fde);
+    dump_fde(fde, "connect");
+    fdevent_connect(fde);
+    fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+    if(fde->state & FDE_PENDING) {
+        fdevent_plist_remove(fde);
+    }
+
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_disconnect(fde);
+        dump_fde(fde, "disconnect");    
+        fdevent_unregister(fde);
+    }
+
+    fde->state = 0;
+    fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+    events &= FDE_EVENTMASK;
+
+    if((fde->state & FDE_EVENTMASK) == (int)events) return;
+
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_update(fde, events);
+        dump_fde(fde, "update");
+    }
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+
+    if(fde->state & FDE_PENDING) {
+            /* if we're pending, make sure
+            ** we don't signal an event that
+            ** is no longer wanted.
+            */
+        fde->events &= (~events);
+        if(fde->events == 0) {
+            fdevent_plist_remove(fde);
+            fde->state &= (~FDE_PENDING);
+        }
+    }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+    fdevent *fde;
+
+    for(;;) {
+#if DEBUG
+        fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+        fdevent_process();
+
+        while((fde = fdevent_plist_dequeue())) {
+            unsigned events = fde->events;
+            fde->events = 0;
+            fde->state &= (~FDE_PENDING);
+            dump_fde(fde, "callback");
+            fde->func(fde->fd, events, fde->arg);
+        }
+    }
+}
+
+/**  FILE EVENT HOOKS
+ **/
+ 
+static void  _event_file_prepare( EventHook  hook )
+{
+    if (hook->wanted & (FDE_READ|FDE_WRITE)) {
+        /* we can always read/write */
+        hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
+    }
+}
+
+static int  _event_file_peek( EventHook  hook )
+{
+    return (hook->wanted & (FDE_READ|FDE_WRITE));
+}
+
+static void  _fh_file_hook( FH  f, int  events, EventHook  hook )
+{
+    hook->h       = f->fh_handle;
+    hook->prepare = _event_file_prepare;
+    hook->peek    = _event_file_peek;
+}
+
+/** SOCKET EVENT HOOKS
+ **/
+
+static void  _event_socket_verify( EventHook  hook, WSANETWORKEVENTS*  evts )
+{
+    if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
+        if (hook->wanted & FDE_READ)
+            hook->ready |= FDE_READ;
+        if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
+            hook->ready |= FDE_ERROR;
+    }
+    if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
+        if (hook->wanted & FDE_WRITE)
+            hook->ready |= FDE_WRITE;
+        if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
+            hook->ready |= FDE_ERROR;
+    }
+    if ( evts->lNetworkEvents & FD_OOB ) {
+        if (hook->wanted & FDE_ERROR)
+            hook->ready |= FDE_ERROR;
+    }
+}
+
+static void  _event_socket_prepare( EventHook  hook )
+{
+    WSANETWORKEVENTS  evts;
+
+    /* look if some of the events we want already happened ? */
+    if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
+        _event_socket_verify( hook, &evts );
+}
+
+static int  _socket_wanted_to_flags( int  wanted )
+{
+    int  flags = 0;
+    if (wanted & FDE_READ)
+        flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+    if (wanted & FDE_WRITE)
+        flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+    if (wanted & FDE_ERROR)
+        flags |= FD_OOB;
+
+    return flags;
+}
+
+static int _event_socket_start( EventHook  hook )
+{
+    /* create an event which we're going to wait for */
+    FH    fh    = hook->fh;
+    long  flags = _socket_wanted_to_flags( hook->wanted );
+
+    hook->h = fh->event;
+    if (hook->h == INVALID_HANDLE_VALUE) {
+        D( "_event_socket_start: no event for %s\n", fh->name );
+        return 0;
+    }
+
+    if ( flags != fh->mask ) {
+        D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+        if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
+            D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+            CloseHandle( hook->h );
+            hook->h = INVALID_HANDLE_VALUE;
+            exit(1);
+            return 0;
+        }
+        fh->mask = flags;
+    }
+    return 1;
+}
+
+static void _event_socket_stop( EventHook  hook )
+{
+    hook->h = INVALID_HANDLE_VALUE;
+}
+
+static int  _event_socket_check( EventHook  hook )
+{
+    int               result = 0;
+    FH                fh = hook->fh;
+    WSANETWORKEVENTS  evts;
+
+    if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
+        _event_socket_verify( hook, &evts );
+        result = (hook->ready != 0);
+        if (result) {
+            ResetEvent( hook->h );
+        }
+    }
+    D( "_event_socket_check %s returns %d\n", fh->name, result );
+    return  result;
+}
+
+static int  _event_socket_peek( EventHook  hook )
+{
+    WSANETWORKEVENTS  evts;
+    FH                fh = hook->fh;
+
+    /* look if some of the events we want already happened ? */
+    if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
+        _event_socket_verify( hook, &evts );
+        if (hook->ready)
+            ResetEvent( hook->h );
+    }
+
+    return hook->ready != 0;
+}
+
+
+
+static void  _fh_socket_hook( FH  f, int  events, EventHook  hook )
+{
+    hook->prepare = _event_socket_prepare;
+    hook->start   = _event_socket_start;
+    hook->stop    = _event_socket_stop;
+    hook->check   = _event_socket_check;
+    hook->peek    = _event_socket_peek;
+
+    _event_socket_start( hook );
+}
+
+/** SOCKETPAIR EVENT HOOKS
+ **/
+
+static void  _event_socketpair_prepare( EventHook  hook )
+{
+    FH          fh   = hook->fh;
+    SocketPair  pair = fh->fh_pair;
+    BipBuffer   rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+    BipBuffer   wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+    if (hook->wanted & FDE_READ && rbip->can_read)
+        hook->ready |= FDE_READ;
+
+    if (hook->wanted & FDE_WRITE && wbip->can_write) 
+        hook->ready |= FDE_WRITE;
+ }
+
+ static int  _event_socketpair_start( EventHook  hook )
+ {
+    FH          fh   = hook->fh;
+    SocketPair  pair = fh->fh_pair;
+    BipBuffer   rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+    BipBuffer   wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+    if (hook->wanted == FDE_READ)
+        hook->h = rbip->evt_read;
+
+    else if (hook->wanted == FDE_WRITE)
+        hook->h = wbip->evt_write;
+
+    else {
+        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+        return 0;
+    }
+    D( "_event_socketpair_start: hook %s for %x wanted=%x\n", 
+       hook->fh->name, _fh_to_int(fh), hook->wanted);
+    return 1;
+}
+
+static int  _event_socketpair_peek( EventHook  hook )
+{
+    _event_socketpair_prepare( hook );
+    return hook->ready != 0;
+}
+
+static void  _fh_socketpair_hook( FH  fh, int  events, EventHook  hook )
+{
+    hook->prepare = _event_socketpair_prepare;
+    hook->start   = _event_socketpair_start;
+    hook->peek    = _event_socketpair_peek;
+}
+
+
+void
+adb_sysdeps_init( void )
+{
+#define  ADB_MUTEX(x)  InitializeCriticalSection( & x );
+#include "mutex_list.h"
+    InitializeCriticalSection( &_win32_lock );
+}
+
diff --git a/adb/test_track_devices.c b/adb/test_track_devices.c
new file mode 100644
index 0000000..77b3ad9
--- /dev/null
+++ b/adb/test_track_devices.c
@@ -0,0 +1,97 @@
+/* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+
+static void
+panic( const char*  msg )
+{
+    fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
+    exit(1);
+}
+
+static int
+unix_write( int  fd, const char*  buf, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int  len2 = write(fd, buf, len);
+        if (len2 < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            return -1;
+        }
+        result += len2;
+        len -= len2;
+        buf += len2;
+    }
+    return  result;
+}
+
+static int
+unix_read( int  fd, char*  buf, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int  len2 = read(fd, buf, len);
+        if (len2 < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            return -1;
+        }
+        result += len2;
+        len -= len2;
+        buf += len2;
+    }
+    return  result;
+}
+
+
+int  main( void )
+{
+    int                  ret, s;
+    struct sockaddr_in   server;
+    char                 buffer[1024];
+    const char*          request = "host:track-devices";
+    int                  len;
+
+    memset( &server, 0, sizeof(server) );
+    server.sin_family      = AF_INET;
+    server.sin_port        = htons(5037);
+    server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket( PF_INET, SOCK_STREAM, 0 );
+    ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
+    if (ret < 0) panic( "could not connect to server" );
+
+    /* send the request */
+    len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
+    if (unix_write(s, buffer, len) < 0)
+        panic( "could not send request" );
+
+    /* read the OKAY answer */
+    if (unix_read(s, buffer, 4) != 4)
+        panic( "could not read request" );
+
+    printf( "server answer: %.*s\n", 4, buffer );
+
+    /* now loop */
+    for (;;) {
+        char  head[5] = "0000";
+
+        if (unix_read(s, head, 4) < 0)
+            panic("could not read length");
+
+        if ( sscanf( head, "%04x", &len ) != 1 )
+            panic("could not decode length");
+
+        if (unix_read(s, buffer, len) != len)
+            panic("could not read data");
+
+        printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+    }
+    close(s);
+}
diff --git a/adb/test_track_jdwp.c b/adb/test_track_jdwp.c
new file mode 100644
index 0000000..8ecc6b8
--- /dev/null
+++ b/adb/test_track_jdwp.c
@@ -0,0 +1,97 @@
+/* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <netdb.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+
+static void
+panic( const char*  msg )
+{
+    fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
+    exit(1);
+}
+
+static int
+unix_write( int  fd, const char*  buf, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int  len2 = write(fd, buf, len);
+        if (len2 < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            return -1;
+        }
+        result += len2;
+        len -= len2;
+        buf += len2;
+    }
+    return  result;
+}
+
+static int
+unix_read( int  fd, char*  buf, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int  len2 = read(fd, buf, len);
+        if (len2 < 0) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            return -1;
+        }
+        result += len2;
+        len -= len2;
+        buf += len2;
+    }
+    return  result;
+}
+
+
+int  main( void )
+{
+    int                  ret, s;
+    struct sockaddr_in   server;
+    char                 buffer[1024];
+    const char*          request = "track-jdwp";
+    int                  len;
+
+    memset( &server, 0, sizeof(server) );
+    server.sin_family      = AF_INET;
+    server.sin_port        = htons(5037);
+    server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket( PF_INET, SOCK_STREAM, 0 );
+    ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
+    if (ret < 0) panic( "could not connect to server" );
+
+    /* send the request */
+    len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
+    if (unix_write(s, buffer, len) < 0)
+        panic( "could not send request" );
+
+    /* read the OKAY answer */
+    if (unix_read(s, buffer, 4) != 4)
+        panic( "could not read request" );
+
+    printf( "server answer: %.*s\n", 4, buffer );
+
+    /* now loop */
+    for (;;) {
+        char  head[5] = "0000";
+
+        if (unix_read(s, head, 4) < 0)
+            panic("could not read length");
+
+        if ( sscanf( head, "%04x", &len ) != 1 )
+            panic("could not decode length");
+
+        if (unix_read(s, buffer, len) != len)
+            panic("could not read data");
+
+        printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+    }
+    close(s);
+}
diff --git a/adb/transport.c b/adb/transport.c
new file mode 100644
index 0000000..c76f1a5
--- /dev/null
+++ b/adb/transport.c
@@ -0,0 +1,958 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_TRANSPORT
+#include "adb.h"
+
+static void transport_unref(atransport *t);
+
+static atransport transport_list = {
+    .next = &transport_list,
+    .prev = &transport_list,
+};
+
+ADB_MUTEX_DEFINE( transport_lock );
+
+#if ADB_TRACE
+static void  dump_hex( const unsigned char*  ptr, size_t  len )
+{
+    int  nn, len2 = len;
+
+    if (len2 > 16) len2 = 16;
+
+    for (nn = 0; nn < len2; nn++)
+        D("%02x", ptr[nn]);
+    D("  ");
+
+    for (nn = 0; nn < len2; nn++) {
+        int  c = ptr[nn];
+        if (c < 32 || c > 127)
+            c = '.';
+        D("%c", c);
+    }
+    D("\n");
+    fflush(stdout);
+}
+#endif
+
+void
+kick_transport(atransport*  t)
+{
+    if (t && !t->kicked)
+    {
+        int  kicked;
+
+        adb_mutex_lock(&transport_lock);
+        kicked = t->kicked;
+        if (!kicked)
+            t->kicked = 1;
+        adb_mutex_unlock(&transport_lock);
+
+        if (!kicked)
+            t->kick(t);
+    }
+}
+
+void
+run_transport_disconnects(atransport*  t)
+{
+    adisconnect*  dis = t->disconnects.next;
+
+    D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" );
+    while (dis != &t->disconnects) {
+        adisconnect*  next = dis->next;
+        dis->func( dis->opaque, t );
+        dis = next;
+    }
+}
+
+static int
+read_packet(int  fd, apacket** ppacket)
+{
+    char *p = (char*)ppacket;  /* really read a packet address */
+    int   r;
+    int   len = sizeof(*ppacket);
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            D("read_packet: %d error %d %d\n", fd, r, errno);
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+
+#if ADB_TRACE
+    if (ADB_TRACING)
+    {
+        unsigned  command = (*ppacket)->msg.command;
+        int       len     = (*ppacket)->msg.data_length;
+        char      cmd[5];
+        int       n;
+
+        for (n = 0; n < 4; n++) {
+            int  b = (command >> (n*8)) & 255;
+            if (b >= 32 && b < 127)
+                cmd[n] = (char)b;
+            else
+                cmd[n] = '.';
+        }
+        cmd[4] = 0;
+
+        D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ",
+          fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
+        dump_hex((*ppacket)->data, len);
+    }
+#endif
+    return 0;
+}
+
+static int
+write_packet(int  fd, apacket** ppacket)
+{
+    char *p = (char*) ppacket;  /* we really write the packet address */
+    int r, len = sizeof(ppacket);
+
+#if ADB_TRACE
+    if (ADB_TRACING)
+    {
+        unsigned  command = (*ppacket)->msg.command;
+        int       len     = (*ppacket)->msg.data_length;
+        char      cmd[5];
+        int       n;
+
+        for (n = 0; n < 4; n++) {
+            int  b = (command >> (n*8)) & 255;
+            if (b >= 32 && b < 127)
+                cmd[n] = (char)b;
+            else
+                cmd[n] = '.';
+        }
+        cmd[4] = 0;
+
+        D("write_packet: %d [%08x %s] %08x %08x (%d) ",
+          fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
+        dump_hex((*ppacket)->data, len);
+    }
+#endif
+    len = sizeof(ppacket);
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            D("write_packet: %d error %d %d\n", fd, r, errno);
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void transport_socket_events(int fd, unsigned events, void *_t)
+{
+    if(events & FDE_READ){
+        apacket *p = 0;
+        if(read_packet(fd, &p)){
+            D("failed to read packet from transport socket on fd %d\n", fd);
+        } else {
+            handle_packet(p, (atransport *) _t);
+        }
+    }
+}
+
+void send_packet(apacket *p, atransport *t)
+{
+    unsigned char *x;
+    unsigned sum;
+    unsigned count;
+
+    p->msg.magic = p->msg.command ^ 0xffffffff;
+
+    count = p->msg.data_length;
+    x = (unsigned char *) p->data;
+    sum = 0;
+    while(count-- > 0){
+        sum += *x++;
+    }
+    p->msg.data_check = sum;
+
+    print_packet("send", p);
+
+    if (t == NULL) {
+        fatal_errno("Transport is null");
+        D("Transport is null \n");
+    }
+
+    if(write_packet(t->transport_socket, &p)){
+        fatal_errno("cannot enqueue packet on transport socket");
+    }
+}
+
+/* The transport is opened by transport_register_func before
+** the input and output threads are started.
+**
+** The output thread issues a SYNC(1, token) message to let
+** the input thread know to start things up.  In the event
+** of transport IO failure, the output thread will post a
+** SYNC(0,0) message to ensure shutdown.
+**
+** The transport will not actually be closed until both
+** threads exit, but the input thread will kick the transport
+** on its way out to disconnect the underlying device.
+*/
+
+static void *output_thread(void *_t)
+{
+    atransport *t = _t;
+    apacket *p;
+
+    D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd );
+
+    D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1);
+    p = get_apacket();
+    p->msg.command = A_SYNC;
+    p->msg.arg0 = 1;
+    p->msg.arg1 = ++(t->sync_token);
+    p->msg.magic = A_SYNC ^ 0xffffffff;
+    if(write_packet(t->fd, &p)) {
+        put_apacket(p);
+        D("from_remote: failed to write SYNC apacket to transport %p", t);
+        goto oops;
+    }
+
+    D("from_remote: data pump  for transport %p\n", t);
+    for(;;) {
+        p = get_apacket();
+
+        if(t->read_from_remote(p, t) == 0){
+            D("from_remote: received remote packet, sending to transport %p\n",
+              t);
+            if(write_packet(t->fd, &p)){
+                put_apacket(p);
+                D("from_remote: failed to write apacket to transport %p", t);
+                goto oops;
+            }
+        } else {
+            D("from_remote: remote read failed for transport %p\n", p);
+            put_apacket(p);
+            break;
+        }
+    }
+
+    D("from_remote: SYNC offline for transport %p\n", t);
+    p = get_apacket();
+    p->msg.command = A_SYNC;
+    p->msg.arg0 = 0;
+    p->msg.arg1 = 0;
+    p->msg.magic = A_SYNC ^ 0xffffffff;
+    if(write_packet(t->fd, &p)) {
+        put_apacket(p);
+        D("from_remote: failed to write SYNC apacket to transport %p", t);
+    }
+
+oops:
+    D("from_remote: thread is exiting for transport %p\n", t);
+    kick_transport(t);
+    transport_unref(t);
+    return 0;
+}
+
+static void *input_thread(void *_t)
+{
+    atransport *t = _t;
+    apacket *p;
+    int active = 0;
+
+    D("to_remote: starting input_thread for %p, reading from fd %d\n",
+       t, t->fd);
+
+    for(;;){
+        if(read_packet(t->fd, &p)) {
+            D("to_remote: failed to read apacket from transport %p on fd %d\n", 
+               t, t->fd );
+            break;
+        }
+        if(p->msg.command == A_SYNC){
+            if(p->msg.arg0 == 0) {
+                D("to_remote: transport %p SYNC offline\n", t);
+                put_apacket(p);
+                break;
+            } else {
+                if(p->msg.arg1 == t->sync_token) {
+                    D("to_remote: transport %p SYNC online\n", t);
+                    active = 1;
+                } else {
+                    D("to_remote: trandport %p ignoring SYNC %d != %d\n",
+                      t, p->msg.arg1, t->sync_token);
+                }
+            }
+        } else {
+            if(active) {
+                D("to_remote: transport %p got packet, sending to remote\n", t);
+                t->write_to_remote(p, t);
+            } else {
+                D("to_remote: transport %p ignoring packet while offline\n", t);
+            }
+        }
+
+        put_apacket(p);
+    }
+
+    // this is necessary to avoid a race condition that occured when a transport closes
+    // while a client socket is still active.
+    close_all_sockets(t);
+
+    D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd);
+    kick_transport(t);
+    transport_unref(t);
+    return 0;
+}
+
+
+static int transport_registration_send = -1;
+static int transport_registration_recv = -1;
+static fdevent transport_registration_fde;
+
+
+#if ADB_HOST
+static int list_transports_msg(char*  buffer, size_t  bufferlen)
+{
+    char  head[5];
+    int   len;
+
+    len = list_transports(buffer+4, bufferlen-4);
+    snprintf(head, sizeof(head), "%04x", len);
+    memcpy(buffer, head, 4);
+    len += 4;
+    return len;
+}
+
+/* this adds support required by the 'track-devices' service.
+ * this is used to send the content of "list_transport" to any
+ * number of client connections that want it through a single
+ * live TCP connection
+ */
+typedef struct device_tracker  device_tracker;
+struct device_tracker {
+    asocket          socket;
+    int              update_needed;
+    device_tracker*  next;
+};
+
+/* linked list of all device trackers */
+static device_tracker*   device_tracker_list;
+
+static void
+device_tracker_remove( device_tracker*  tracker )
+{
+    device_tracker**  pnode = &device_tracker_list;
+    device_tracker*   node  = *pnode;
+
+    adb_mutex_lock( &transport_lock );
+    while (node) {
+        if (node == tracker) {
+            *pnode = node->next;
+            break;
+        }
+        pnode = &node->next;
+        node  = *pnode;
+    }
+    adb_mutex_unlock( &transport_lock );
+}
+
+static void
+device_tracker_close( asocket*  socket )
+{
+    device_tracker*  tracker = (device_tracker*) socket;
+    asocket*         peer    = socket->peer;
+
+    D( "device tracker %p removed\n", tracker);
+    if (peer) {
+        peer->peer = NULL;
+        peer->close(peer);
+    }
+    device_tracker_remove(tracker);
+    free(tracker);
+}
+
+static int
+device_tracker_enqueue( asocket*  socket, apacket*  p )
+{
+    /* you can't read from a device tracker, close immediately */
+    put_apacket(p);
+    device_tracker_close(socket);
+    return -1;
+}
+
+static int
+device_tracker_send( device_tracker*  tracker,
+                     const char*      buffer,
+                     int              len )
+{
+    apacket*  p = get_apacket();
+    asocket*  peer = tracker->socket.peer;
+
+    memcpy(p->data, buffer, len);
+    p->len = len;
+    return peer->enqueue( peer, p );
+}
+
+
+static void
+device_tracker_ready( asocket*  socket )
+{
+    device_tracker*  tracker = (device_tracker*) socket;
+
+    /* we want to send the device list when the tracker connects
+    * for the first time, even if no update occured */
+    if (tracker->update_needed > 0) {
+        char  buffer[1024];
+        int   len;
+
+        tracker->update_needed = 0;
+
+        len = list_transports_msg(buffer, sizeof(buffer));
+        device_tracker_send(tracker, buffer, len);
+    }
+}
+
+
+asocket*
+create_device_tracker(void)
+{
+    device_tracker*  tracker = calloc(1,sizeof(*tracker));
+
+    if(tracker == 0) fatal("cannot allocate device tracker");
+
+    D( "device tracker %p created\n", tracker);
+
+    tracker->socket.enqueue = device_tracker_enqueue;
+    tracker->socket.ready   = device_tracker_ready;
+    tracker->socket.close   = device_tracker_close;
+    tracker->update_needed  = 1;
+
+    tracker->next       = device_tracker_list;
+    device_tracker_list = tracker;
+
+    return &tracker->socket;
+}
+
+
+/* call this function each time the transport list has changed */
+void  update_transports(void)
+{
+    char             buffer[1024];
+    int              len;
+    device_tracker*  tracker;
+
+    len = list_transports_msg(buffer, sizeof(buffer));
+
+    tracker = device_tracker_list;
+    while (tracker != NULL) {
+        device_tracker*  next = tracker->next;
+        /* note: this may destroy the tracker if the connection is closed */
+        device_tracker_send(tracker, buffer, len);
+        tracker = next;
+    }
+}
+#else
+void  update_transports(void)
+{
+    // nothing to do on the device side
+}
+#endif // ADB_HOST
+
+typedef struct tmsg tmsg;
+struct tmsg
+{
+    atransport *transport;
+    int         action;
+};
+
+static int
+transport_read_action(int  fd, struct tmsg*  m)
+{
+    char *p   = (char*)m;
+    int   len = sizeof(*m);
+    int   r;
+
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            if((r < 0) && (errno == EINTR)) continue;
+            D("transport_read_action: on fd %d, error %d: %s\n", 
+              fd, errno, strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+transport_write_action(int  fd, struct tmsg*  m)
+{
+    char *p   = (char*)m;
+    int   len = sizeof(*m);
+    int   r;
+
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p   += r;
+        } else {
+            if((r < 0) && (errno == EINTR)) continue;
+            D("transport_write_action: on fd %d, error %d: %s\n", 
+              fd, errno, strerror(errno));
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void transport_registration_func(int _fd, unsigned ev, void *data)
+{
+    tmsg m;
+    adb_thread_t output_thread_ptr;
+    adb_thread_t input_thread_ptr;
+    int s[2];
+    atransport *t;
+
+    if(!(ev & FDE_READ)) {
+        return;
+    }
+
+    if(transport_read_action(_fd, &m)) {
+        fatal_errno("cannot read transport registration socket");
+    }
+
+    t = m.transport;
+
+    if(m.action == 0){
+        D("transport: %p removing and free'ing %d\n", t, t->transport_socket);
+
+            /* IMPORTANT: the remove closes one half of the
+            ** socket pair.  The close closes the other half.
+            */
+        fdevent_remove(&(t->transport_fde));
+        adb_close(t->fd);
+
+        adb_mutex_lock(&transport_lock);
+        t->next->prev = t->prev;
+        t->prev->next = t->next;
+        adb_mutex_unlock(&transport_lock);
+
+        run_transport_disconnects(t);
+
+        if (t->product)
+            free(t->product);
+        if (t->serial)
+            free(t->serial);
+
+        memset(t,0xee,sizeof(atransport));
+        free(t);
+
+        update_transports();
+        return;
+    }
+
+        /* initial references are the two threads */
+    t->ref_count = 2;
+
+    if(adb_socketpair(s)) {
+        fatal_errno("cannot open transport socketpair");
+    }
+
+    D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
+
+    t->transport_socket = s[0];
+    t->fd = s[1];
+
+        /* put us on the master device list */
+    adb_mutex_lock(&transport_lock);
+    t->next = &transport_list;
+    t->prev = transport_list.prev;
+    t->next->prev = t;
+    t->prev->next = t;
+    adb_mutex_unlock(&transport_lock);
+
+    D("transport: %p install %d\n", t, t->transport_socket );
+    fdevent_install(&(t->transport_fde),
+                    t->transport_socket,
+                    transport_socket_events,
+                    t);
+
+    fdevent_set(&(t->transport_fde), FDE_READ);
+
+    if(adb_thread_create(&input_thread_ptr, input_thread, t)){
+        fatal_errno("cannot create input thread");
+    }
+
+    if(adb_thread_create(&output_thread_ptr, output_thread, t)){
+        fatal_errno("cannot create output thread");
+    }
+
+    t->disconnects.next = t->disconnects.prev = &t->disconnects;
+
+    update_transports();
+}
+
+void init_transport_registration(void)
+{
+    int s[2];
+
+    if(adb_socketpair(s)){
+        fatal_errno("cannot open transport registration socketpair");
+    }
+
+    transport_registration_send = s[0];
+    transport_registration_recv = s[1];
+
+    fdevent_install(&transport_registration_fde,
+                    transport_registration_recv,
+                    transport_registration_func,
+                    0);
+
+    fdevent_set(&transport_registration_fde, FDE_READ);
+}
+
+/* the fdevent select pump is single threaded */
+static void register_transport(atransport *transport)
+{
+    tmsg m;
+    m.transport = transport;
+    m.action = 1;
+    D("transport: %p registered\n", transport);
+    if(transport_write_action(transport_registration_send, &m)) {
+        fatal_errno("cannot write transport registration socket\n");
+    }
+}
+
+static void remove_transport(atransport *transport)
+{
+    tmsg m;
+    m.transport = transport;
+    m.action = 0;
+    D("transport: %p removed\n", transport);
+    if(transport_write_action(transport_registration_send, &m)) {
+        fatal_errno("cannot write transport registration socket\n");
+    }
+}
+
+
+static void transport_unref(atransport *t)
+{
+    if (t) {
+        adb_mutex_lock(&transport_lock);
+        t->ref_count--;
+        D("transport: %p R- (ref=%d)\n", t, t->ref_count);
+        if (t->ref_count == 0) {
+            D("transport: %p kicking and closing\n", t);
+            if (!t->kicked) {
+                t->kicked = 1;
+                t->kick(t);
+            }
+            t->close(t);
+            remove_transport(t);
+        }
+        adb_mutex_unlock(&transport_lock);
+    }
+}
+
+void add_transport_disconnect(atransport*  t, adisconnect*  dis)
+{
+    adb_mutex_lock(&transport_lock);
+    dis->next       = &t->disconnects;
+    dis->prev       = dis->next->prev;
+    dis->prev->next = dis;
+    dis->next->prev = dis;
+    adb_mutex_unlock(&transport_lock);
+}
+
+void remove_transport_disconnect(atransport*  t, adisconnect*  dis)
+{
+    dis->prev->next = dis->next;
+    dis->next->prev = dis->prev;
+    dis->next = dis->prev = dis;
+}
+
+
+atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
+{
+    atransport *t;
+    atransport *result = NULL;
+    int ambiguous = 0;
+
+retry:
+    if (error_out)
+        *error_out = "device not found";
+
+    adb_mutex_lock(&transport_lock);
+    for (t = transport_list.next; t != &transport_list; t = t->next) {
+        /* check for matching serial number */
+        if (serial) {
+            if (t->serial && !strcmp(serial, t->serial)) {
+                result = t;
+                break;
+            }
+        } else {
+            if (ttype == kTransportUsb && t->type == kTransportUsb) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one device";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one emulator";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            } else if (ttype == kTransportAny) {
+                if (result) {
+                    if (error_out)
+                        *error_out = "more than one device and emulator";
+                    ambiguous = 1;
+                    result = NULL;
+                    break;
+                }
+                result = t;
+            }
+        }
+    }
+    adb_mutex_unlock(&transport_lock);
+
+    if (result) {
+         /* offline devices are ignored -- they are either being born or dying */
+        if (result && result->connection_state == CS_OFFLINE) {
+            if (error_out)
+                *error_out = "device offline";
+            result = NULL;
+        }
+
+         /* check for required connection state */
+        if (result && state != CS_ANY && result->connection_state != state) {
+            if (error_out)
+                *error_out = "invalid device state";
+            result = NULL;
+        }
+    }
+
+    if (result) {
+        /* found one that we can take */
+        if (error_out)
+            *error_out = NULL;
+    } else if (state != CS_ANY && (serial || !ambiguous)) {
+        adb_sleep_ms(1000);
+        goto retry;
+    }
+
+    return result;
+}
+
+#if ADB_HOST
+static const char *statename(atransport *t)
+{
+    switch(t->connection_state){
+    case CS_OFFLINE: return "offline";
+    case CS_BOOTLOADER: return "bootloader";
+    case CS_DEVICE: return "device";
+    case CS_HOST: return "host";
+    case CS_RECOVERY: return "recovery";
+    default: return "unknown";
+    }
+}
+
+int list_transports(char *buf, size_t  bufsize)
+{
+    char*       p   = buf;
+    char*       end = buf + bufsize;
+    int         len;
+    atransport *t;
+
+        /* XXX OVERRUN PROBLEMS XXX */
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        len = snprintf(p, end - p, "%s\t%s\n",
+                t->serial ? t->serial : "",
+                statename(t));
+
+        if (p + len >= end) {
+            /* discard last line if buffer is too short */
+            break;
+        }
+        p += len;
+    }
+    p[0] = 0;
+    adb_mutex_unlock(&transport_lock);
+    return p - buf;
+}
+
+
+/* hack for osx */
+void close_usb_devices()
+{
+    atransport *t;
+
+    adb_mutex_lock(&transport_lock);
+    for(t = transport_list.next; t != &transport_list; t = t->next) {
+        if ( !t->kicked ) {
+            t->kicked = 1;
+            t->kick(t);
+        }
+    }
+    adb_mutex_unlock(&transport_lock);
+}
+#endif // ADB_HOST
+
+void register_socket_transport(int s, const char *serial, int  port)
+{
+    atransport *t = calloc(1, sizeof(atransport));
+    D("transport: %p init'ing for socket %d, on port %d\n", t, s, port);
+    if ( init_socket_transport(t, s, port) < 0 ) {
+        adb_close(s);
+        free(t);
+        return;
+    }
+    if(serial) {
+        t->serial = strdup(serial);
+    }
+    register_transport(t);
+}
+
+void register_usb_transport(usb_handle *usb, const char *serial)
+{
+    atransport *t = calloc(1, sizeof(atransport));
+    D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+      serial ? serial : "");
+    init_usb_transport(t, usb);
+    if(serial) {
+        t->serial = strdup(serial);
+    }
+    register_transport(t);
+}
+
+
+#undef TRACE_TAG
+#define TRACE_TAG  TRACE_RWX
+
+int readx(int fd, void *ptr, size_t len)
+{
+    char *p = ptr;
+    int r;
+#if ADB_TRACE
+    int  len0 = len;
+#endif
+    D("readx: %d %p %d\n", fd, ptr, (int)len);
+    while(len > 0) {
+        r = adb_read(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            D("readx: %d %d %s\n", fd, r, strerror(errno));
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+
+#if ADB_TRACE
+    D("readx: %d ok: ", fd);
+    dump_hex( ptr, len0 );
+#endif
+    return 0;
+}
+
+int writex(int fd, const void *ptr, size_t len)
+{
+    char *p = (char*) ptr;
+    int r;
+
+#if ADB_TRACE
+    D("writex: %d %p %d: ", fd, ptr, (int)len);
+    dump_hex( ptr, len );
+#endif
+    while(len > 0) {
+        r = adb_write(fd, p, len);
+        if(r > 0) {
+            len -= r;
+            p += r;
+        } else {
+            D("writex: %d %d %s\n", fd, r, strerror(errno));
+            if((r < 0) && (errno == EINTR)) continue;
+            return -1;
+        }
+    }
+
+    D("writex: %d ok\n", fd);
+    return 0;
+}
+
+int check_header(apacket *p)
+{
+    if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+        D("check_header(): invalid magic\n");
+        return -1;
+    }
+
+    if(p->msg.data_length > MAX_PAYLOAD) {
+        D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+        return -1;
+    }
+
+    return 0;
+}
+
+int check_data(apacket *p)
+{
+    unsigned count, sum;
+    unsigned char *x;
+
+    count = p->msg.data_length;
+    x = p->data;
+    sum = 0;
+    while(count-- > 0) {
+        sum += *x++;
+    }
+
+    if(sum != p->msg.data_check) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
diff --git a/adb/transport_local.c b/adb/transport_local.c
new file mode 100644
index 0000000..be01f29
--- /dev/null
+++ b/adb/transport_local.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+#include <sys/types.h>
+
+#define  TRACE_TAG  TRACE_TRANSPORT
+#include "adb.h"
+
+#ifdef __ppc__
+#define H4(x)	(((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
+static inline void fix_endians(apacket *p)
+{
+    p->msg.command     = H4(p->msg.command);
+    p->msg.arg0        = H4(p->msg.arg0);
+    p->msg.arg1        = H4(p->msg.arg1);
+    p->msg.data_length = H4(p->msg.data_length);
+    p->msg.data_check  = H4(p->msg.data_check);
+    p->msg.magic       = H4(p->msg.magic);
+}
+#else
+#define fix_endians(p) do {} while (0)
+#endif
+
+#if ADB_HOST
+/* we keep a list of opened transports, transport 0 is bound to 5555,
+ * transport 1 to 5557, .. transport n to 5555 + n*2. the list is used
+ * to detect when we're trying to connect twice to a given local transport
+ */
+#define  ADB_LOCAL_TRANSPORT_MAX  16
+
+ADB_MUTEX_DEFINE( local_transports_lock );
+
+static atransport*  local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
+#endif /* ADB_HOST */
+
+static int remote_read(apacket *p, atransport *t)
+{
+    if(readx(t->sfd, &p->msg, sizeof(amessage))){
+        D("remote local: read terminated (message)\n");
+        return -1;
+    }
+
+    fix_endians(p);
+
+#if 0 && defined __ppc__
+    D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
+      p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
+#endif
+    if(check_header(p)) {
+        D("bad header: terminated (data)\n");
+        return -1;
+    }
+
+    if(readx(t->sfd, p->data, p->msg.data_length)){
+        D("remote local: terminated (data)\n");
+        return -1;
+    }
+
+    if(check_data(p)) {
+        D("bad data: terminated (data)\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+    int   length = p->msg.data_length;
+
+    fix_endians(p);
+
+#if 0 && defined __ppc__
+    D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
+      p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
+#endif
+    if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) {
+        D("remote local: write terminated\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+int  local_connect(int  port)
+{
+    char buf[64];
+    int  fd = -1;
+
+#if ADB_HOST
+    const char *host = getenv("ADBHOST");
+    if (host) {
+        fd = socket_network_client(host, port, SOCK_STREAM);
+    }
+#endif
+    if (fd < 0) {
+        fd = socket_loopback_client(port, SOCK_STREAM);
+    }
+
+    if (fd >= 0) {
+        D("client: connected on remote on fd %d\n", fd);
+        close_on_exec(fd);
+        disable_tcp_nagle(fd);
+        snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1);
+        register_socket_transport(fd, buf, port);
+        return 0;
+    }
+    return -1;
+}
+
+
+static void *client_socket_thread(void *x)
+{
+#if ADB_HOST
+    int  port  = ADB_LOCAL_TRANSPORT_PORT;
+    int  count = ADB_LOCAL_TRANSPORT_MAX;
+
+    D("transport: client_socket_thread() starting\n");
+
+    /* try to connect to any number of running emulator instances     */
+    /* this is only done when ADB starts up. later, each new emulator */
+    /* will send a message to ADB to indicate that is is starting up  */
+    for ( ; count > 0; count--, port += 2 ) {
+        (void) local_connect(port);
+    }
+#endif
+    return 0;
+}
+
+static void *server_socket_thread(void *x)
+{
+    int serverfd, fd;
+    struct sockaddr addr;
+    socklen_t alen;
+
+    D("transport: server_socket_thread() starting\n");
+    serverfd = -1;
+    for(;;) {
+        if(serverfd == -1) {
+            serverfd = socket_inaddr_any_server(ADB_LOCAL_TRANSPORT_PORT, SOCK_STREAM);
+            if(serverfd < 0) {
+                D("server: cannot bind socket yet\n");
+                adb_sleep_ms(1000);
+                continue;
+            }
+            close_on_exec(serverfd);
+        }
+
+        alen = sizeof(addr);
+        D("server: trying to get new connection from %d\n", ADB_LOCAL_TRANSPORT_PORT);
+        fd = adb_socket_accept(serverfd, &addr, &alen);
+        if(fd >= 0) {
+            D("server: new connection on fd %d\n", fd);
+            close_on_exec(fd);
+            disable_tcp_nagle(fd);
+            register_socket_transport(fd,"host",ADB_LOCAL_TRANSPORT_PORT);
+        }
+    }
+    D("transport: server_socket_thread() exiting\n");
+    return 0;
+}
+
+void local_init(void)
+{
+    adb_thread_t thr;
+    void* (*func)(void *);
+
+    if(HOST) {
+        func = client_socket_thread;
+    } else {
+        func = server_socket_thread;
+    }
+
+    D("transport: local %s init\n", HOST ? "client" : "server");
+
+    if(adb_thread_create(&thr, func, 0)) {
+        fatal_errno("cannot create local socket %s thread",
+                    HOST ? "client" : "server");
+    }
+}
+
+static void remote_kick(atransport *t)
+{
+    int fd = t->sfd;
+    t->sfd = -1;
+    adb_close(fd);
+
+#if ADB_HOST
+    if(HOST) {
+        int  nn;
+        adb_mutex_lock( &local_transports_lock );
+        for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
+            if (local_transports[nn] == t) {
+                local_transports[nn] = NULL;
+                break;
+            }
+        }
+        adb_mutex_unlock( &local_transports_lock );
+    }
+#endif
+}
+
+static void remote_close(atransport *t)
+{
+    adb_close(t->fd);
+}
+
+int init_socket_transport(atransport *t, int s, int  port)
+{
+    int  fail = 0;
+
+    t->kick = remote_kick;
+    t->close = remote_close;
+    t->read_from_remote = remote_read;
+    t->write_to_remote = remote_write;
+    t->sfd = s;
+    t->sync_token = 1;
+    t->connection_state = CS_OFFLINE;
+    t->type = kTransportLocal;
+
+#if ADB_HOST
+    if (HOST) {
+        adb_mutex_lock( &local_transports_lock );
+        {
+            int  index = (port - ADB_LOCAL_TRANSPORT_PORT)/2;
+
+            if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) {
+                D("bad local transport port number: %d\n", port);
+                fail = -1;
+            }
+            else if (local_transports[index] != NULL) {
+                D("local transport for port %d already registered (%p)?\n",
+                port, local_transports[index]);
+                fail = -1;
+            }
+            else
+                local_transports[index] = t;
+        }
+        adb_mutex_unlock( &local_transports_lock );
+    }
+#endif
+    return fail;
+}
diff --git a/adb/transport_usb.c b/adb/transport_usb.c
new file mode 100644
index 0000000..01c4a7e
--- /dev/null
+++ b/adb/transport_usb.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sysdeps.h>
+
+#define  TRACE_TAG  TRACE_TRANSPORT
+#include "adb.h"
+
+/* XXX better define? */
+#ifdef __ppc__
+#define H4(x)	(((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
+static inline void fix_endians(apacket *p)
+{
+    p->msg.command     = H4(p->msg.command);
+    p->msg.arg0        = H4(p->msg.arg0);
+    p->msg.arg1        = H4(p->msg.arg1);
+    p->msg.data_length = H4(p->msg.data_length);
+    p->msg.data_check  = H4(p->msg.data_check);
+    p->msg.magic       = H4(p->msg.magic);
+}
+unsigned host_to_le32(unsigned n)
+{
+    return H4(n);
+}
+#else
+#define fix_endians(p) do {} while (0)
+unsigned host_to_le32(unsigned n)
+{
+    return n;
+}
+#endif
+
+static int remote_read(apacket *p, atransport *t)
+{
+    if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+        D("remote usb: read terminated (message)\n");
+        return -1;
+    }
+
+    fix_endians(p);
+
+    if(check_header(p)) {
+        D("remote usb: check_header failed\n");
+        return -1;
+    }
+
+    if(p->msg.data_length) {
+        if(usb_read(t->usb, p->data, p->msg.data_length)){
+            D("remote usb: terminated (data)\n");
+            return -1;
+        }
+    }
+
+    if(check_data(p)) {
+        D("remote usb: check_data failed\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int remote_write(apacket *p, atransport *t)
+{
+    unsigned size = p->msg.data_length;
+
+    fix_endians(p);
+
+    if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+        D("remote usb: 1 - write terminated\n");
+        return -1;
+    }
+    if(p->msg.data_length == 0) return 0;
+    if(usb_write(t->usb, &p->data, size)) {
+        D("remote usb: 2 - write terminated\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void remote_close(atransport *t)
+{
+    usb_close(t->usb);
+    t->usb = 0;
+}
+
+static void remote_kick(atransport *t)
+{
+    usb_kick(t->usb);
+}
+
+void init_usb_transport(atransport *t, usb_handle *h)
+{
+    D("transport: usb\n");
+    t->close = remote_close;
+    t->kick = remote_kick;
+    t->read_from_remote = remote_read;
+    t->write_to_remote = remote_write;
+    t->sync_token = 1;
+    t->connection_state = CS_OFFLINE;
+    t->type = kTransportUsb;
+    t->usb = h;
+
+#if ADB_HOST
+    HOST = 1;
+#else
+    HOST = 0;
+#endif
+}
+
+int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+{
+    if (vid == VENDOR_ID_GOOGLE) {
+            /* might support adb */
+    } else if (vid == VENDOR_ID_HTC) {
+            /* might support adb */
+    } else {
+            /* not supported */
+        return 0;
+    }
+
+        /* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */
+    if(usb_class == 0xff) {
+        if((usb_subclass == 0x42) && (usb_protocol == 0x01)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
diff --git a/adb/usb_linux.c b/adb/usb_linux.c
new file mode 100644
index 0000000..32ce0a9
--- /dev/null
+++ b/adb/usb_linux.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+#include <asm/byteorder.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_USB
+#include "adb.h"
+
+
+/* usb scan debugging is waaaay too verbose */
+#define DBGX(x...)
+
+static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
+
+struct usb_handle
+{
+    usb_handle *prev;
+    usb_handle *next;
+
+    char fname[64];
+    int desc;
+    unsigned char ep_in;
+    unsigned char ep_out;
+
+    unsigned zero_mask;
+
+    struct usbdevfs_urb urb_in;
+    struct usbdevfs_urb urb_out;
+
+    int urb_in_busy;
+    int urb_out_busy;
+    int dead;
+
+    adb_cond_t notify;
+    adb_mutex_t lock;
+
+    // for garbage collecting disconnected devices
+    int mark;
+
+    // ID of thread currently in REAPURB
+    pthread_t reaper_thread;
+};
+
+static usb_handle handle_list = {
+    .prev = &handle_list,
+    .next = &handle_list,
+};
+
+static int known_device(const char *dev_name)
+{
+    usb_handle *usb;
+
+    adb_mutex_lock(&usb_lock);
+    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+        if(!strcmp(usb->fname, dev_name)) {
+            // set mark flag to indicate this device is still alive
+            usb->mark = 1;
+            adb_mutex_unlock(&usb_lock);
+            return 1;
+        }
+    }
+    adb_mutex_unlock(&usb_lock);
+    return 0;
+}
+
+static void kick_disconnected_devices()
+{
+    usb_handle *usb;
+
+    adb_mutex_lock(&usb_lock);
+    // kick any devices in the device list that were not found in the device scan
+    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+        if (usb->mark == 0) {
+            usb_kick(usb);
+        } else {
+            usb->mark = 0;
+        }
+    }
+    adb_mutex_unlock(&usb_lock);
+
+}
+
+static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
+                            int ifc, const char *serial, unsigned zero_mask);
+
+static inline int badname(const char *name)
+{
+    while(*name) {
+        if(!isdigit(*name++)) return 1;
+    }
+    return 0;
+}
+
+static int find_usb_device(const char *base,
+                           void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned))
+{
+    char busname[32], devname[32];
+    unsigned char local_ep_in, local_ep_out;
+    DIR *busdir , *devdir ;
+    struct dirent *de;
+    int fd ;
+    int found_device = 0;
+    char serial[256];
+
+    busdir = opendir(base);
+    if(busdir == 0) return 0;
+
+    while((de = readdir(busdir)) != 0) {
+        if(badname(de->d_name)) continue;
+
+        snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
+        devdir = opendir(busname);
+        if(devdir == 0) continue;
+
+//        DBGX("[ scanning %s ]\n", busname);
+        while((de = readdir(devdir))) {
+            unsigned char devdesc[256];
+            unsigned char* bufptr = devdesc;
+            struct usb_device_descriptor* device;
+            struct usb_config_descriptor* config;
+            struct usb_interface_descriptor* interface;
+            struct usb_endpoint_descriptor *ep1, *ep2;
+            unsigned zero_mask = 0;
+            unsigned vid, pid;
+            int i, interfaces;
+            size_t desclength;
+
+            if(badname(de->d_name)) continue;
+            snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
+
+            if(known_device(devname)) {
+                DBGX("skipping %s\n", devname);
+                continue;
+            }
+
+//            DBGX("[ scanning %s ]\n", devname);
+            if((fd = unix_open(devname, O_RDWR)) < 0) {
+                continue;
+            }
+
+            desclength = adb_read(fd, devdesc, sizeof(devdesc));
+
+                // should have device and configuration descriptors, and atleast two endpoints
+            if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
+                D("desclength %d is too small\n", desclength);
+                adb_close(fd);
+                continue;
+            }
+
+            device = (struct usb_device_descriptor*)bufptr;
+            bufptr += USB_DT_DEVICE_SIZE;
+
+            if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
+                adb_close(fd);
+                continue;
+            }
+
+            vid = __le16_to_cpu(device->idVendor);
+            pid = __le16_to_cpu(device->idProduct);
+            pid = devdesc[10] | (devdesc[11] << 8);
+            DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
+
+                // should have config descriptor next
+            config = (struct usb_config_descriptor *)bufptr;
+            bufptr += USB_DT_CONFIG_SIZE;
+            if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
+                D("usb_config_descriptor not found\n");
+                adb_close(fd);
+                continue;
+            }
+
+                // loop through all the interfaces and look for the ADB interface
+            interfaces = config->bNumInterfaces;
+            for (i = 0; i < interfaces; i++) {
+                if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
+                    break;
+
+                interface = (struct usb_interface_descriptor *)bufptr;
+                bufptr += USB_DT_INTERFACE_SIZE;
+                if (interface->bLength != USB_DT_INTERFACE_SIZE ||
+                    interface->bDescriptorType != USB_DT_INTERFACE) {
+                    D("usb_interface_descriptor not found\n");
+                    break;
+                }
+
+                DBGX("bInterfaceClass: %d,  bInterfaceSubClass: %d,"
+                     "bInterfaceProtocol: %d, bNumEndpoints: %d\n",
+                     interface->bInterfaceClass, interface->bInterfaceSubClass,
+                     interface->bInterfaceProtocol, interface->bNumEndpoints);
+
+                if (interface->bNumEndpoints == 2 &&
+                        is_adb_interface(vid, pid, interface->bInterfaceClass,
+                        interface->bInterfaceSubClass, interface->bInterfaceProtocol))  {
+
+                    DBGX("looking for bulk endpoints\n");
+                        // looks like ADB...
+                    ep1 = (struct usb_endpoint_descriptor *)bufptr;
+                    bufptr += USB_DT_ENDPOINT_SIZE;
+                    ep2 = (struct usb_endpoint_descriptor *)bufptr;
+                    bufptr += USB_DT_ENDPOINT_SIZE;
+
+                    if (bufptr > devdesc + desclength ||
+                        ep1->bLength != USB_DT_ENDPOINT_SIZE ||
+                        ep1->bDescriptorType != USB_DT_ENDPOINT ||
+                        ep2->bLength != USB_DT_ENDPOINT_SIZE ||
+                        ep2->bDescriptorType != USB_DT_ENDPOINT) {
+                        D("endpoints not found\n");
+                        break;
+                    }
+
+                        // both endpoints should be bulk
+                    if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
+                        ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
+                        D("bulk endpoints not found\n");
+                        continue;
+                    }
+
+                        /* aproto 01 needs 0 termination */
+                    if(interface->bInterfaceProtocol == 0x01) {
+                        zero_mask = ep1->wMaxPacketSize - 1;
+                    }
+
+                        // we have a match.  now we just need to figure out which is in and which is out.
+                    if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                        local_ep_in = ep1->bEndpointAddress;
+                        local_ep_out = ep2->bEndpointAddress;
+                    } else {
+                        local_ep_in = ep2->bEndpointAddress;
+                        local_ep_out = ep1->bEndpointAddress;
+                    }
+
+                        // read the device's serial number
+                    serial[0] = 0;
+                    memset(serial, 0, sizeof(serial));
+                    if (device->iSerialNumber) {
+                        struct usbdevfs_ctrltransfer  ctrl;
+                        __u16 buffer[128];
+                        int result;
+
+                        memset(buffer, 0, sizeof(buffer));
+                        memset(&ctrl, 0, sizeof(ctrl));
+
+                        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+                        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+                        ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber;
+                        ctrl.wIndex = 0;
+                        ctrl.wLength = sizeof(buffer);
+                        ctrl.data = buffer;
+
+                        result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
+                        if (result > 0) {
+                            int i;
+                                // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+                            result /= 2;
+                            for (i = 1; i < result; i++)
+                                serial[i - 1] = buffer[i];
+                            serial[i - 1] = 0;
+                        }
+                    }
+
+                    register_device_callback(devname, local_ep_in, local_ep_out,
+                            interface->bInterfaceNumber, serial, zero_mask);
+
+                    found_device = 1;
+                    break;
+                } else {
+                        // skip to next interface
+                    bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
+                }
+            } // end of for
+
+            adb_close(fd);
+        } // end of devdir while
+        closedir(devdir);
+    } //end of busdir while
+    closedir(busdir);
+
+    return found_device;
+}
+
+void usb_cleanup()
+{
+}
+
+static int usb_bulk_write(usb_handle *h, const void *data, int len)
+{
+    struct usbdevfs_urb *urb = &h->urb_out;
+    int res;
+
+    memset(urb, 0, sizeof(*urb));
+    urb->type = USBDEVFS_URB_TYPE_BULK;
+    urb->endpoint = h->ep_out;
+    urb->status = -1;
+    urb->buffer = (void*) data;
+    urb->buffer_length = len;
+
+    D("++ write ++\n");
+
+    adb_mutex_lock(&h->lock);
+    if(h->dead) {
+        res = -1;
+        goto fail;
+    }
+    do {
+        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
+    } while((res < 0) && (errno == EINTR));
+
+    if(res < 0) {
+        goto fail;
+    }
+
+    res = -1;
+    h->urb_out_busy = 1;
+    for(;;) {
+        adb_cond_wait(&h->notify, &h->lock);
+        if(h->dead) {
+            break;
+        }
+        if(h->urb_out_busy == 0) {
+            if(urb->status == 0) {
+                res = urb->actual_length;
+            }
+            break;
+        }
+    }
+fail:
+    adb_mutex_unlock(&h->lock);
+    D("-- write --\n");
+    return res;
+}
+
+static int usb_bulk_read(usb_handle *h, void *data, int len)
+{
+    struct usbdevfs_urb *urb = &h->urb_in;
+    struct usbdevfs_urb *out = NULL;
+    int res;
+
+    memset(urb, 0, sizeof(*urb));
+    urb->type = USBDEVFS_URB_TYPE_BULK;
+    urb->endpoint = h->ep_in;
+    urb->status = -1;
+    urb->buffer = data;
+    urb->buffer_length = len;
+
+
+    adb_mutex_lock(&h->lock);
+    if(h->dead) {
+        res = -1;
+        goto fail;
+    }
+    do {
+        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
+    } while((res < 0) && (errno == EINTR));
+
+    if(res < 0) {
+        goto fail;
+    }
+
+    h->urb_in_busy = 1;
+    for(;;) {
+        D("[ reap urb - wait ]\n");
+        h->reaper_thread = pthread_self();
+        adb_mutex_unlock(&h->lock);
+        res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
+        adb_mutex_lock(&h->lock);
+        h->reaper_thread = 0;
+        if(h->dead) {
+            res = -1;
+            break;
+        }
+        if(res < 0) {
+            if(errno == EINTR) {
+                continue;
+            }
+            D("[ reap urb - error ]\n");
+            break;
+        }
+        D("[ urb @%p status = %d, actual = %d ]\n",
+            out, out->status, out->actual_length);
+
+        if(out == &h->urb_in) {
+            D("[ reap urb - IN complete ]\n");
+            h->urb_in_busy = 0;
+            if(urb->status == 0) {
+                res = urb->actual_length;
+            } else {
+                res = -1;
+            }
+            break;
+        }
+        if(out == &h->urb_out) {
+            D("[ reap urb - OUT compelete ]\n");
+            h->urb_out_busy = 0;
+            adb_cond_broadcast(&h->notify);
+        }
+    }
+fail:
+    adb_mutex_unlock(&h->lock);
+    return res;
+}
+
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+    unsigned char *data = (unsigned char*) _data;
+    int n;
+    int need_zero = 0;
+
+    if(h->zero_mask) {
+            /* if we need 0-markers and our transfer
+            ** is an even multiple of the packet size,
+            ** we make note of it
+            */
+        if(!(len & h->zero_mask)) {
+            need_zero = 1;
+        }
+    }
+
+    while(len > 0) {
+        int xfer = (len > 4096) ? 4096 : len;
+
+        n = usb_bulk_write(h, data, xfer);
+        if(n != xfer) {
+            D("ERROR: n = %d, errno = %d (%s)\n",
+                n, errno, strerror(errno));
+            return -1;
+        }
+
+        len -= xfer;
+        data += xfer;
+    }
+
+    if(need_zero){
+        n = usb_bulk_write(h, _data, 0);
+        return n;
+    }
+
+    return 0;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+    unsigned char *data = (unsigned char*) _data;
+    int n;
+
+    D("++ usb_read ++\n");
+    while(len > 0) {
+        int xfer = (len > 4096) ? 4096 : len;
+
+        D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+        n = usb_bulk_read(h, data, xfer);
+        D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
+        if(n != xfer) {
+            if((errno == ETIMEDOUT) && (h->desc != -1)) {
+                D("[ timeout ]\n");
+                if(n > 0){
+                    data += n;
+                    len -= n;
+                }
+                continue;
+            }
+            D("ERROR: n = %d, errno = %d (%s)\n",
+                n, errno, strerror(errno));
+            return -1;
+        }
+
+        len -= xfer;
+        data += xfer;
+    }
+
+    D("-- usb_read --\n");
+    return 0;
+}
+
+void usb_kick(usb_handle *h)
+{
+    D("[ kicking %p (fd = %d) ]\n", h, h->desc);
+    adb_mutex_lock(&h->lock);
+    if(h->dead == 0) {
+        h->dead = 1;
+
+        /* HACK ALERT!
+        ** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
+        ** This is a workaround for that problem.
+        */
+        if (h->reaper_thread) {
+            pthread_kill(h->reaper_thread, SIGALRM);
+        }
+
+        /* cancel any pending transactions
+        ** these will quietly fail if the txns are not active,
+        ** but this ensures that a reader blocked on REAPURB
+        ** will get unblocked
+        */
+        ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
+        ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
+        h->urb_in.status = -ENODEV;
+        h->urb_out.status = -ENODEV;
+        h->urb_in_busy = 0;
+        h->urb_out_busy = 0;
+        adb_cond_broadcast(&h->notify);
+    }
+    adb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+    D("[ usb close ... ]\n");
+    adb_mutex_lock(&usb_lock);
+    h->next->prev = h->prev;
+    h->prev->next = h->next;
+    h->prev = 0;
+    h->next = 0;
+
+    adb_close(h->desc);
+    D("[ usb closed %p (fd = %d) ]\n", h, h->desc);
+    adb_mutex_unlock(&usb_lock);
+
+    free(h);
+    return 0;
+}
+
+static void register_device(const char *dev_name,
+                            unsigned char ep_in, unsigned char ep_out,
+                            int interface,
+                            const char *serial, unsigned zero_mask)
+{
+    usb_handle* usb = 0;
+    int n = 0;
+
+        /* Since Linux will not reassign the device ID (and dev_name)
+        ** as long as the device is open, we can add to the list here
+        ** once we open it and remove from the list when we're finally
+        ** closed and everything will work out fine.
+        **
+        ** If we have a usb_handle on the list 'o handles with a matching
+        ** name, we have no further work to do.
+        */
+    adb_mutex_lock(&usb_lock);
+    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+        if(!strcmp(usb->fname, dev_name)) {
+            adb_mutex_unlock(&usb_lock);
+            return;
+        }
+    }
+    adb_mutex_unlock(&usb_lock);
+
+    D("[ usb located new device %s (%d/%d/%d) ]\n",
+        dev_name, ep_in, ep_out, interface);
+    usb = calloc(1, sizeof(usb_handle));
+    strcpy(usb->fname, dev_name);
+    usb->ep_in = ep_in;
+    usb->ep_out = ep_out;
+    usb->zero_mask = zero_mask;
+
+    adb_cond_init(&usb->notify, 0);
+    adb_mutex_init(&usb->lock, 0);
+    /* initialize mark to 1 so we don't get garbage collected after the device scan */
+    usb->mark = 1;
+    usb->reaper_thread = 0;
+
+    usb->desc = unix_open(usb->fname, O_RDWR);
+    if(usb->desc < 0) goto fail;
+    D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
+    n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
+    if(n != 0) goto fail;
+
+        /* add to the end of the active handles */
+    adb_mutex_lock(&usb_lock);
+    usb->next = &handle_list;
+    usb->prev = handle_list.prev;
+    usb->prev->next = usb;
+    usb->next->prev = usb;
+    adb_mutex_unlock(&usb_lock);
+
+    register_usb_transport(usb, serial);
+    return;
+
+fail:
+    D("[ usb open %s error=%d, err_str = %s]\n",
+        usb->fname,  errno, strerror(errno));
+    if(usb->desc >= 0) {
+        adb_close(usb->desc);
+    }
+    free(usb);
+}
+
+void* device_poll_thread(void* unused)
+{
+    D("Created device thread\n");
+    for(;;) {
+            /* XXX use inotify */
+        find_usb_device("/dev/bus/usb", register_device);
+        kick_disconnected_devices();
+        sleep(1);
+    }
+    return NULL;
+}
+
+static void sigalrm_handler(int signo)
+{
+    // don't need to do anything here
+}
+
+void usb_init()
+{
+    adb_thread_t tid;
+    struct sigaction    actions;
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = sigalrm_handler;
+    sigaction(SIGALRM,& actions, NULL);
+
+    if(adb_thread_create(&tid, device_poll_thread, NULL)){
+        fatal_errno("cannot create input thread");
+    }
+}
+
diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c
new file mode 100644
index 0000000..530bd04
--- /dev/null
+++ b/adb/usb_linux_client.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_USB
+#include "adb.h"
+
+
+struct usb_handle
+{
+    int fd;
+    adb_cond_t notify;
+    adb_mutex_t lock;
+};
+
+void usb_cleanup()
+{
+    // nothing to do here
+}
+
+static void *usb_open_thread(void *x)
+{
+    struct usb_handle *usb = (struct usb_handle *)x;
+    int fd;
+
+    while (1) {
+        // wait until the USB device needs opening
+        adb_mutex_lock(&usb->lock);
+        while (usb->fd != -1)
+            adb_cond_wait(&usb->notify, &usb->lock);
+        adb_mutex_unlock(&usb->lock);
+
+        D("[ usb_thread - opening device ]\n");
+        do {
+            /* XXX use inotify? */
+            fd = unix_open("/dev/android_adb", O_RDWR);
+            if (fd < 0) {
+                // to support older kernels
+                fd = unix_open("/dev/android", O_RDWR);
+            }
+            if (fd < 0) {
+                adb_sleep_ms(1000);
+            }
+        } while (fd < 0);
+        D("[ opening device succeeded ]\n");
+
+        close_on_exec(fd);
+        usb->fd = fd;
+
+        D("[ usb_thread - registering device ]\n");
+        register_usb_transport(usb, 0);
+    }
+
+    // never gets here
+    return 0;
+}
+
+int usb_write(usb_handle *h, const void *data, int len)
+{
+    int n;
+
+    D("[ write %d ]\n", len);
+    n = adb_write(h->fd, data, len);
+    if(n != len) {
+        D("ERROR: n = %d, errno = %d (%s)\n",
+            n, errno, strerror(errno));
+        return -1;
+    }
+    D("[ done ]\n");
+    return 0;
+}
+
+int usb_read(usb_handle *h, void *data, int len)
+{
+    int n;
+
+    D("[ read %d ]\n", len);
+    n = adb_read(h->fd, data, len);
+    if(n != len) {
+        D("ERROR: n = %d, errno = %d (%s)\n",
+            n, errno, strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+void usb_init()
+{
+    usb_handle *h;
+    adb_thread_t tid;
+    int fd;
+
+    h = calloc(1, sizeof(usb_handle));
+    h->fd = -1;
+    adb_cond_init(&h->notify, 0);
+    adb_mutex_init(&h->lock, 0);
+
+    // Open the file /dev/android_adb_enable to trigger 
+    // the enabling of the adb USB function in the kernel.
+    // We never touch this file again - just leave it open
+    // indefinitely so the kernel will know when we are running
+    // and when we are not.
+    fd = unix_open("/dev/android_adb_enable", O_RDWR);
+    if (fd < 0) {
+       D("failed to open /dev/android_adb_enable\n");
+    } else {
+        close_on_exec(fd);
+    }
+
+    D("[ usb_init - starting thread ]\n");
+    if(adb_thread_create(&tid, usb_open_thread, h)){
+        fatal_errno("cannot create usb thread");
+    }
+}
+
+void usb_kick(usb_handle *h)
+{
+    D("usb_kick\n");
+    adb_mutex_lock(&h->lock);
+    adb_close(h->fd);
+    h->fd = -1;
+
+    // notify usb_open_thread that we are disconnected
+    adb_cond_signal(&h->notify);
+    adb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+    // nothing to do here
+    return 0;
+}
diff --git a/adb/usb_osx.c b/adb/usb_osx.c
new file mode 100644
index 0000000..49e1eef
--- /dev/null
+++ b/adb/usb_osx.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include "sysdeps.h"
+
+#include <stdio.h>
+
+#define TRACE_TAG   TRACE_USB
+#include "adb.h"
+
+#define  DBG   D
+
+typedef struct {
+    int vid;
+    int pid;
+} VendorProduct;
+
+#define kSupportedDeviceCount   4
+VendorProduct kSupportedDevices[kSupportedDeviceCount] = {
+    { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER },
+    { VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP },
+    { VENDOR_ID_HTC, PRODUCT_ID_DREAM },
+    { VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP },
+};
+
+static IONotificationPortRef    notificationPort = 0;
+static io_iterator_t            notificationIterators[kSupportedDeviceCount];
+
+struct usb_handle
+{
+    UInt8                     bulkIn;
+    UInt8                     bulkOut;
+    IOUSBInterfaceInterface   **interface;
+    io_object_t               usbNotification;
+    unsigned int              zero_mask;
+};
+
+static CFRunLoopRef currentRunLoop = 0;
+static pthread_mutex_t start_lock;
+static pthread_cond_t start_cond;
+
+
+static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator);
+static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument);
+static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product);
+
+static int
+InitUSB()
+{
+    CFMutableDictionaryRef  matchingDict;
+    CFRunLoopSourceRef      runLoopSource;
+    SInt32					vendor, product;
+    int                     i;
+
+    //* To set up asynchronous notifications, create a notification port and
+    //* add its run loop event source to the program's run loop
+    notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+    runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
+    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
+
+    memset(notificationIterators, 0, sizeof(notificationIterators));
+
+    //* loop through all supported vendor/product pairs
+    for (i = 0; i < kSupportedDeviceCount; i++) {
+        //* Create our matching dictionary to find the Android device
+        //* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this
+        matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+        if (!matchingDict) {
+            DBG("ERR: Couldn't create USB matching dictionary.\n");
+            return -1;
+        }
+
+        //* Set up two matching dictionaries, one for each product ID we support.
+        //* This will cause the kernel to notify us only if the vendor and product IDs match.
+        vendor = kSupportedDevices[i].vid;
+        product = kSupportedDevices[i].pid;
+        CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor));
+        CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product));
+
+        //* Now set up two notifications: one to be called when a raw device
+        //* is first matched by the I/O Kit and another to be called when the
+        //* device is terminated.
+        //* we need to do this with each matching dictionary.
+        IOServiceAddMatchingNotification(
+                notificationPort,
+                kIOFirstMatchNotification,
+                matchingDict,
+                AndroidDeviceAdded,
+                NULL,
+                &notificationIterators[i]);
+
+        //* Iterate over set of matching devices to access already-present devices
+        //* and to arm the notification
+        AndroidDeviceAdded(NULL, notificationIterators[i]);
+    }
+
+    return 0;
+}
+
+static void
+AndroidDeviceAdded(void *refCon, io_iterator_t iterator)
+{
+    kern_return_t            kr;
+    io_service_t             usbDevice;
+    IOCFPlugInInterface      **plugInInterface = NULL;
+    IOUSBDeviceInterface182  **dev = NULL;
+    HRESULT                  result;
+    SInt32                   score;
+    UInt16                   vendor;
+    UInt16                   product;
+    UInt8                    serialIndex;
+    char                     serial[256];
+
+    while ((usbDevice = IOIteratorNext(iterator))) {
+        //* Create an intermediate plugin
+        kr = IOCreatePlugInInterfaceForService(usbDevice,
+                                               kIOUSBDeviceUserClientTypeID,
+                                               kIOCFPlugInInterfaceID,
+                                               &plugInInterface, &score);
+
+        if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+            DBG("ERR: Unable to create a plug-in (%08x)\n", kr);
+            goto continue1;
+        }
+
+        //* Now create the device interface
+        result = (*plugInInterface)->QueryInterface(plugInInterface,
+                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+
+        if (result || !dev) {
+            DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result);
+            goto continue2;
+        }
+
+        //* Check the device to see if it's ours
+        kr = (*dev)->GetDeviceVendor(dev, &vendor);
+        kr = (*dev)->GetDeviceProduct(dev, &product);
+        kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+        if (serialIndex > 0) {
+            IOUSBDevRequest req;
+            UInt16          buffer[256];
+
+            req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+            req.bRequest = kUSBRqGetDescriptor;
+            req.wValue = (kUSBStringDesc << 8) | serialIndex;
+            req.wIndex = 0;
+            req.pData = buffer;
+            req.wLength = sizeof(buffer);
+            kr = (*dev)->DeviceRequest(dev, &req);
+
+            if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+                int i, count;
+
+                // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+                count = (req.wLenDone - 1) / 2;
+                for (i = 0; i < count; i++)
+                  serial[i] = buffer[i + 1];
+                serial[i] = 0;
+            }
+        }
+
+        usb_handle* handle = NULL;
+
+        //* Open the device
+        kr = (*dev)->USBDeviceOpen(dev);
+
+        if (kr != kIOReturnSuccess) {
+            DBG("ERR: Could not open device: %08x\n", kr);
+            goto continue3;
+        } else {
+            //* Find an interface for the device
+            handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product);
+        }
+
+        if (handle == NULL) {
+            DBG("ERR: Could not find device interface: %08x\n", kr);
+            (*dev)->USBDeviceClose(dev);
+            goto continue3;
+        }
+
+        DBG("AndroidDeviceAdded calling register_usb_transport\n");
+        register_usb_transport(handle, (serial[0] ? serial : NULL));
+
+        // Register for an interest notification of this device being removed. Pass the reference to our
+        // private data as the refCon for the notification.
+        kr = IOServiceAddInterestNotification(notificationPort,
+                usbDevice,
+                kIOGeneralInterest,
+                AndroidDeviceNotify,
+                handle,
+                &handle->usbNotification);
+        if (kIOReturnSuccess != kr) {
+            DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+        }
+
+continue3:
+        (void)(*dev)->Release(dev);
+continue2:
+        IODestroyPlugInInterface(plugInInterface);
+continue1:
+        IOObjectRelease(usbDevice);
+    }
+}
+
+static void
+AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+    usb_handle *handle = (usb_handle *)refCon;
+
+    if (messageType == kIOMessageServiceIsTerminated) {
+        DBG("AndroidDeviceNotify\n");
+        IOObjectRelease(handle->usbNotification);
+        usb_kick(handle);
+    }
+}
+
+static usb_handle*
+FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product)
+{
+    usb_handle*                 handle = NULL;
+    IOReturn                    kr;
+    IOUSBFindInterfaceRequest   request;
+    io_iterator_t               iterator;
+    io_service_t                usbInterface;
+    IOCFPlugInInterface         **plugInInterface;
+    IOUSBInterfaceInterface     **interface = NULL;
+    HRESULT                     result;
+    SInt32                      score;
+    UInt8  interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+    UInt8  endpoint, configuration;
+
+    //* Placing the constant KIOUSBFindInterfaceDontCare into the following
+    //* fields of the IOUSBFindInterfaceRequest structure will allow us to
+    //* find all of the interfaces
+    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+    //* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary.
+    configuration = 0;
+    (*dev)->GetConfiguration(dev, &configuration);
+    if (configuration != 1)
+        (*dev)->SetConfiguration(dev, 1);
+
+    //* Get an iterator for the interfaces on the device
+    kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
+
+    if (kr != kIOReturnSuccess) {
+        DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr);
+        return NULL;
+    }
+
+    while ((usbInterface = IOIteratorNext(iterator))) {
+    //* Create an intermediate plugin
+        kr = IOCreatePlugInInterfaceForService(
+                usbInterface,
+                kIOUSBInterfaceUserClientTypeID,
+                kIOCFPlugInInterfaceID,
+                &plugInInterface,
+                &score);
+
+        //* No longer need the usbInterface object now that we have the plugin
+        (void) IOObjectRelease(usbInterface);
+
+        if ((kr != kIOReturnSuccess) || (!plugInInterface)) {
+            DBG("ERR: Unable to create plugin (%08x)\n", kr);
+            break;
+        }
+
+        //* Now create the interface interface for the interface
+        result = (*plugInInterface)->QueryInterface(
+                plugInInterface,
+                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+                (LPVOID) &interface);
+
+        //* No longer need the intermediate plugin
+        (*plugInInterface)->Release(plugInInterface);
+
+        if (result || !interface) {
+            DBG("ERR: Couldn't create interface interface: (%08x)\n",
+               (unsigned int) result);
+            break;
+        }
+
+        //* Now open the interface.  This will cause the pipes associated with
+        //* the endpoints in the interface descriptor to be instantiated
+        kr = (*interface)->USBInterfaceOpen(interface);
+
+        if (kr != kIOReturnSuccess)
+        {
+            DBG("ERR: Could not open interface: (%08x)\n", kr);
+            (void) (*interface)->Release(interface);
+            //* continue so we can try the next interface
+            continue;
+        }
+
+        //* Get the number of endpoints associated with this interface
+        kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+
+        if (kr != kIOReturnSuccess) {
+            DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+            goto next_interface;
+        }
+
+        //* Get interface class, subclass and protocol
+        if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
+            (*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
+            (*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess)
+        {
+            DBG("ERR: Unable to get interface class, subclass and protocol\n");
+            goto next_interface;
+        }
+
+        //* check to make sure interface class, subclass and protocol match ADB
+        //* avoid opening mass storage endpoints
+        if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
+            handle = calloc(1, sizeof(usb_handle));
+
+            //* Iterate over the endpoints for this interface and find the first
+            //* bulk in/out pipes available.  These will be our read/write pipes.
+            for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+                UInt8   transferType;
+                UInt16  maxPacketSize;
+                UInt8   interval;
+                UInt8   number;
+                UInt8   direction;
+
+                kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
+                        &number, &transferType, &maxPacketSize, &interval);
+
+                if (kIOReturnSuccess == kr) {
+                    if (kUSBBulk != transferType)
+                        continue;
+
+                    if (kUSBIn == direction)
+                        handle->bulkIn = endpoint;
+
+                    if (kUSBOut == direction)
+                        handle->bulkOut = endpoint;
+
+                    if (interfaceProtocol == 0x01) {
+                        handle->zero_mask = maxPacketSize - 1;
+                    }
+
+                } else {
+                    DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+                }
+            }
+
+            handle->interface = interface;
+            break;
+        }
+
+next_interface:
+        (*interface)->USBInterfaceClose(interface);
+        (*interface)->Release(interface);
+    }
+
+    return handle;
+}
+
+
+void* RunLoopThread(void* unused)
+{
+    int i;
+
+    InitUSB();
+
+    currentRunLoop = CFRunLoopGetCurrent();
+
+    // Signal the parent that we are running
+    adb_mutex_lock(&start_lock);
+    adb_cond_signal(&start_cond);
+    adb_mutex_unlock(&start_lock);
+
+    CFRunLoopRun();
+    currentRunLoop = 0;
+
+    for (i = 0; i < kSupportedDeviceCount; i++) {
+        IOObjectRelease(notificationIterators[i]);
+    }
+    IONotificationPortDestroy(notificationPort);
+
+    DBG("RunLoopThread done\n");
+    return NULL;    
+}
+
+
+static int initialized = 0;
+void usb_init()
+{
+    if (!initialized)
+    {
+        adb_thread_t    tid;
+
+        adb_mutex_init(&start_lock, NULL);
+        adb_cond_init(&start_cond, NULL);
+
+        if(adb_thread_create(&tid, RunLoopThread, NULL))
+            fatal_errno("cannot create input thread");
+
+        // Wait for initialization to finish
+        adb_mutex_lock(&start_lock);
+        adb_cond_wait(&start_cond, &start_lock);
+        adb_mutex_unlock(&start_lock);
+
+        adb_mutex_destroy(&start_lock);
+        adb_cond_destroy(&start_cond);
+
+        initialized = 1;
+    }
+}
+
+void usb_cleanup()
+{
+    DBG("usb_cleanup\n");
+    close_usb_devices();
+    if (currentRunLoop)
+        CFRunLoopStop(currentRunLoop);
+}
+
+int usb_write(usb_handle *handle, const void *buf, int len)
+{
+    IOReturn    result;
+
+    if (!len)
+        return 0;
+
+    if (!handle)
+        return -1;
+
+    if (NULL == handle->interface) {
+        DBG("ERR: usb_write interface was null\n");
+        return -1;
+    }
+
+    if (0 == handle->bulkOut) {
+        DBG("ERR: bulkOut endpoint not assigned\n");
+        return -1;
+    }
+
+    result =
+        (*handle->interface)->WritePipe(
+                              handle->interface, handle->bulkOut, (void *)buf, len);
+
+    if ((result == 0) && (handle->zero_mask)) {
+        /* we need 0-markers and our transfer */
+        if(!(len & handle->zero_mask)) {
+            result =
+                (*handle->interface)->WritePipe(
+                        handle->interface, handle->bulkOut, (void *)buf, 0);
+        }
+    }
+
+    if (0 == result)
+        return 0;
+
+    DBG("ERR: usb_write failed with status %d\n", result);
+    return -1;
+}
+
+int usb_read(usb_handle *handle, void *buf, int len)
+{
+    IOReturn result;
+    UInt32  numBytes = len;
+
+    if (!len) {
+        return 0;
+    }
+
+    if (!handle) {
+        return -1;
+    }
+
+    if (NULL == handle->interface) {
+        DBG("ERR: usb_read interface was null\n");
+        return -1;
+    }
+
+    if (0 == handle->bulkIn) {
+        DBG("ERR: bulkIn endpoint not assigned\n");
+        return -1;
+    }
+
+    result =
+      (*handle->interface)->ReadPipe(handle->interface,
+                                    handle->bulkIn, buf, &numBytes);
+
+    if (0 == result)
+        return 0;
+    else {
+        DBG("ERR: usb_read failed with status %d\n", result);
+    }
+
+    return -1;
+}
+
+int usb_close(usb_handle *handle)
+{
+    return 0;
+}
+
+void usb_kick(usb_handle *handle)
+{
+    /* release the interface */
+    if (handle->interface)
+    {
+        (*handle->interface)->USBInterfaceClose(handle->interface);
+        (*handle->interface)->Release(handle->interface);
+        handle->interface = 0;
+    }
+}
diff --git a/adb/usb_windows.c b/adb/usb_windows.c
new file mode 100644
index 0000000..7ddaa0c
--- /dev/null
+++ b/adb/usb_windows.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+#include <winerror.h>
+#include <errno.h>
+#include <usb100.h>
+#include <adb_api.h>
+#include <stdio.h>
+
+#include "sysdeps.h"
+
+#define   TRACE_TAG  TRACE_USB
+#include "adb.h"
+
+/** Structure usb_handle describes our connection to the usb device via
+  AdbWinApi.dll. This structure is returned from usb_open() routine and
+  is expected in each subsequent call that is accessing the device.
+*/
+struct usb_handle {
+  /// Previous entry in the list of opened usb handles
+  usb_handle *prev;
+
+  /// Next entry in the list of opened usb handles
+  usb_handle *next;
+
+  /// Handle to USB interface
+  ADBAPIHANDLE  adb_interface;
+
+  /// Handle to USB read pipe (endpoint)
+  ADBAPIHANDLE  adb_read_pipe;
+
+  /// Handle to USB write pipe (endpoint)
+  ADBAPIHANDLE  adb_write_pipe;
+
+  /// Interface name
+  char*         interface_name;
+
+  /// Mask for determining when to use zero length packets
+  unsigned zero_mask;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+/// List of opened usb handles
+static usb_handle handle_list = {
+  .prev = &handle_list,
+  .next = &handle_list,
+};
+
+/// Locker for the list of opened usb handles
+ADB_MUTEX_DEFINE( usb_lock );
+
+/// Checks if there is opened usb handle in handle_list for this device.
+int known_device(const char* dev_name);
+
+/// Checks if there is opened usb handle in handle_list for this device.
+/// usb_lock mutex must be held before calling this routine.
+int known_device_locked(const char* dev_name);
+
+/// Registers opened usb handle (adds it to handle_list).
+int register_new_device(usb_handle* handle);
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle);
+
+/// Enumerates present and available interfaces (devices), opens new ones and
+/// registers usb transport for them.
+void find_devices();
+
+/// Entry point for thread that polls (every second) for new usb interfaces.
+/// This routine calls find_devices in infinite loop.
+void* device_poll_thread(void* unused);
+
+/// Initializes this module
+void usb_init();
+
+/// Cleans up this module
+void usb_cleanup();
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+/// Gets interface (device) name for an opened usb handle
+const char *usb_name(usb_handle* handle);
+
+int known_device_locked(const char* dev_name) {
+  usb_handle* usb;
+
+  if (NULL != dev_name) {
+    // Iterate through the list looking for the name match.
+    for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+      // In Windows names are not case sensetive!
+      if((NULL != usb->interface_name) &&
+         (0 == stricmp(usb->interface_name, dev_name))) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+int known_device(const char* dev_name) {
+  int ret = 0;
+
+  if (NULL != dev_name) {
+    adb_mutex_lock(&usb_lock);
+    ret = known_device_locked(dev_name);
+    adb_mutex_unlock(&usb_lock);
+  }
+
+  return ret;
+}
+
+int register_new_device(usb_handle* handle) {
+  if (NULL == handle)
+    return 0;
+
+  adb_mutex_lock(&usb_lock);
+
+  // Check if device is already in the list
+  if (known_device_locked(handle->interface_name)) {
+    adb_mutex_unlock(&usb_lock);
+    return 0;
+  }
+
+  // Not in the list. Add this handle to the list.
+  handle->next = &handle_list;
+  handle->prev = handle_list.prev;
+  handle->prev->next = handle;
+  handle->next->prev = handle;
+
+  adb_mutex_unlock(&usb_lock);
+
+  return 1;
+}
+
+void* device_poll_thread(void* unused) {
+  D("Created device thread\n");
+
+  while(1) {
+    find_devices();
+    adb_sleep_ms(1000);
+  }
+
+  return NULL;
+}
+
+void usb_init() {
+  adb_thread_t tid;
+
+  if(adb_thread_create(&tid, device_poll_thread, NULL)) {
+    fatal_errno("cannot create input thread");
+  }
+}
+
+void usb_cleanup() {
+}
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+  // Allocate our handle
+  usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
+  if (NULL == ret)
+    return NULL;
+
+  // Set linkers back to the handle
+  ret->next = ret;
+  ret->prev = ret;
+
+  // Create interface.
+  ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+
+  if (NULL == ret->adb_interface) {
+    free(ret);
+    errno = GetLastError();
+    return NULL;
+  }
+
+  // Open read pipe (endpoint)
+  ret->adb_read_pipe =
+    AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+                                   AdbOpenAccessTypeReadWrite,
+                                   AdbOpenSharingModeReadWrite);
+  if (NULL != ret->adb_read_pipe) {
+    // Open write pipe (endpoint)
+    ret->adb_write_pipe =
+      AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+                                      AdbOpenAccessTypeReadWrite,
+                                      AdbOpenSharingModeReadWrite);
+    if (NULL != ret->adb_write_pipe) {
+      // Save interface name
+      unsigned long name_len = 0;
+
+      // First get expected name length
+      AdbGetInterfaceName(ret->adb_interface,
+                          NULL,
+                          &name_len,
+                          true);
+      if (0 != name_len) {
+        ret->interface_name = (char*)malloc(name_len);
+
+        if (NULL != ret->interface_name) {
+          // Now save the name
+          if (AdbGetInterfaceName(ret->adb_interface,
+                                  ret->interface_name,
+                                  &name_len,
+                                  true)) {
+            // We're done at this point
+            return ret;
+          }
+        } else {
+          SetLastError(ERROR_OUTOFMEMORY);
+        }
+      }
+    }
+  }
+
+  // Something went wrong.
+  errno = GetLastError();
+  usb_cleanup_handle(ret);
+  free(ret);
+  SetLastError(errno);
+
+  return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+  unsigned long time_out = 500 + len * 8;
+  unsigned long written = 0;
+  int ret;
+
+  D("usb_write %d\n", len);
+  if (NULL != handle) {
+    // Perform write
+    ret = AdbWriteEndpointSync(handle->adb_write_pipe,
+                               (void*)data,
+                               (unsigned long)len,
+                               &written,
+                               time_out);
+    errno = GetLastError();
+
+    if (ret) {
+      // Make sure that we've written what we were asked to write
+      D("usb_write got: %ld, expected: %d\n", written, len);
+      if (written == (unsigned long)len) {
+        if(handle->zero_mask && (len & handle->zero_mask) == 0) {
+          // Send a zero length packet
+          AdbWriteEndpointSync(handle->adb_write_pipe,
+                               (void*)data,
+                               0,
+                               &written,
+                               time_out);
+        }
+        return 0;
+      }
+    } else {
+      // assume ERROR_INVALID_HANDLE indicates we are disconnected
+      if (errno == ERROR_INVALID_HANDLE)
+        usb_kick(handle);
+    }
+  } else {
+    D("usb_write NULL handle\n");
+    SetLastError(ERROR_INVALID_HANDLE);
+  }
+
+  D("usb_write failed: %d\n", errno);
+
+  return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+  unsigned long time_out = 500 + len * 8;
+  unsigned long read = 0;
+  int ret;
+
+  D("usb_read %d\n", len);
+  if (NULL != handle) {
+    while (len > 0) {
+      int xfer = (len > 4096) ? 4096 : len;
+
+      ret = AdbReadEndpointSync(handle->adb_read_pipe,
+                                  (void*)data,
+                                  (unsigned long)xfer,
+                                  &read,
+                                  time_out);
+      errno = GetLastError();
+      D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
+      if (ret) {
+        data += read;
+        len -= read;
+
+        if (len == 0)
+          return 0;
+      } else if (errno != ERROR_SEM_TIMEOUT) {
+        // assume ERROR_INVALID_HANDLE indicates we are disconnected
+        if (errno == ERROR_INVALID_HANDLE)
+          usb_kick(handle);
+        break;
+      }
+    }
+  } else {
+    D("usb_read NULL handle\n");
+    SetLastError(ERROR_INVALID_HANDLE);
+  }
+
+  D("usb_read failed: %d\n", errno);
+
+  return -1;
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+  if (NULL != handle) {
+    if (NULL != handle->interface_name)
+      free(handle->interface_name);
+    if (NULL != handle->adb_write_pipe)
+      AdbCloseHandle(handle->adb_write_pipe);
+    if (NULL != handle->adb_read_pipe)
+      AdbCloseHandle(handle->adb_read_pipe);
+    if (NULL != handle->adb_interface)
+      AdbCloseHandle(handle->adb_interface);
+
+    handle->interface_name = NULL;
+    handle->adb_write_pipe = NULL;
+    handle->adb_read_pipe = NULL;
+    handle->adb_interface = NULL;
+  }
+}
+
+void usb_kick(usb_handle* handle) {
+  if (NULL != handle) {
+    adb_mutex_lock(&usb_lock);
+
+    usb_cleanup_handle(handle);
+
+    adb_mutex_unlock(&usb_lock);
+  } else {
+    SetLastError(ERROR_INVALID_HANDLE);
+    errno = ERROR_INVALID_HANDLE;
+  }
+}
+
+int usb_close(usb_handle* handle) {
+  D("usb_close\n");
+
+  if (NULL != handle) {
+    // Remove handle from the list
+    adb_mutex_lock(&usb_lock);
+
+    if ((handle->next != handle) && (handle->prev != handle)) {
+      handle->next->prev = handle->prev;
+      handle->prev->next = handle->next;
+      handle->prev = handle;
+      handle->next = handle;
+    }
+
+    adb_mutex_unlock(&usb_lock);
+
+    // Cleanup handle
+    usb_cleanup_handle(handle);
+    free(handle);
+  }
+
+  return 0;
+}
+
+const char *usb_name(usb_handle* handle) {
+  if (NULL == handle) {
+    SetLastError(ERROR_INVALID_HANDLE);
+    errno = ERROR_INVALID_HANDLE;
+    return NULL;
+  }
+
+  return (const char*)handle->interface_name;
+}
+
+int recognized_device(usb_handle* handle) {
+  if (NULL == handle)
+    return 0;
+
+  // Check vendor and product id first
+  USB_DEVICE_DESCRIPTOR device_desc;
+
+  if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+                                 &device_desc)) {
+    return 0;
+  }
+
+  // Then check interface properties
+  USB_INTERFACE_DESCRIPTOR interf_desc;
+
+  if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+                                    &interf_desc)) {
+    return 0;
+  }
+
+  // Must have two endpoints
+  if (2 != interf_desc.bNumEndpoints) {
+    return 0;
+  }
+
+  if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
+      interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
+
+    if(interf_desc.bInterfaceProtocol == 0x01) {
+      AdbEndpointInformation endpoint_info;
+      // assuming zero is a valid bulk endpoint ID
+      if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
+        handle->zero_mask = endpoint_info.max_packet_size - 1;
+      }
+    }
+
+    return 1;
+  }
+
+  return 0;
+}
+
+void find_devices() {
+	usb_handle* handle = NULL;
+  char entry_buffer[2048];
+  char interf_name[2048];
+  AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+  unsigned long entry_buffer_size = sizeof(entry_buffer);
+  char* copy_name;
+
+  // Enumerate all present and active interfaces.
+  ADBAPIHANDLE enum_handle =
+    AdbEnumInterfaces(usb_class_id, true, true, true);
+
+  if (NULL == enum_handle)
+    return;
+
+  while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+    // TODO: FIXME - temp hack converting wchar_t into char.
+    // It would be better to change AdbNextInterface so it will return
+    // interface name as single char string.
+    const wchar_t* wchar_name = next_interface->device_name;
+    for(copy_name = interf_name;
+        L'\0' != *wchar_name;
+        wchar_name++, copy_name++) {
+      *copy_name = (char)(*wchar_name);
+    }
+    *copy_name = '\0';
+
+    // Lets see if we already have this device in the list
+    if (!known_device(interf_name)) {
+      // This seems to be a new device. Open it!
+        handle = do_usb_open(next_interface->device_name);
+        if (NULL != handle) {
+        // Lets see if this interface (device) belongs to us
+        if (recognized_device(handle)) {
+          D("adding a new device %s\n", interf_name);
+          char serial_number[512];
+          unsigned long serial_number_len = sizeof(serial_number);
+          if (AdbGetSerialNumber(handle->adb_interface,
+                                serial_number,
+                                &serial_number_len,
+                                true)) {
+            // Lets make sure that we don't duplicate this device
+            if (register_new_device(handle)) {
+              register_usb_transport(handle, serial_number);
+            } else {
+              D("register_new_device failed for %s\n", interf_name);
+              usb_cleanup_handle(handle);
+              free(handle);
+            }
+          } else {
+            D("cannot get serial number\n");
+            usb_cleanup_handle(handle);
+            free(handle);
+          }
+        } else {
+          usb_cleanup_handle(handle);
+          free(handle);
+        }
+      }
+    }
+
+    entry_buffer_size = sizeof(entry_buffer);
+  }
+
+  AdbCloseHandle(enum_handle);
+}
diff --git a/adb/utils.c b/adb/utils.c
new file mode 100644
index 0000000..91518ba
--- /dev/null
+++ b/adb/utils.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "utils.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+char*
+buff_addc (char*  buff, char*  buffEnd, int  c)
+{
+    int  avail = buffEnd - buff;
+
+    if (avail <= 0)  /* already in overflow mode */
+        return buff;
+
+    if (avail == 1) {  /* overflowing, the last byte is reserved for zero */
+        buff[0] = 0;
+        return buff + 1;
+    }
+
+    buff[0] = (char) c;  /* add char and terminating zero */
+    buff[1] = 0;
+    return buff + 1;
+}
+
+char*
+buff_adds (char*  buff, char*  buffEnd, const char*  s)
+{
+    int  slen = strlen(s);
+
+    return buff_addb(buff, buffEnd, s, slen);
+}
+
+char*
+buff_addb (char*  buff, char*  buffEnd, const void*  data, int  len)
+{
+    int  avail = (buffEnd - buff);
+
+    if (avail <= 0 || len <= 0)  /* already overflowing */
+        return buff;
+
+    if (len > avail)
+        len = avail;
+
+    memcpy(buff, data, len);
+
+    buff += len;
+
+    /* ensure there is a terminating zero */
+    if (buff >= buffEnd) {  /* overflow */
+        buff[-1] = 0;
+    } else
+        buff[0] = 0;
+
+    return buff;
+}
+
+char*
+buff_add  (char*  buff, char*  buffEnd, const char*  format, ... )
+{
+    int      avail;
+
+    avail = (buffEnd - buff);
+
+    if (avail > 0) {
+        va_list  args;
+        int      nn;
+
+        va_start(args, format);
+        nn = vsnprintf( buff, avail, format, args);
+        va_end(args);
+
+        if (nn < 0) {
+            /* some C libraries return -1 in case of overflow,
+             * but they will also do that if the format spec is
+             * invalid. We assume ADB is not buggy enough to
+             * trigger that last case. */
+            nn = avail;
+        }
+        else if (nn > avail) {
+            nn = avail;
+        }
+
+        buff += nn;
+
+        /* ensure that there is a terminating zero */
+        if (buff >= buffEnd)
+            buff[-1] = 0;
+        else
+            buff[0] = 0;
+    }
+    return buff;
+}
diff --git a/adb/utils.h b/adb/utils.h
new file mode 100644
index 0000000..f70ecd2
--- /dev/null
+++ b/adb/utils.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _ADB_UTILS_H
+#define _ADB_UTILS_H
+
+/* bounded buffer functions */
+
+/* all these functions are used to append data to a bounded buffer.
+ *
+ * after each operation, the buffer is guaranteed to be zero-terminated,
+ * even in the case of an overflow. they all return the new buffer position
+ * which allows one to use them in succession, only checking for overflows
+ * at the end. For example:
+ *
+ *    BUFF_DECL(temp,p,end,1024);
+ *    char*    p;
+ *
+ *    p = buff_addc(temp, end, '"');
+ *    p = buff_adds(temp, end, string);
+ *    p = buff_addc(temp, end, '"');
+ *
+ *    if (p >= end) {
+ *        overflow detected. note that 'temp' is
+ *        zero-terminated for safety. 
+ *    }
+ *    return strdup(temp);
+ */
+
+/* tries to add a character to the buffer, in case of overflow
+ * this will only write a terminating zero and return buffEnd.
+ */
+char*   buff_addc (char*  buff, char*  buffEnd, int  c);
+
+/* tries to add a string to the buffer */
+char*   buff_adds (char*  buff, char*  buffEnd, const char*  s);
+
+/* tries to add a bytes to the buffer. the input can contain zero bytes,
+ * but a terminating zero will always be appended at the end anyway
+ */
+char*   buff_addb (char*  buff, char*  buffEnd, const void*  data, int  len);
+
+/* tries to add a formatted string to a bounded buffer */
+char*   buff_add  (char*  buff, char*  buffEnd, const char*  format, ... );
+
+/* convenience macro used to define a bounded buffer, as well as
+ * a 'cursor' and 'end' variables all in one go.
+ *
+ * note: this doesn't place an initial terminating zero in the buffer,
+ * you need to use one of the buff_ functions for this. or simply
+ * do _cursor[0] = 0 manually.
+ */
+#define  BUFF_DECL(_buff,_cursor,_end,_size)   \
+    char   _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
+
+#endif /* _ADB_UTILS_H */
diff --git a/cpio/Android.mk b/cpio/Android.mk
new file mode 100644
index 0000000..8d01852
--- /dev/null
+++ b/cpio/Android.mk
@@ -0,0 +1,13 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	mkbootfs.c
+
+LOCAL_MODULE := mkbootfs
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
new file mode 100644
index 0000000..f672336
--- /dev/null
+++ b/cpio/mkbootfs.c
@@ -0,0 +1,261 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include <private/android_filesystem_config.h>
+
+/* NOTES
+**
+** - see buffer-format.txt from the linux kernel docs for
+**   an explanation of this file format
+** - dotfiles are ignored
+** - directories named 'root' are ignored
+** - device notes, pipes, etc are not supported (error)
+*/
+
+void die(const char *why, ...)
+{
+    va_list ap;
+
+    va_start(ap, why);
+    fprintf(stderr,"error: ");
+    vfprintf(stderr, why, ap);
+    fprintf(stderr,"\n");
+    va_end(ap);
+    exit(1);
+}
+
+static int verbose = 0;
+static int total_size = 0;
+
+static void fix_stat(const char *path, struct stat *s)
+{
+    fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
+}
+
+static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
+{
+    // Nothing is special about this value, just picked something in the
+    // approximate range that was being used already, and avoiding small
+    // values which may be special.
+    static unsigned next_inode = 300000;
+
+    while(total_size & 3) {
+        total_size++;
+        putchar(0);
+    }
+
+    fix_stat(out, s);
+//    fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
+
+    printf("%06x%08x%08x%08x%08x%08x%08x"
+           "%08x%08x%08x%08x%08x%08x%08x%s%c",
+           0x070701,
+           next_inode++,  //  s.st_ino,
+           s->st_mode,
+           0, // s.st_uid,
+           0, // s.st_gid,
+           1, // s.st_nlink,
+           0, // s.st_mtime,
+           datasize,
+           0, // volmajor
+           0, // volminor
+           0, // devmajor
+           0, // devminor,
+           olen + 1,
+           0,
+           out,
+           0
+           );
+
+    total_size += 6 + 8*13 + olen + 1;
+
+    if(strlen(out) != olen) die("ACK!");
+
+    while(total_size & 3) {
+        total_size++;
+        putchar(0);
+    }
+
+    if(datasize) {
+        fwrite(data, datasize, 1, stdout);
+        total_size += datasize;
+    }
+}
+
+static void _eject_trailer()
+{
+    struct stat s;
+    memset(&s, 0, sizeof(s));
+    _eject(&s, "TRAILER!!!", 10, 0, 0);
+
+    while(total_size & 0xff) {
+        total_size++;
+        putchar(0);
+    }
+}
+
+static void _archive(char *in, char *out, int ilen, int olen);
+
+static int compare(const void* a, const void* b) {
+  return strcmp(*(const char**)a, *(const char**)b);
+}
+
+static void _archive_dir(char *in, char *out, int ilen, int olen)
+{
+    int i, t;
+    DIR *d;
+    struct dirent *de;
+
+    if(verbose) {
+        fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
+                in, out, ilen, olen);
+    }
+
+    d = opendir(in);
+    if(d == 0) die("cannot open directory '%s'", in);
+
+    int size = 32;
+    int entries = 0;
+    char** names = malloc(size * sizeof(char*));
+    if (names == NULL) {
+      fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
+      exit(1);
+    }
+
+    while((de = readdir(d)) != 0){
+            /* xxx: feature? maybe some dotfiles are okay */
+        if(de->d_name[0] == '.') continue;
+
+            /* xxx: hack. use a real exclude list */
+        if(!strcmp(de->d_name, "root")) continue;
+
+        if (entries >= size) {
+          size *= 2;
+          names = realloc(names, size * sizeof(char*));
+          if (names == NULL) {
+            fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
+                    size);
+            exit(1);
+          }
+        }
+        names[entries] = strdup(de->d_name);
+        if (names[entries] == NULL) {
+          fprintf(stderr, "failed to strdup name \"%s\"\n",
+                  de->d_name);
+          exit(1);
+        }
+        ++entries;
+    }
+
+    qsort(names, entries, sizeof(char*), compare);
+
+    for (i = 0; i < entries; ++i) {
+        t = strlen(names[i]);
+        in[ilen] = '/';
+        memcpy(in + ilen + 1, names[i], t + 1);
+
+        if(olen > 0) {
+            out[olen] = '/';
+            memcpy(out + olen + 1, names[i], t + 1);
+            _archive(in, out, ilen + t + 1, olen + t + 1);
+        } else {
+            memcpy(out, names[i], t + 1);
+            _archive(in, out, ilen + t + 1, t);
+        }
+
+        in[ilen] = 0;
+        out[olen] = 0;
+
+        free(names[i]);
+    }
+    free(names);
+}
+
+static void _archive(char *in, char *out, int ilen, int olen)
+{
+    struct stat s;
+
+    if(verbose) {
+        fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
+                in, out, ilen, olen);
+    }
+
+    if(lstat(in, &s)) die("could not stat '%s'\n", in);
+
+    if(S_ISREG(s.st_mode)){
+        char *tmp;
+        int fd;
+
+        fd = open(in, O_RDONLY);
+        if(fd < 0) die("cannot open '%s' for read", in);
+
+        tmp = (char*) malloc(s.st_size);
+        if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
+
+        if(read(fd, tmp, s.st_size) != s.st_size) {
+            die("cannot read %d bytes", s.st_size);
+        }
+
+        _eject(&s, out, olen, tmp, s.st_size);
+
+        free(tmp);
+        close(fd);
+    } else if(S_ISDIR(s.st_mode)) {
+        _eject(&s, out, olen, 0, 0);
+        _archive_dir(in, out, ilen, olen);
+    } else if(S_ISLNK(s.st_mode)) {
+        char buf[1024];
+        int size;
+        size = readlink(in, buf, 1024);
+        if(size < 0) die("cannot read symlink '%s'", in);
+        _eject(&s, out, olen, buf, size);
+    } else {
+        die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
+    }
+}
+
+void archive(const char *start, const char *prefix)
+{
+    char in[8192];
+    char out[8192];
+
+    strcpy(in, start);
+    strcpy(out, prefix);
+
+    _archive_dir(in, out, strlen(in), strlen(out));
+}
+
+int main(int argc, char *argv[])
+{
+    argc--;
+    argv++;
+
+    if(argc == 0) die("no directories to process?!");
+
+    while(argc-- > 0){
+        char *x = strchr(*argv, '=');
+        if(x != 0) {
+            *x++ = 0;
+        } else {
+            x = "";
+        }
+
+        archive(*argv, x);
+
+        argv++;
+    }
+
+    _eject_trailer();
+
+    return 0;
+}
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
new file mode 100644
index 0000000..b22e1a8
--- /dev/null
+++ b/debuggerd/Android.mk
@@ -0,0 +1,26 @@
+# Copyright 2005 The Android Open Source Project
+
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c
+LOCAL_CFLAGS := -Wall
+LOCAL_MODULE := debuggerd
+
+LOCAL_STATIC_LIBRARIES := libcutils libc
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.c 
+LOCAL_SRC_FILES += crashglue.S
+LOCAL_MODULE := crasher 
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libcutils libc
+include $(BUILD_EXECUTABLE)
+
+endif # TARGET_ARCH == arm
diff --git a/debuggerd/MODULE_LICENSE_APACHE2 b/debuggerd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/debuggerd/MODULE_LICENSE_APACHE2
diff --git a/debuggerd/NOTICE b/debuggerd/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/debuggerd/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
new file mode 100644
index 0000000..f4a5a62
--- /dev/null
+++ b/debuggerd/crasher.c
@@ -0,0 +1,105 @@
+
+//#include <cutils/misc.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+#include <pthread.h>
+
+#include <cutils/sockets.h>
+
+void crash1(void);
+void crashnostack(void);
+
+static void debuggerd_connect()
+{
+    char tmp[1];
+    int s;
+    sprintf(tmp, "%d", gettid());
+    s = socket_local_client("android:debuggerd",
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);    
+    if(s >= 0) {
+        read(s, tmp, 1);
+        close(s);
+    }
+}
+
+void test_call1()
+{
+    *((int*) 32) = 1;
+}
+
+void *test_thread(void *x)
+{
+    printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid());
+
+    sleep(1);
+    test_call1();
+    printf("goodbye\n");
+
+    return 0;
+}
+
+void *noisy(void *x)
+{
+    char c = (unsigned) x;
+    for(;;) {
+        usleep(250*1000);
+        write(2, &c, 1);
+        if(c == 'C') *((unsigned*) 0) = 42;
+    }
+    return 0;
+}
+
+int ctest()
+{
+    pthread_t thr;
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create(&thr, &attr, noisy, (void*) 'A');
+    pthread_create(&thr, &attr, noisy, (void*) 'B');
+    pthread_create(&thr, &attr, noisy, (void*) 'C');
+    for(;;) ;
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    pthread_t thr;
+    pthread_attr_t attr;
+
+    fprintf(stderr,"crasher: " __TIME__ "!@\n");
+    fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
+
+    if(argc > 1) {
+        if(!strcmp(argv[1],"nostack")) crashnostack();
+        if(!strcmp(argv[1],"ctest")) return ctest();
+        if(!strcmp(argv[1],"exit")) exit(1);
+        if(!strcmp(argv[1],"abort")) maybeabort();
+        
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        pthread_create(&thr, &attr, test_thread, 0);
+        while(1) sleep(1);
+    } else {
+        crash1();
+//        *((int*) 0) = 42;
+    }
+    
+    return 0;
+}
+
+void maybeabort()
+{
+    if(time(0) != 42) abort();
+}
diff --git a/debuggerd/crashglue.S b/debuggerd/crashglue.S
new file mode 100644
index 0000000..888951b
--- /dev/null
+++ b/debuggerd/crashglue.S
@@ -0,0 +1,28 @@
+.globl crash1
+.globl crashnostack
+		
+crash1:
+	ldr r0, =0xa5a50000
+	ldr r1, =0xa5a50001
+	ldr r2, =0xa5a50002
+	ldr r3, =0xa5a50003
+	ldr r4, =0xa5a50004
+	ldr r5, =0xa5a50005
+	ldr r6, =0xa5a50006
+	ldr r7, =0xa5a50007
+	ldr r8, =0xa5a50008
+	ldr r9, =0xa5a50009
+	ldr r10, =0xa5a50010
+	ldr r11, =0xa5a50011
+	ldr r12, =0xa5a50012
+
+	mov lr, #0
+	ldr lr, [lr]
+	b .
+
+
+crashnostack:
+	mov sp, #0
+	mov r0, #0
+	ldr r0, [r0]
+	b .
\ No newline at end of file
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
new file mode 100644
index 0000000..9394e1c
--- /dev/null
+++ b/debuggerd/debuggerd.c
@@ -0,0 +1,852 @@
+/* system/debuggerd/debuggerd.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <sys/exec_elf.h>
+#include <sys/stat.h>
+
+#include <cutils/sockets.h>
+#include <cutils/logd.h>
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+
+#include <linux/input.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "utility.h"
+
+/* Main entry point to get the backtrace from the crashing process */
+extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
+                                        unsigned int sp_list[],
+                                        int *frame0_pc_sane,
+                                        bool at_fault);
+
+static char **process_name_ptr;
+
+static int logsocket = -1;
+
+#define ANDROID_LOG_INFO 4
+
+/* Log information onto the tombstone */
+void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
+{
+    char buf[128];
+    
+    va_list ap;
+    va_start(ap, fmt);
+
+    if (tfd >= 0) {
+        int len;
+        vsnprintf(buf, sizeof(buf), fmt, ap);
+        len = strlen(buf);
+        if(tfd >= 0) write(tfd, buf, len);
+    }
+
+    if (!in_tombstone_only)
+        __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
+}
+
+#define LOG(fmt...) _LOG(-1, 0, fmt)
+#if 0
+#define XLOG(fmt...) _LOG(-1, 0, fmt)
+#else
+#define XLOG(fmt...) do {} while(0)
+#endif
+
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
+// 012345678901234567890123456789012345678901234567890123456789
+// 0         1         2         3         4         5
+
+mapinfo *parse_maps_line(char *line)
+{
+    mapinfo *mi;
+    int len = strlen(line);
+
+    if(len < 1) return 0;
+    line[--len] = 0;
+    
+    if(len < 50) return 0;
+    if(line[20] != 'x') return 0;
+
+    mi = malloc(sizeof(mapinfo) + (len - 47));
+    if(mi == 0) return 0;
+    
+    mi->start = strtoul(line, 0, 16);
+    mi->end = strtoul(line + 9, 0, 16);
+    /* To be filled in parse_exidx_info if the mapped section starts with 
+     * elf_header
+     */
+    mi->exidx_start = mi->exidx_end = 0;
+    mi->next = 0;
+    strcpy(mi->name, line + 49);
+
+    return mi;
+}
+
+void dump_build_info(int tfd)
+{
+    char fingerprint[PROPERTY_VALUE_MAX];
+
+    property_get("ro.build.fingerprint", fingerprint, "unknown");
+
+    _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint);
+}
+
+
+void dump_stack_and_code(int tfd, int pid, mapinfo *map, 
+                         int unwind_depth, unsigned int sp_list[],
+                         int frame0_pc_sane, bool at_fault)
+{
+    unsigned int sp, pc, p, end, data;
+    struct pt_regs r;
+    int sp_depth;
+    bool only_in_tombstone = !at_fault;
+
+    if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return;
+    sp = r.ARM_sp;
+    pc = r.ARM_pc;
+
+    /* Died because calling the weeds - dump
+     * the code around the PC in the next frame instead.
+     */
+    if (frame0_pc_sane == 0) {
+        pc = r.ARM_lr;
+    }
+
+    _LOG(tfd, true, "code%s:\n", frame0_pc_sane ? "" : " (around frame #01)");
+
+    end = p = pc & ~3;
+    p -= 16;
+
+    /* Dump the code as:
+     *  PC         contents
+     *  00008d34   fffffcd0 4c0eb530 b0934a0e 1c05447c
+     *  00008d44   f7ff18a0 490ced94 68035860 d0012b00
+     */
+    while (p <= end) {
+        int i;
+
+        _LOG(tfd, true, " %08x  ", p);
+        for (i = 0; i < 4; i++) {
+            data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
+            _LOG(tfd, true, " %08x", data);
+            p += 4;
+        }
+        _LOG(tfd, true, "\n", p);
+    }
+
+    p = sp - 64;
+    p &= ~3;
+    if (unwind_depth != 0) {
+        if (unwind_depth < STACK_CONTENT_DEPTH) {
+            end = sp_list[unwind_depth-1];
+        }
+        else {
+            end = sp_list[STACK_CONTENT_DEPTH-1];
+        }
+    }
+    else {
+        end = sp | 0x000000ff;
+        end += 0xff;
+    }
+
+    _LOG(tfd, only_in_tombstone, "stack:\n");
+
+    /* If the crash is due to PC == 0, there will be two frames that
+     * have identical SP value.
+     */
+    if (sp_list[0] == sp_list[1]) {
+        sp_depth = 1;
+    }
+    else {
+        sp_depth = 0;
+    }
+
+    while (p <= end) {
+         char *prompt; 
+         char level[16];
+         data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
+         if (p == sp_list[sp_depth]) {
+             sprintf(level, "#%02d", sp_depth++);
+             prompt = level;
+         }
+         else {
+             prompt = "   ";
+         }
+         
+         /* Print the stack content in the log for the first 3 frames. For the
+          * rest only print them in the tombstone file.
+          */
+         _LOG(tfd, (sp_depth > 2) || only_in_tombstone, 
+              "%s %08x  %08x  %s\n", prompt, p, data, 
+              map_to_name(map, data, ""));
+         p += 4;
+    }
+    /* print another 64-byte of stack data after the last frame */
+
+    end = p+64;
+    while (p <= end) {
+         data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
+         _LOG(tfd, (sp_depth > 2) || only_in_tombstone, 
+              "    %08x  %08x  %s\n", p, data, 
+              map_to_name(map, data, ""));
+         p += 4;
+    }
+}
+
+void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, 
+                    bool at_fault)
+{
+    struct pt_regs r;
+
+    if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
+        _LOG(tfd, !at_fault, "tid %d not responding!\n", pid);
+        return;
+    }
+
+    if (unwound_level == 0) {
+        _LOG(tfd, !at_fault, "         #%02d  pc %08x  %s\n", 0, r.ARM_pc,
+             map_to_name(map, r.ARM_pc, "<unknown>"));
+    }
+    _LOG(tfd, !at_fault, "         #%02d  lr %08x  %s\n", 1, r.ARM_lr,
+            map_to_name(map, r.ARM_lr, "<unknown>"));
+}
+
+void dump_registers(int tfd, int pid, bool at_fault) 
+{
+    struct pt_regs r;
+    bool only_in_tombstone = !at_fault;
+
+    if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
+        _LOG(tfd, only_in_tombstone, 
+             "cannot get registers: %s\n", strerror(errno));
+        return;
+    }
+    
+    _LOG(tfd, only_in_tombstone, " r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",
+         r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3);
+    _LOG(tfd, only_in_tombstone, " r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",
+         r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7);
+    _LOG(tfd, only_in_tombstone, " r8 %08x  r9 %08x  10 %08x  fp %08x\n",
+         r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp);
+    _LOG(tfd, only_in_tombstone, 
+         " ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n",
+         r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr);  
+}
+
+const char *get_signame(int sig)
+{
+    switch(sig) {
+    case SIGILL:     return "SIGILL";
+    case SIGABRT:    return "SIGABRT";
+    case SIGBUS:     return "SIGBUS";
+    case SIGFPE:     return "SIGFPE";
+    case SIGSEGV:    return "SIGSEGV";
+    case SIGSTKFLT:  return "SIGSTKFLT";
+    default:         return "?";
+    }
+}
+
+void dump_fault_addr(int tfd, int pid, int sig)
+{
+    siginfo_t si;
+    
+    memset(&si, 0, sizeof(si));
+    if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){
+        _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
+    } else {
+        _LOG(tfd, false, "signal %d (%s), fault addr %08x\n",
+            sig, get_signame(sig), si.si_addr);
+    }
+}
+
+void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
+{
+    char data[1024];
+    char *x = 0;
+    FILE *fp;
+    
+    sprintf(data, "/proc/%d/cmdline", pid);
+    fp = fopen(data, "r");
+    if(fp) {
+        x = fgets(data, 1024, fp);
+        fclose(fp);
+    }
+    
+    _LOG(tfd, false,
+         "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+    dump_build_info(tfd);
+    _LOG(tfd, false, "pid: %d, tid: %d  >>> %s <<<\n",
+         pid, tid, x ? x : "UNKNOWN");
+    
+    if(sig) dump_fault_addr(tfd, tid, sig);
+}
+
+static void parse_exidx_info(mapinfo *milist, pid_t pid)
+{
+    mapinfo *mi;
+    for (mi = milist; mi != NULL; mi = mi->next) {
+        Elf32_Ehdr ehdr;
+
+        memset(&ehdr, 0, sizeof(Elf32_Ehdr));
+        /* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of 
+         * mapped section.
+         */
+        get_remote_struct(pid, (void *) (mi->start), &ehdr, 
+                          sizeof(Elf32_Ehdr));
+        /* Check if it has the matching magic words */
+        if (IS_ELF(ehdr)) {
+            Elf32_Phdr phdr;
+            Elf32_Phdr *ptr;
+            int i;
+
+            ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff);
+            for (i = 0; i < ehdr.e_phnum; i++) {
+                /* Parse the program header */
+                get_remote_struct(pid, (void *) ptr+i, &phdr, 
+                                  sizeof(Elf32_Phdr));
+                /* Found a EXIDX segment? */
+                if (phdr.p_type == PT_ARM_EXIDX) {
+                    mi->exidx_start = mi->start + phdr.p_offset;
+                    mi->exidx_end = mi->exidx_start + phdr.p_filesz;
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
+{
+    char data[1024];
+    FILE *fp;
+    mapinfo *milist = 0;
+    unsigned int sp_list[STACK_CONTENT_DEPTH];
+    int stack_depth;
+    int frame0_pc_sane = 1;
+    
+    if (!at_fault) {
+        _LOG(tfd, true,
+         "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+        _LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid);
+    }
+
+    dump_registers(tfd, tid, at_fault);
+    
+    /* Clear stack pointer records */
+    memset(sp_list, 0, sizeof(sp_list));
+
+    sprintf(data, "/proc/%d/maps", pid);
+    fp = fopen(data, "r");
+    if(fp) {
+        while(fgets(data, 1024, fp)) {
+            mapinfo *mi = parse_maps_line(data);
+            if(mi) {
+                mi->next = milist;
+                milist = mi;
+            }
+        }
+        fclose(fp);
+    }
+
+    parse_exidx_info(milist, tid);
+
+    /* If stack unwinder fails, use the default solution to dump the stack
+     * content.
+     */
+    stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list,
+                                               &frame0_pc_sane, at_fault);
+
+    /* The stack unwinder should at least unwind two levels of stack. If less
+     * level is seen we make sure at lease pc and lr are dumped.
+     */
+    if (stack_depth < 2) {
+        dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault);
+    }
+
+    dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane,
+                        at_fault);
+
+    while(milist) {
+        mapinfo *next = milist->next;
+        free(milist);
+        milist = next;
+    }
+}
+
+/* FIXME: unused: use it or lose it*/
+#if 0
+static
+void start_gdbserver_vs(int pid, int port)
+{
+    pid_t p;
+    char *args[5];
+    char commspec[16];
+    char pidspec[16];
+    
+    p = fork();
+    if(p < 0) {
+        LOG("could not fork()\n");
+        return;
+    }
+
+    if(p == 0) {
+        sprintf(commspec, ":%d", port);
+        sprintf(pidspec, "%d", pid);
+        args[0] = "/system/bin/gdbserver";
+        args[1] = commspec;
+        args[2] = "--attach";
+        args[3] = pidspec;
+        args[4] = 0;
+        exit(execv(args[0], args));
+    } else {
+        LOG("gdbserver pid=%d port=%d targetpid=%d\n",
+            p, port, pid);
+
+        sleep(5);
+    }
+}
+#endif
+
+#define MAX_TOMBSTONES	10
+
+#define typecheck(x,y) {    \
+    typeof(x) __dummy1;     \
+    typeof(y) __dummy2;     \
+    (void)(&__dummy1 == &__dummy2); }
+
+#define TOMBSTONE_DIR	"/data/tombstones"
+
+/*
+ * find_and_open_tombstone - find an available tombstone slot, if any, of the
+ * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
+ * file is available, we reuse the least-recently-modified file.
+ */
+static int find_and_open_tombstone(void)
+{
+    unsigned long mtime = ULONG_MAX;
+    struct stat sb;
+    char path[128];
+    int fd, i, oldest = 0;
+
+    /*
+     * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
+     * to, our logic breaks. This check will generate a warning if that happens.
+     */
+    typecheck(mtime, sb.st_mtime);
+
+    /*
+     * In a single wolf-like pass, find an available slot and, in case none
+     * exist, find and record the least-recently-modified file.
+     */
+    for (i = 0; i < MAX_TOMBSTONES; i++) {
+        snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
+
+        if (!stat(path, &sb)) {
+            if (sb.st_mtime < mtime) {
+                oldest = i;
+                mtime = sb.st_mtime;
+            }
+            continue;
+        }
+        if (errno != ENOENT)
+            continue;
+
+        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
+        if (fd < 0)
+            continue;	/* raced ? */
+
+        fchown(fd, AID_SYSTEM, AID_SYSTEM);
+        return fd;
+    }
+
+    /* we didn't find an available file, so we clobber the oldest one */
+    snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
+    fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+    fchown(fd, AID_SYSTEM, AID_SYSTEM);
+
+    return fd;
+}
+
+/* Return true if some thread is not detached cleanly */
+static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
+{
+    char task_path[1024];
+
+    sprintf(task_path, "/proc/%d/task", pid);
+    DIR *d;
+    struct dirent *de;
+    int need_cleanup = 0;
+
+    d = opendir(task_path);
+    /* Bail early if cannot open the task directory */
+    if (d == NULL) {
+        XLOG("Cannot open /proc/%d/task\n", pid);
+        return false;
+    }
+    while ((de = readdir(d)) != NULL) {
+        unsigned new_tid;
+        /* Ignore "." and ".." */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 
+            continue;
+        new_tid = atoi(de->d_name);
+        /* The main thread at fault has been handled individually */
+        if (new_tid == tid)
+            continue;
+
+        /* Skip this thread if cannot ptrace it */
+        if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0)
+            continue;
+
+        dump_crash_report(tfd, pid, new_tid, false);
+        need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0);
+    }
+    closedir(d);
+    return need_cleanup != 0;
+}
+
+/* Return true if some thread is not detached cleanly */
+static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, 
+                              int signal)
+{
+    int fd;
+    bool need_cleanup = false;
+
+    mkdir(TOMBSTONE_DIR, 0755);
+    chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
+
+    fd = find_and_open_tombstone();
+    if (fd < 0)
+        return need_cleanup;
+
+    dump_crash_banner(fd, pid, tid, signal);
+    dump_crash_report(fd, pid, tid, true);
+    /* 
+     * If the user has requested to attach gdb, don't collect the per-thread
+     * information as it increases the chance to lose track of the process.
+     */
+    if ((signed)pid > debug_uid) {
+        need_cleanup = dump_sibling_thread_report(fd, pid, tid);
+    }
+
+    close(fd);
+    return need_cleanup;
+}
+
+static int
+write_string(const char* file, const char* string)
+{
+    int len;
+    int fd;
+    ssize_t amt;
+    fd = open(file, O_RDWR);
+    len = strlen(string);
+    if (fd < 0)
+        return -errno;
+    amt = write(fd, string, len);
+    close(fd);
+    return amt >= 0 ? 0 : -errno;
+}
+
+static
+void init_debug_led(void)
+{
+    // trout leds
+    write_string("/sys/class/leds/red/brightness", "0");
+    write_string("/sys/class/leds/green/brightness", "0");
+    write_string("/sys/class/leds/blue/brightness", "0");
+    write_string("/sys/class/leds/red/device/blink", "0");
+    // sardine leds
+    write_string("/sys/class/leds/left/cadence", "0,0");
+}
+
+static
+void enable_debug_led(void)
+{
+    // trout leds
+    write_string("/sys/class/leds/red/brightness", "255");
+    // sardine leds
+    write_string("/sys/class/leds/left/cadence", "1,0");
+}
+
+static
+void disable_debug_led(void)
+{
+    // trout leds
+    write_string("/sys/class/leds/red/brightness", "0");
+    // sardine leds
+    write_string("/sys/class/leds/left/cadence", "0,0");
+}
+
+extern int init_getevent();
+extern void uninit_getevent();
+extern int get_event(struct input_event* event, int timeout);
+
+static void wait_for_user_action(unsigned tid, struct ucred* cr)
+{
+    (void)tid;
+    /* First log a helpful message */
+    LOG(    "********************************************************\n"
+            "* process %d crashed. debuggerd waiting for gdbserver   \n"
+            "*                                                       \n"
+            "*     adb shell gdbserver :port --attach %d &           \n"
+            "*                                                       \n"
+            "* and press the HOME key.                               \n"
+            "********************************************************\n",                
+            cr->pid, cr->pid);
+
+    /* wait for HOME key */
+    if (init_getevent() == 0) {
+        int ms = 1200 / 10;
+        int dit = 1;
+        int dah = 3*dit;
+        int _       = -dit;
+        int ___     = 3*_;
+        int _______ = 7*_;
+        const signed char codes[] = {
+           dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
+        };
+        size_t s = 0;
+        struct input_event e;
+        int home = 0;
+        init_debug_led();
+        enable_debug_led();
+        do {
+            int timeout = abs((int)(codes[s])) * ms;
+            int res = get_event(&e, timeout);
+            if (res == 0) {
+                if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0)
+                    home = 1;
+            } else if (res == 1) {
+                if (++s >= sizeof(codes)/sizeof(*codes))
+                    s = 0;
+                if (codes[s] > 0) {
+                    enable_debug_led();
+                } else {
+                    disable_debug_led();
+                }
+            }
+        } while (!home); 
+        uninit_getevent();
+    }
+
+    /* don't forget to turn debug led off */
+    disable_debug_led();
+    
+    /* close filedescriptor */
+    LOG("debuggerd resuming process %d", cr->pid);
+ }
+
+static void handle_crashing_process(int fd)
+{
+    char buf[64];
+    struct stat s;
+    unsigned tid;
+    struct ucred cr;
+    int n, len, status; 
+    int tid_attach_status = -1;
+    unsigned retry = 30;
+    bool need_cleanup = false;
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.db.uid", value, "-1");
+    int debug_uid = atoi(value);
+    
+    XLOG("handle_crashing_process(%d)\n", fd);
+    
+    len = sizeof(cr);
+    n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+    if(n != 0) {
+        LOG("cannot get credentials\n");
+        goto done;
+    }
+
+    XLOG("reading tid\n");    
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) {
+        if(errno == EINTR) continue;
+        if(errno == EWOULDBLOCK) {
+            if(retry-- > 0) {
+                usleep(100 * 1000);
+                continue;
+            }
+            LOG("timed out reading tid\n");
+            goto done;
+        }
+        LOG("read failure? %s\n", strerror(errno));
+        goto done;
+    }
+
+    sprintf(buf,"/proc/%d/task/%d", cr.pid, tid);
+    if(stat(buf, &s)) {
+        LOG("tid %d does not exist in pid %d. ignorning debug request\n",
+            tid, cr.pid);
+        close(fd);
+        return;
+    }
+    
+    XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid);
+
+    tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0);
+    if(tid_attach_status < 0) {
+        LOG("ptrace attach failed: %s\n", strerror(errno));
+        goto done;
+    }
+
+    close(fd);
+    fd = -1;
+
+    for(;;) {
+        n = waitpid(tid, &status, __WALL);
+        
+        if(n < 0) {
+            if(errno == EAGAIN) continue;
+            LOG("waitpid failed: %s\n", strerror(errno));
+            goto done;
+        }
+
+        XLOG("waitpid: n=%d status=%08x\n", n, status);
+
+        if(WIFSTOPPED(status)){
+            n = WSTOPSIG(status);
+            switch(n) {
+            case SIGSTOP:
+                XLOG("stopped -- continuing\n");
+                n = ptrace(PTRACE_CONT, tid, 0, 0);
+                if(n) {
+                    LOG("ptrace failed: %s\n", strerror(errno));
+                    goto done;
+                }
+                continue;
+                
+            case SIGILL:
+            case SIGABRT:
+            case SIGBUS:
+            case SIGFPE:
+            case SIGSEGV:
+            case SIGSTKFLT: {
+                XLOG("stopped -- fatal signal\n");
+                need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
+                kill(tid, SIGSTOP);
+                goto done;
+            }
+
+            default:
+                XLOG("stopped -- unexpected signal\n");
+                goto done;
+            }
+        } else {
+            XLOG("unexpected waitpid response\n");
+            goto done;
+        }
+    }
+    
+done:
+    XLOG("detaching\n");
+    
+    /* stop the process so we can debug */
+    kill(cr.pid, SIGSTOP);
+
+    /* 
+     * If a thread has been attached by ptrace, make sure it is detached 
+     * successfully otherwise we will get a zombie.
+     */ 
+    if (tid_attach_status == 0) {
+        int detach_status;
+        /* detach so we can attach gdbserver */
+        detach_status = ptrace(PTRACE_DETACH, tid, 0, 0);
+        need_cleanup |= (detach_status != 0);
+    }
+
+    /*
+     * if debug.db.uid is set, its value indicates if we should wait
+     * for user action for the crashing process.
+     * in this case, we log a message and turn the debug LED on
+     * waiting for a gdb connection (for instance)
+     */
+
+    if ((signed)cr.uid <= debug_uid) {
+        wait_for_user_action(tid, &cr);
+    }
+
+    /* resume stopped process (so it can crash in peace) */
+    kill(cr.pid, SIGCONT);
+
+    if (need_cleanup) {
+        LOG("debuggerd committing suicide to free the zombie!\n");
+        kill(getpid(), SIGKILL);
+    }
+
+    if(fd != -1) close(fd);
+}
+
+int main(int argc, char **argv)
+{
+    int s;
+    struct sigaction act;
+    
+    process_name_ptr = argv;
+    
+    logsocket = socket_local_client("logd", 
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
+    if(logsocket < 0) {
+        logsocket = -1;
+    } else {
+        fcntl(logsocket, F_SETFD, FD_CLOEXEC);
+    }
+
+    act.sa_handler = SIG_DFL;
+    sigemptyset(&act.sa_mask);
+    sigaddset(&act.sa_mask,SIGCHLD);
+    act.sa_flags = SA_NOCLDWAIT;
+    sigaction(SIGCHLD, &act, 0);
+    
+    s = socket_local_server("android:debuggerd", 
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    if(s < 0) return -1;
+    fcntl(s, F_SETFD, FD_CLOEXEC);
+
+    LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
+    
+    for(;;) {
+        struct sockaddr addr;
+        socklen_t alen;
+        int fd;
+        
+        alen = sizeof(addr);
+        fd = accept(s, &addr, &alen);
+        if(fd < 0) continue;
+        
+        fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+        handle_crashing_process(fd);
+    }
+    return 0;
+}
diff --git a/debuggerd/getevent.c b/debuggerd/getevent.c
new file mode 100644
index 0000000..ebd070c
--- /dev/null
+++ b/debuggerd/getevent.c
@@ -0,0 +1,219 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+#include <linux/input.h>
+#include <errno.h>
+#include <cutils/log.h>
+
+static struct pollfd *ufds;
+static char **device_names;
+static int nfds;
+
+static int open_device(const char *device)
+{
+    int version;
+    int fd;
+    struct pollfd *new_ufds;
+    char **new_device_names;
+    char name[80];
+    char location[80];
+    char idstr[80];
+    struct input_id id;
+
+    fd = open(device, O_RDWR);
+    if(fd < 0) {
+        return -1;
+    }
+    
+    if(ioctl(fd, EVIOCGVERSION, &version)) {
+        return -1;
+    }
+    if(ioctl(fd, EVIOCGID, &id)) {
+        return -1;
+    }
+    name[sizeof(name) - 1] = '\0';
+    location[sizeof(location) - 1] = '\0';
+    idstr[sizeof(idstr) - 1] = '\0';
+    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+        //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
+        name[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+        //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
+        location[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+        //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
+        idstr[0] = '\0';
+    }
+
+    new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
+    if(new_ufds == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    ufds = new_ufds;
+    new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
+    if(new_device_names == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    device_names = new_device_names;
+    ufds[nfds].fd = fd;
+    ufds[nfds].events = POLLIN;
+    device_names[nfds] = strdup(device);
+    nfds++;
+
+    return 0;
+}
+
+int close_device(const char *device)
+{
+    int i;
+    for(i = 1; i < nfds; i++) {
+        if(strcmp(device_names[i], device) == 0) {
+            int count = nfds - i - 1;
+            free(device_names[i]);
+            memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+            memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+            nfds--;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static int read_notify(const char *dirname, int nfd)
+{
+    int res;
+    char devname[PATH_MAX];
+    char *filename;
+    char event_buf[512];
+    int event_size;
+    int event_pos = 0;
+    struct inotify_event *event;
+
+    res = read(nfd, event_buf, sizeof(event_buf));
+    if(res < (int)sizeof(*event)) {
+        if(errno == EINTR)
+            return 0;
+        fprintf(stderr, "could not get event, %s\n", strerror(errno));
+        return 1;
+    }
+    //printf("got %d bytes of event information\n", res);
+
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+
+    while(res >= (int)sizeof(*event)) {
+        event = (struct inotify_event *)(event_buf + event_pos);
+        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+        if(event->len) {
+            strcpy(filename, event->name);
+            if(event->mask & IN_CREATE) {
+                open_device(devname);
+            }
+            else {
+                close_device(devname);
+            }
+        }
+        event_size = sizeof(*event) + event->len;
+        res -= event_size;
+        event_pos += event_size;
+    }
+    return 0;
+}
+
+static int scan_dir(const char *dirname)
+{
+    char devname[PATH_MAX];
+    char *filename;
+    DIR *dir;
+    struct dirent *de;
+    dir = opendir(dirname);
+    if(dir == NULL)
+        return -1;
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+    while((de = readdir(dir))) {
+        if(de->d_name[0] == '.' &&
+           (de->d_name[1] == '\0' ||
+            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+            continue;
+        strcpy(filename, de->d_name);
+        open_device(devname);
+    }
+    closedir(dir);
+    return 0;
+}
+
+int init_getevent()
+{
+    int res;
+    const char *device_path = "/dev/input";
+
+    nfds = 1;
+    ufds = calloc(1, sizeof(ufds[0]));
+    ufds[0].fd = inotify_init();
+    ufds[0].events = POLLIN;
+
+	res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+    if(res < 0) {
+        return 1;
+    }
+    res = scan_dir(device_path);
+    if(res < 0) {
+        return 1;
+    }
+    return 0;
+}
+
+void uninit_getevent()
+{
+    int i;
+    for(i = 0; i < nfds; i++) {
+        close(ufds[i].fd);
+    }
+    free(ufds);
+    ufds = 0;
+    nfds = 0;
+}
+
+int get_event(struct input_event* event, int timeout)
+{
+    int res;
+    int i;
+    int pollres;
+    const char *device_path = "/dev/input";
+    while(1) {
+        pollres = poll(ufds, nfds, timeout);
+        if (pollres == 0) {
+            return 1;
+        }
+        if(ufds[0].revents & POLLIN) {
+            read_notify(device_path, ufds[0].fd);
+        }
+        for(i = 1; i < nfds; i++) {
+            if(ufds[i].revents) {
+                if(ufds[i].revents & POLLIN) {
+                    res = read(ufds[i].fd, event, sizeof(*event));
+                    if(res < (int)sizeof(event)) {
+                        fprintf(stderr, "could not get event\n");
+                        return -1;
+                    }
+                    return 0;
+                }
+            }
+        }
+    }
+    return 0;
+}
diff --git a/debuggerd/pr-support.c b/debuggerd/pr-support.c
new file mode 100644
index 0000000..358d9b7
--- /dev/null
+++ b/debuggerd/pr-support.c
@@ -0,0 +1,345 @@
+/* ARM EABI compliant unwinding routines
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   Contributed by Paul Brook
+ 
+   This file is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   In addition to the permissions in the GNU General Public License, the
+   Free Software Foundation gives you unlimited permission to link the
+   compiled version of this file into combinations with other programs,
+   and to distribute those combinations without any restriction coming
+   from the use of this file.  (The General Public License restrictions
+   do apply in other respects; for example, they cover modification of
+   the file, and distribution when not linked into a combine
+   executable.)
+
+   This file 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING.  If not, write to
+   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/****************************************************************************
+ * The functions here are derived from gcc/config/arm/pr-support.c from the 
+ * 4.3.x release. The main changes here involve the use of ptrace to retrieve
+ * memory/processor states from a remote process.
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <unwind.h>
+
+#include "utility.h"
+
+/* We add a prototype for abort here to avoid creating a dependency on
+   target headers.  */
+extern void abort (void);
+
+/* Derived from _Unwind_VRS_Pop to use ptrace */
+extern _Unwind_VRS_Result 
+unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, 
+                            _Unwind_VRS_RegClass regclass, 
+                            _uw discriminator, 
+                            _Unwind_VRS_DataRepresentation representation, 
+                            pid_t pid);
+
+typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
+
+/* Misc constants.  */
+#define R_IP    12
+#define R_SP    13
+#define R_LR    14
+#define R_PC    15
+
+#define uint32_highbit (((_uw) 1) << 31)
+
+void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
+
+/* Unwind descriptors.  */
+
+typedef struct
+{
+  _uw16 length;
+  _uw16 offset;
+} EHT16;
+
+typedef struct
+{
+  _uw length;
+  _uw offset;
+} EHT32;
+
+/* Personality routine helper functions.  */
+
+#define CODE_FINISH (0xb0)
+
+/* Derived from next_unwind_byte to use ptrace */
+/* Return the next byte of unwinding information, or CODE_FINISH if there is
+   no data remaining.  */
+static inline _uw8
+next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid)
+{
+  _uw8 b;
+
+  if (uws->bytes_left == 0)
+    {
+      /* Load another word */
+      if (uws->words_left == 0)
+	return CODE_FINISH; /* Nothing left.  */
+      uws->words_left--;
+      uws->data = get_remote_word(pid, uws->next);
+      uws->next++;
+      uws->bytes_left = 3;
+    }
+  else
+    uws->bytes_left--;
+
+  /* Extract the most significant byte.  */
+  b = (uws->data >> 24) & 0xff;
+  uws->data <<= 8;
+  return b;
+}
+
+/* Execute the unwinding instructions described by UWS.  */
+_Unwind_Reason_Code
+unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
+                           pid_t pid)
+{
+  _uw op;
+  int set_pc;
+  _uw reg;
+
+  set_pc = 0;
+  for (;;)
+    {
+      op = next_unwind_byte_with_ptrace (uws, pid);
+      if (op == CODE_FINISH)
+	{
+	  /* If we haven't already set pc then copy it from lr.  */
+	  if (!set_pc)
+	    {
+	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
+			       &reg);
+	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
+			       &reg);
+	      set_pc = 1;
+	    }
+	  /* Drop out of the loop.  */
+	  break;
+	}
+      if ((op & 0x80) == 0)
+	{
+	  /* vsp = vsp +- (imm6 << 2 + 4).  */
+	  _uw offset;
+
+	  offset = ((op & 0x3f) << 2) + 4;
+	  _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
+	  if (op & 0x40)
+	    reg -= offset;
+	  else
+	    reg += offset;
+	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
+	  continue;
+	}
+      
+      if ((op & 0xf0) == 0x80)
+	{
+	  op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid);
+	  if (op == 0x8000)
+	    {
+	      /* Refuse to unwind.  */
+	      return _URC_FAILURE;
+	    }
+	  /* Pop r4-r15 under mask.  */
+	  op = (op << 4) & 0xfff0;
+	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32, 
+                                      pid)
+	      != _UVRSR_OK)
+	    return _URC_FAILURE;
+	  if (op & (1 << R_PC))
+	    set_pc = 1;
+	  continue;
+	}
+      if ((op & 0xf0) == 0x90)
+	{
+	  op &= 0xf;
+	  if (op == 13 || op == 15)
+	    /* Reserved.  */
+	    return _URC_FAILURE;
+	  /* vsp = r[nnnn].  */
+	  _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, &reg);
+	  _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, &reg);
+	  continue;
+	}
+      if ((op & 0xf0) == 0xa0)
+	{
+	  /* Pop r4-r[4+nnn], [lr].  */
+	  _uw mask;
+	  
+	  mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
+	  if (op & 8)
+	    mask |= (1 << R_LR);
+	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32,
+                                      pid)
+	      != _UVRSR_OK)
+	    return _URC_FAILURE;
+	  continue;
+	}
+      if ((op & 0xf0) == 0xb0)
+	{
+	  /* op == 0xb0 already handled.  */
+	  if (op == 0xb1)
+	    {
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      if (op == 0 || ((op & 0xf0) != 0))
+		/* Spare.  */
+		return _URC_FAILURE;
+	      /* Pop r0-r4 under mask.  */
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, 
+                                          _UVRSD_UINT32, pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  if (op == 0xb2)
+	    {
+	      /* vsp = vsp + 0x204 + (uleb128 << 2).  */
+	      int shift;
+
+	      _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
+			       &reg);
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      shift = 2;
+	      while (op & 0x80)
+		{
+		  reg += ((op & 0x7f) << shift);
+		  shift += 7;
+		  op = next_unwind_byte_with_ptrace (uws, pid);
+		}
+	      reg += ((op & 0x7f) << shift) + 0x204;
+	      _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
+			       &reg);
+	      continue;
+	    }
+	  if (op == 0xb3)
+	    {
+	      /* Pop VFP registers with fldmx.  */
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, 
+                                          pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  if ((op & 0xfc) == 0xb4)
+	    {
+	      /* Pop FPA E[4]-E[4+nn].  */
+	      op = 0x40000 | ((op & 3) + 1);
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, 
+                                          pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  /* op & 0xf8 == 0xb8.  */
+	  /* Pop VFP D[8]-D[8+nnn] with fldmx.  */
+	  op = 0x80000 | ((op & 7) + 1);
+	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid)
+	      != _UVRSR_OK)
+	    return _URC_FAILURE;
+	  continue;
+	}
+      if ((op & 0xf0) == 0xc0)
+	{
+	  if (op == 0xc6)
+	    {
+	      /* Pop iWMMXt D registers.  */
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, 
+                                          _UVRSD_UINT64, pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  if (op == 0xc7)
+	    {
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      if (op == 0 || (op & 0xf0) != 0)
+		/* Spare.  */
+		return _URC_FAILURE;
+	      /* Pop iWMMXt wCGR{3,2,1,0} under mask.  */
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op, 
+                                          _UVRSD_UINT32, pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  if ((op & 0xf8) == 0xc0)
+	    {
+	      /* Pop iWMMXt wR[10]-wR[10+nnn].  */
+	      op = 0xa0000 | ((op & 0xf) + 1);
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, 
+                                          _UVRSD_UINT64, pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  if (op == 0xc8)
+	    {
+#ifndef __VFP_FP__
+ 	      /* Pop FPA registers.  */
+ 	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
+ 	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
+                                          pid)
+ 		  != _UVRSR_OK)
+ 		return _URC_FAILURE;
+ 	      continue;
+#else
+              /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm.  */
+              op = next_unwind_byte_with_ptrace (uws, pid);
+              op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1);
+              if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, 
+                                              _UVRSD_DOUBLE, pid)
+                  != _UVRSR_OK)
+                return _URC_FAILURE;
+              continue;
+#endif
+	    }
+	  if (op == 0xc9)
+	    {
+	      /* Pop VFP registers with fldmd.  */
+	      op = next_unwind_byte_with_ptrace (uws, pid);
+	      op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
+	      if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, 
+                                          _UVRSD_DOUBLE, pid)
+		  != _UVRSR_OK)
+		return _URC_FAILURE;
+	      continue;
+	    }
+	  /* Spare.  */
+	  return _URC_FAILURE;
+	}
+      if ((op & 0xf8) == 0xd0)
+	{
+	  /* Pop VFP D[8]-D[8+nnn] with fldmd.  */
+	  op = 0x80000 | ((op & 7) + 1);
+	  if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE, 
+                                      pid)
+	      != _UVRSR_OK)
+	    return _URC_FAILURE;
+	  continue;
+	}
+      /* Spare.  */
+      return _URC_FAILURE;
+    }
+  return _URC_OK;
+}
diff --git a/debuggerd/unwind-arm.c b/debuggerd/unwind-arm.c
new file mode 100644
index 0000000..9642d2e
--- /dev/null
+++ b/debuggerd/unwind-arm.c
@@ -0,0 +1,654 @@
+/* ARM EABI compliant unwinding routines.
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   Contributed by Paul Brook
+
+   This file is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   In addition to the permissions in the GNU General Public License, the
+   Free Software Foundation gives you unlimited permission to link the
+   compiled version of this file into combinations with other programs,
+   and to distribute those combinations without any restriction coming
+   from the use of this file.  (The General Public License restrictions
+   do apply in other respects; for example, they cover modification of
+   the file, and distribution when not linked into a combine
+   executable.)
+
+   This file 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING.  If not, write to
+   the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/****************************************************************************
+ * The functions here are derived from gcc/config/arm/unwind-arm.c from the 
+ * 4.3.x release. The main changes here involve the use of ptrace to retrieve
+ * memory/processor states from a remote process.
+ ****************************************************************************/
+
+#include <cutils/logd.h>
+#include <sys/ptrace.h>
+#include <unwind.h>
+#include "utility.h"
+
+typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
+
+void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
+bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp);
+bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp,
+                        const type_info *rttip,
+                        bool is_reference,
+                        void **matched_object);
+
+/* Misc constants.  */
+#define R_IP	12
+#define R_SP	13
+#define R_LR	14
+#define R_PC	15
+
+#define EXIDX_CANTUNWIND 1
+#define uint32_highbit (((_uw) 1) << 31)
+
+#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1)
+#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
+#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
+#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
+
+struct core_regs
+{
+  _uw r[16];
+};
+
+/* We use normal integer types here to avoid the compiler generating
+   coprocessor instructions.  */
+struct vfp_regs
+{
+  _uw64 d[16];
+  _uw pad;
+};
+
+struct vfpv3_regs
+{
+  /* Always populated via VSTM, so no need for the "pad" field from
+     vfp_regs (which is used to store the format word for FSTMX).  */
+  _uw64 d[16];
+};
+
+struct fpa_reg
+{
+  _uw w[3];
+};
+
+struct fpa_regs
+{
+  struct fpa_reg f[8];
+};
+
+struct wmmxd_regs
+{
+  _uw64 wd[16];
+};
+
+struct wmmxc_regs
+{
+  _uw wc[4];
+};
+
+/* Unwind descriptors.  */
+
+typedef struct
+{
+  _uw16 length;
+  _uw16 offset;
+} EHT16;
+
+typedef struct
+{
+  _uw length;
+  _uw offset;
+} EHT32;
+
+/* The ABI specifies that the unwind routines may only use core registers,
+   except when actually manipulating coprocessor state.  This allows
+   us to write one implementation that works on all platforms by
+   demand-saving coprocessor registers.
+
+   During unwinding we hold the coprocessor state in the actual hardware
+   registers and allocate demand-save areas for use during phase1
+   unwinding.  */
+
+typedef struct
+{
+  /* The first fields must be the same as a phase2_vrs.  */
+  _uw demand_save_flags;
+  struct core_regs core;
+  _uw prev_sp; /* Only valid during forced unwinding.  */
+  struct vfp_regs vfp;
+  struct vfpv3_regs vfp_regs_16_to_31;
+  struct fpa_regs fpa;
+  struct wmmxd_regs wmmxd;
+  struct wmmxc_regs wmmxc;
+} phase1_vrs;
+
+/* This must match the structure created by the assembly wrappers.  */
+typedef struct
+{
+  _uw demand_save_flags;
+  struct core_regs core;
+} phase2_vrs;
+
+
+/* An exception index table entry.  */
+
+typedef struct __EIT_entry
+{
+  _uw fnoffset;
+  _uw content;
+} __EIT_entry;
+
+/* Derived version to use ptrace */
+typedef _Unwind_Reason_Code (*personality_routine_with_ptrace)
+           (_Unwind_State,
+			_Unwind_Control_Block *,
+			_Unwind_Context *,
+            pid_t);
+
+/* Derived version to use ptrace */
+/* ABI defined personality routines.  */
+static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State,
+    _Unwind_Control_Block *, _Unwind_Context *, pid_t);
+static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State,
+    _Unwind_Control_Block *, _Unwind_Context *, pid_t);
+static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State,
+    _Unwind_Control_Block *, _Unwind_Context *, pid_t);
+
+/* Execute the unwinding instructions described by UWS.  */
+extern _Unwind_Reason_Code
+unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
+                           pid_t pid);
+
+/* Derived version to use ptrace. Only handles core registers. Disregards
+ * FP and others. 
+ */
+/* ABI defined function to pop registers off the stack.  */
+
+_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
+				    _Unwind_VRS_RegClass regclass,
+				    _uw discriminator,
+				    _Unwind_VRS_DataRepresentation representation,
+                    pid_t pid)
+{
+  phase1_vrs *vrs = (phase1_vrs *) context;
+
+  switch (regclass)
+    {
+    case _UVRSC_CORE:
+      {
+	_uw *ptr;
+	_uw mask;
+	int i;
+
+	if (representation != _UVRSD_UINT32)
+	  return _UVRSR_FAILED;
+
+	mask = discriminator & 0xffff;
+	ptr = (_uw *) vrs->core.r[R_SP];
+	/* Pop the requested registers.  */
+	for (i = 0; i < 16; i++)
+	  {
+	    if (mask & (1 << i)) {
+	      vrs->core.r[i] = get_remote_word(pid, ptr);
+          ptr++;
+        }
+	  }
+	/* Writeback the stack pointer value if it wasn't restored.  */
+	if ((mask & (1 << R_SP)) == 0)
+	  vrs->core.r[R_SP] = (_uw) ptr;
+      }
+      return _UVRSR_OK;
+
+    default:
+      return _UVRSR_FAILED;
+    }
+}
+
+/* Core unwinding functions.  */
+
+/* Calculate the address encoded by a 31-bit self-relative offset at address
+   P.  */
+static inline _uw
+selfrel_offset31 (const _uw *p, pid_t pid)
+{
+  _uw offset = get_remote_word(pid, (void*)p);
+
+  //offset = *p;
+  /* Sign extend to 32 bits.  */
+  if (offset & (1 << 30))
+    offset |= 1u << 31;
+  else
+    offset &= ~(1u << 31);
+
+  return offset + (_uw) p;
+}
+
+
+/* Perform a binary search for RETURN_ADDRESS in TABLE.  The table contains
+   NREC entries.  */
+
+static const __EIT_entry *
+search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address,
+                  pid_t pid)
+{
+  _uw next_fn;
+  _uw this_fn;
+  int n, left, right;
+
+  if (nrec == 0)
+    return (__EIT_entry *) 0;
+
+  left = 0;
+  right = nrec - 1;
+
+  while (1)
+    {
+      n = (left + right) / 2;
+      this_fn = selfrel_offset31 (&table[n].fnoffset, pid);
+      if (n != nrec - 1)
+	next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1;
+      else
+	next_fn = (_uw)0 - 1;
+
+      if (return_address < this_fn)
+	{
+	  if (n == left)
+	    return (__EIT_entry *) 0;
+	  right = n - 1;
+	}
+      else if (return_address <= next_fn)
+	return &table[n];
+      else
+	left = n + 1;
+    }
+}
+
+/* Find the exception index table eintry for the given address. */
+static const __EIT_entry*
+get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map)
+{
+  const __EIT_entry *eitp = NULL;
+  int nrec;
+  mapinfo *mi;
+  
+  /* The return address is the address of the instruction following the
+     call instruction (plus one in thumb mode).  If this was the last
+     instruction in the function the address will lie in the following
+     function.  Subtract 2 from the address so that it points within the call
+     instruction itself.  */
+  if (return_address >= 2)
+      return_address -= 2;
+
+  for (mi = map; mi != NULL; mi = mi->next) {
+    if (return_address >= mi->start && return_address <= mi->end) break;
+  }
+
+  if (mi) {
+    if (containing_map) *containing_map = mi;
+    eitp = (__EIT_entry *) mi->exidx_start;
+    nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry);
+    eitp = search_EIT_table (eitp, nrec, return_address, pid);
+  }
+  return eitp;
+}
+
+/* Find the exception index table eintry for the given address.
+   Fill in the relevant fields of the UCB.
+   Returns _URC_FAILURE if an error occurred, _URC_OK on success.  */
+
+static _Unwind_Reason_Code
+get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, 
+               mapinfo *map, mapinfo **containing_map)
+{
+  const __EIT_entry *eitp;
+  
+  eitp = get_eitp(return_address, pid, map, containing_map);
+
+  if (!eitp)
+    {
+      UCB_PR_ADDR (ucbp) = 0;
+      return _URC_FAILURE;
+    }
+  ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid);
+
+  _uw eitp_content = get_remote_word(pid, (void *)&eitp->content);
+
+  /* Can this frame be unwound at all?  */
+  if (eitp_content == EXIDX_CANTUNWIND)
+    {
+      UCB_PR_ADDR (ucbp) = 0;
+      return _URC_END_OF_STACK;
+    }
+
+  /* Obtain the address of the "real" __EHT_Header word.  */
+
+  if (eitp_content & uint32_highbit)
+    {
+      /* It is immediate data.  */
+      ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content;
+      ucbp->pr_cache.additional = 1;
+    }
+  else
+    {
+      /* The low 31 bits of the content field are a self-relative
+	 offset to an _Unwind_EHT_Entry structure.  */
+      ucbp->pr_cache.ehtp =
+	(_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid);
+      ucbp->pr_cache.additional = 0;
+    }
+
+  /* Discover the personality routine address.  */
+  if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31))
+    {
+      /* One of the predefined standard routines.  */
+      _uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf;
+      if (idx == 0)
+	UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace;
+      else if (idx == 1)
+	UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace;
+      else if (idx == 2)
+	UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace;
+      else
+	{ /* Failed */
+	  UCB_PR_ADDR (ucbp) = 0;
+	  return _URC_FAILURE;
+	}
+    } 
+  else
+    {
+      /* Execute region offset to PR */
+      UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid);
+      /* Since we are unwinding the stack from a different process, it is
+       * impossible to execute the personality routine in debuggerd. Punt here.
+       */
+	  return _URC_FAILURE;
+    }
+  return _URC_OK;
+}
+
+/* Print out the current call level, pc, and module name in the crash log */
+static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, 
+                                        int tfd,
+                                        int stack_level,
+                                        mapinfo *map,
+                                        unsigned int sp_list[],
+                                        bool at_fault)
+{
+    _uw pc;
+    _uw rel_pc; 
+    phase2_vrs *vrs = (phase2_vrs*) context;
+    const mapinfo *mi;
+    bool only_in_tombstone = !at_fault;
+
+    if (stack_level < STACK_CONTENT_DEPTH) {
+        sp_list[stack_level] = vrs->core.r[R_SP];
+    }
+    pc = vrs->core.r[R_PC];
+
+    // Top level frame
+    if (stack_level == 0) {
+        pc &= ~1;
+    }
+    // For deeper framers, rollback pc by one instruction
+    else {
+        pc = vrs->core.r[R_PC];
+        /* Thumb mode - need to check whether the bl(x) has long offset or not.
+         * Examples:
+         *
+         * arm blx in the middle of thumb:
+         * 187ae:       2300            movs    r3, #0
+         * 187b0:       f7fe ee1c       blx     173ec
+         * 187b4:       2c00            cmp     r4, #0
+         *
+         * arm bl in the middle of thumb:
+         * 187d8:       1c20            adds    r0, r4, #0
+         * 187da:       f136 fd15       bl      14f208
+         * 187de:       2800            cmp     r0, #0
+         *
+         * pure thumb:
+         * 18894:       189b            adds    r3, r3, r2
+         * 18896:       4798            blx     r3
+         * 18898:       b001            add     sp, #4
+         */
+        if (pc & 1) {
+            _uw prev_word;
+            pc = (pc & ~1);
+            prev_word = get_remote_word(pid, (void *) pc-4);
+            // Long offset 
+            if ((prev_word & 0xf0000000) == 0xf0000000 && 
+                (prev_word & 0x0000e000) == 0x0000e000) {
+                pc -= 4;
+            }
+            else {
+                pc -= 2;
+            }
+        }
+        else { 
+            pc -= 4;
+        }
+    }
+
+    /* We used to print the absolute PC in the back trace, and mask out the top
+     * 3 bits to guesstimate the offset in the .so file. This is not working for
+     * non-prelinked libraries since the starting offset may not be aligned on 
+     * 1MB boundaries, and the library may be larger than 1MB. So for .so 
+     * addresses we print the relative offset in back trace.
+     */
+    rel_pc = pc;
+    mi = pc_to_mapinfo(map, pc, &rel_pc);
+
+    _LOG(tfd, only_in_tombstone, 
+         "         #%02d  pc %08x  %s\n", stack_level, rel_pc, 
+         mi ? mi->name : "");
+
+    return _URC_NO_REASON;
+}
+
+/* Derived from __gnu_Unwind_Backtrace to use ptrace */
+/* Perform stack backtrace through unwind data. Return the level of stack it
+ * unwinds.
+ */
+int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, 
+                                 unsigned int sp_list[], int *frame0_pc_sane,
+                                 bool at_fault)
+{
+    phase1_vrs saved_vrs;
+    _Unwind_Reason_Code code = _URC_OK;
+    struct pt_regs r;
+    int i;
+    int stack_level = 0;
+
+    _Unwind_Control_Block ucb;
+    _Unwind_Control_Block *ucbp = &ucb;
+
+    if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0;
+
+    for (i = 0; i < 16; i++) {
+        saved_vrs.core.r[i] = r.uregs[i];
+        /*
+        _LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]);
+        */
+    }
+
+    /* Set demand-save flags.  */
+    saved_vrs.demand_save_flags = ~(_uw) 0;
+
+    /* 
+     * If the app crashes because of calling the weeds, we cannot pass the PC 
+     * to the usual unwinding code as the EXIDX mapping will fail. 
+     * Instead, we simply print out the 0 as the top frame, and resume the 
+     * unwinding process with the value stored in LR.
+     */
+    if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { 
+        *frame0_pc_sane = 0;
+        log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, 
+                      map, sp_list, at_fault);
+        saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR];
+        stack_level++;
+    }
+
+    do {
+        mapinfo *this_map = NULL;
+        /* Find the entry for this routine.  */
+        if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map)
+            != _URC_OK) {
+            /* Uncomment the code below to study why the unwinder failed */
+#if 0
+            /* Shed more debugging info for stack unwinder improvement */
+            if (this_map) {
+                _LOG(tfd, 1, 
+                     "Relative PC=%#x from %s not contained in EXIDX\n", 
+                     saved_vrs.core.r[R_PC] - this_map->start, this_map->name);
+            }
+            _LOG(tfd, 1, "PC=%#x SP=%#x\n", 
+                 saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]);
+#endif
+            code = _URC_FAILURE;
+            break;
+        }
+
+        /* The dwarf unwinder assumes the context structure holds things
+        like the function and LSDA pointers.  The ARM implementation
+        caches these in the exception header (UCB).  To avoid
+        rewriting everything we make the virtual IP register point at
+        the UCB.  */
+        _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp);
+
+        /* Call log function.  */
+        if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level,
+                          map, sp_list, at_fault) != _URC_NO_REASON) {
+            code = _URC_FAILURE;
+            break;
+        }
+        stack_level++;
+
+        /* Call the pr to decide what to do.  */
+        code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))(
+                _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp, 
+                (void *) &saved_vrs, pid);
+    /* 
+     * In theory the unwinding process will stop when the end of stack is
+     * reached or there is no unwinding information for the code address.
+     * To add another level of guarantee that the unwinding process
+     * will terminate we will stop it when the STACK_CONTENT_DEPTH is reached.
+     */
+    } while (code != _URC_END_OF_STACK && code != _URC_FAILURE && 
+             stack_level < STACK_CONTENT_DEPTH);
+    return stack_level;
+}
+
+
+/* Derived version to use ptrace */
+/* Common implementation for ARM ABI defined personality routines.
+   ID is the index of the personality routine, other arguments are as defined
+   by __aeabi_unwind_cpp_pr{0,1,2}.  */
+
+static _Unwind_Reason_Code
+unwind_pr_common_with_ptrace (_Unwind_State state,
+			_Unwind_Control_Block *ucbp,
+			_Unwind_Context *context,
+			int id,
+            pid_t pid)
+{
+  __gnu_unwind_state uws;
+  _uw *data;
+  int phase2_call_unexpected_after_unwind = 0;
+
+  state &= _US_ACTION_MASK;
+
+  data = (_uw *) ucbp->pr_cache.ehtp;
+  uws.data = get_remote_word(pid, data);
+  data++;
+  uws.next = data;
+  if (id == 0)
+    {
+      uws.data <<= 8;
+      uws.words_left = 0;
+      uws.bytes_left = 3;
+    }
+  else
+    {
+      uws.words_left = (uws.data >> 16) & 0xff;
+      uws.data <<= 16;
+      uws.bytes_left = 2;
+      data += uws.words_left;
+    }
+
+  /* Restore the saved pointer.  */
+  if (state == _US_UNWIND_FRAME_RESUME)
+    data = (_uw *) ucbp->cleanup_cache.bitpattern[0];
+
+  if ((ucbp->pr_cache.additional & 1) == 0)
+    {
+      /* Process descriptors.  */
+      while (get_remote_word(pid, data)) {
+      /**********************************************************************
+       * The original code here seems to deal with exceptions that are not
+       * applicable in our toolchain, thus there is no way to test it for now.
+       * Instead of leaving it here and causing potential instability in
+       * debuggerd, we'd better punt here and leave the stack unwound.
+       * In the future when we discover cases where the stack should be unwound
+       * further but is not, we can revisit the code here.
+       **********************************************************************/
+        return _URC_FAILURE;
+	  }
+	  /* Finished processing this descriptor.  */
+    }
+
+  if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK)
+    return _URC_FAILURE;
+
+  if (phase2_call_unexpected_after_unwind)
+    {
+      /* Enter __cxa_unexpected as if called from the call site.  */
+      _Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC));
+      _Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected);
+      return _URC_INSTALL_CONTEXT;
+    }
+
+  return _URC_CONTINUE_UNWIND;
+}
+
+
+/* ABI defined personality routine entry points.  */
+
+static _Unwind_Reason_Code
+unwind_cpp_pr0_with_ptrace (_Unwind_State state,
+			_Unwind_Control_Block *ucbp,
+			_Unwind_Context *context,
+            pid_t pid)
+{
+  return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid);
+}
+
+static _Unwind_Reason_Code
+unwind_cpp_pr1_with_ptrace (_Unwind_State state,
+			_Unwind_Control_Block *ucbp,
+			_Unwind_Context *context,
+            pid_t pid)
+{
+  return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid);
+}
+
+static _Unwind_Reason_Code
+unwind_cpp_pr2_with_ptrace (_Unwind_State state,
+			_Unwind_Control_Block *ucbp,
+			_Unwind_Context *context,
+            pid_t pid)
+{
+  return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid);
+}
diff --git a/debuggerd/utility.c b/debuggerd/utility.c
new file mode 100644
index 0000000..8f3931c
--- /dev/null
+++ b/debuggerd/utility.c
@@ -0,0 +1,83 @@
+/* system/debuggerd/utility.c
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <sys/ptrace.h>
+#include <sys/exec_elf.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "utility.h"
+
+/* Get a word from pid using ptrace. The result is the return value. */
+int get_remote_word(int pid, void *src)
+{
+    return ptrace(PTRACE_PEEKTEXT, pid, src, NULL);
+}
+
+
+/* Handy routine to read aggregated data from pid using ptrace. The read 
+ * values are written to the dest locations directly. 
+ */
+void get_remote_struct(int pid, void *src, void *dst, size_t size)
+{
+    unsigned int i;
+
+    for (i = 0; i+4 <= size; i+=4) {
+        *(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
+    }
+
+    if (i < size) {
+        int val;
+
+        assert((size - i) < 4);
+        val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
+        while (i < size) {
+            ((unsigned char *)dst)[i] = val & 0xff;
+            i++;
+            val >>= 8;
+        }
+    }
+}
+
+/* Map a pc address to the name of the containing ELF file */
+const char *map_to_name(mapinfo *mi, unsigned pc, const char* def)
+{
+    while(mi) {
+        if((pc >= mi->start) && (pc < mi->end)){
+            return mi->name;
+        }
+        mi = mi->next;
+    }
+    return def;
+}
+
+/* Find the containing map info for the pc */
+const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc)
+{
+    while(mi) {
+        if((pc >= mi->start) && (pc < mi->end)){
+            // Only calculate the relative offset for shared libraries
+            if (strstr(mi->name, ".so")) {
+                *rel_pc = pc - mi->start;
+            }
+            return mi;
+        }
+        mi = mi->next;
+    }
+    return NULL;
+}
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
new file mode 100644
index 0000000..49f5951
--- /dev/null
+++ b/debuggerd/utility.h
@@ -0,0 +1,56 @@
+/* system/debuggerd/utility.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#ifndef __utility_h
+#define __utility_h
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifndef PT_ARM_EXIDX
+#define PT_ARM_EXIDX    0x70000001      /* .ARM.exidx segment */
+#endif
+
+#define STACK_CONTENT_DEPTH 32
+
+typedef struct mapinfo {
+    struct mapinfo *next;
+    unsigned start;
+    unsigned end;
+    unsigned exidx_start;
+    unsigned exidx_end;
+    char name[];
+} mapinfo;
+
+/* Get a word from pid using ptrace. The result is the return value. */
+extern int get_remote_word(int pid, void *src);
+
+/* Handy routine to read aggregated data from pid using ptrace. The read 
+ * values are written to the dest locations directly. 
+ */
+extern void get_remote_struct(int pid, void *src, void *dst, size_t size);
+
+/* Find the containing map for the pc */
+const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc);
+
+/* Map a pc address to the name of the containing ELF file */
+const char *map_to_name(mapinfo *mi, unsigned pc, const char* def);
+
+/* Log information onto the tombstone */
+extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...);
+
+#endif
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
new file mode 100644
index 0000000..7a9d35f
--- /dev/null
+++ b/fastboot/Android.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg
+LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c 
+LOCAL_MODULE := fastboot
+
+ifeq ($(HOST_OS),linux)
+  LOCAL_SRC_FILES += usb_linux.c util_linux.c
+endif
+
+ifeq ($(HOST_OS),darwin)
+  LOCAL_SRC_FILES += usb_osx.c util_osx.c
+  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \
+	-framework Carbon
+endif
+
+ifeq ($(HOST_OS),windows)
+  LOCAL_SRC_FILES += usb_windows.c util_windows.c
+  EXTRA_STATIC_LIBS := AdbWinApi
+  LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api
+  ifeq ($(strip $(USE_CYGWIN)),)
+    LOCAL_LDLIBS += -lws2_32
+    USE_SYSDEPS_WIN32 := 1
+  endif
+endif
+
+LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz
+
+include $(BUILD_HOST_EXECUTABLE)
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := usbtest.c usb_linux.c
+LOCAL_MODULE := usbtest
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
+endif
diff --git a/fastboot/bootimg.c b/fastboot/bootimg.c
new file mode 100644
index 0000000..1d77b3c
--- /dev/null
+++ b/fastboot/bootimg.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <bootimg.h>
+
+void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
+{
+    strcpy((char*) h->cmdline, cmdline);
+}
+
+boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
+                        void *ramdisk, unsigned ramdisk_size,
+                        void *second, unsigned second_size,
+                        unsigned page_size,
+                        unsigned *bootimg_size)
+{
+    unsigned kernel_actual;
+    unsigned ramdisk_actual;
+    unsigned second_actual;
+    unsigned page_mask;
+    boot_img_hdr *hdr;
+    
+    page_mask = page_size - 1;
+    
+    kernel_actual = (kernel_size + page_mask) & (~page_mask);
+    ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
+    second_actual = (second_size + page_mask) & (~page_mask);
+    
+    *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
+    
+    hdr = calloc(*bootimg_size, 1);
+    
+    if(hdr == 0) {
+        return hdr;
+    }
+
+    memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+    
+    hdr->kernel_size = kernel_size;
+    hdr->kernel_addr = 0x10008000;
+    hdr->ramdisk_size = ramdisk_size;
+    hdr->ramdisk_addr = 0x11000000;
+    hdr->second_size = second_size;
+    hdr->second_addr = 0x10F00000;
+    
+    hdr->tags_addr = 0x10000100;
+    hdr->page_size = page_size;
+
+    memcpy(hdr->magic + page_size, 
+           kernel, kernel_size);
+    memcpy(hdr->magic + page_size + kernel_actual, 
+           ramdisk, ramdisk_size);
+    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
+           second, second_size);
+    return hdr;
+}
diff --git a/fastboot/engine.c b/fastboot/engine.c
new file mode 100644
index 0000000..4c7e197
--- /dev/null
+++ b/fastboot/engine.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "fastboot.h"
+
+char *mkmsg(const char *fmt, ...)
+{
+    char buf[256];
+    char *s;
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsprintf(buf, fmt, ap);
+    va_end(ap);
+    
+    s = strdup(buf);
+    if (s == 0) die("out of memory");
+    return s;
+}
+
+#define OP_DOWNLOAD   1
+#define OP_COMMAND    2
+#define OP_QUERY      3
+#define OP_NOTICE     4
+
+typedef struct Action Action;
+
+struct Action 
+{
+    unsigned op;
+    Action *next;
+
+    char cmd[64];    
+    void *data;
+    unsigned size;
+
+    const char *msg;
+    int (*func)(Action *a, int status, char *resp);
+};
+
+static Action *action_list = 0;
+static Action *action_last = 0;
+
+static int cb_default(Action *a, int status, char *resp)
+{
+    if (status) {
+        fprintf(stderr,"FAILED (%s)\n", resp);
+    } else {
+        fprintf(stderr,"OKAY\n");
+    }
+    return status;
+}
+
+static Action *queue_action(unsigned op, const char *fmt, ...)
+{
+    Action *a;
+    va_list ap;
+
+    a = calloc(1, sizeof(Action));
+    if (a == 0) die("out of memory");
+
+    va_start(ap, fmt);
+    vsprintf(a->cmd, fmt, ap);
+    va_end(ap);
+
+    if (action_last) {
+        action_last->next = a;
+    } else {
+        action_list = a;
+    }
+    action_last = a;
+    a->op = op;
+    a->func = cb_default;
+    return a;
+}
+
+void fb_queue_erase(const char *ptn)
+{
+    Action *a;
+    a = queue_action(OP_COMMAND, "erase:%s", ptn);
+    a->msg = mkmsg("erasing '%s'", ptn);
+}
+
+void fb_queue_flash(const char *ptn, void *data, unsigned sz)
+{
+    Action *a;
+
+    a = queue_action(OP_DOWNLOAD, "");
+    a->data = data;
+    a->size = sz;
+    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
+
+    a = queue_action(OP_COMMAND, "flash:%s", ptn);
+    a->msg = mkmsg("writing '%s'", ptn);
+}
+
+static int match(char *str, const char **value, unsigned count)
+{
+    const char *val;
+    unsigned n;
+    int len;
+
+    for (n = 0; n < count; n++) {
+        const char *val = value[n];
+        int len = strlen(val);
+        int match;
+
+        if ((len > 1) && (val[len-1] == '*')) {
+            len--;
+            match = !strncmp(val, str, len);
+        } else {
+            match = !strcmp(val, str);
+        }
+
+        if (match) return 1;
+    }
+
+    return 0;
+}
+
+
+
+static int cb_check(Action *a, int status, char *resp, int invert)
+{
+    const char **value = a->data;
+    unsigned count = a->size;
+    unsigned n;
+    int yes;
+
+    if (status) {
+        fprintf(stderr,"FAILED (%s)\n", resp);
+        return status;
+    }
+
+    yes = match(resp, value, count);
+    if (invert) yes = !yes;
+
+    if (yes) {
+        fprintf(stderr,"OKAY\n");
+        return 0;
+    }
+
+    fprintf(stderr,"FAILED\n\n");
+    fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
+    fprintf(stderr,"Update %s '%s'",
+            invert ? "rejects" : "requires", value[0]);
+    for (n = 1; n < count; n++) {
+        fprintf(stderr," or '%s'", value[n]);
+    }
+    fprintf(stderr,".\n\n");
+    return -1;
+}
+
+static int cb_require(Action *a, int status, char *resp)
+{
+    return cb_check(a, status, resp, 0);
+}
+
+static int cb_reject(Action *a, int status, char *resp)
+{
+    return cb_check(a, status, resp, 1);
+}
+
+void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value)
+{
+    Action *a;
+    a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = value;
+    a->size = nvalues;
+    a->msg = mkmsg("checking %s", var);
+    a->func = invert ? cb_reject : cb_require;
+    if (a->data == 0) die("out of memory");
+}
+
+static int cb_display(Action *a, int status, char *resp)
+{
+    if (status) {
+        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+        return status;
+    }
+    fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
+    return 0;
+}
+
+void fb_queue_display(const char *var, const char *prettyname)
+{
+    Action *a;
+    a = queue_action(OP_QUERY, "getvar:%s", var);
+    a->data = strdup(prettyname);
+    if (a->data == 0) die("out of memory");
+    a->func = cb_display;
+}
+
+static int cb_do_nothing(Action *a, int status, char *resp)
+{
+    fprintf(stderr,"\n");
+    return 0;
+}
+
+void fb_queue_reboot(void)
+{
+    Action *a = queue_action(OP_COMMAND, "reboot");
+    a->func = cb_do_nothing;
+    a->msg = "rebooting";
+}
+
+void fb_queue_command(const char *cmd, const char *msg)
+{
+    Action *a = queue_action(OP_COMMAND, cmd);
+    a->msg = msg;
+}
+
+void fb_queue_download(const char *name, void *data, unsigned size)
+{
+    Action *a = queue_action(OP_DOWNLOAD, "");
+    a->data = data;
+    a->size = size;
+    a->msg = mkmsg("downloading '%s'", name);
+}
+
+void fb_queue_notice(const char *notice)
+{
+    Action *a = queue_action(OP_NOTICE, "");
+    a->data = (void*) notice;
+}
+
+void fb_execute_queue(usb_handle *usb)
+{
+    Action *a;
+    char resp[FB_RESPONSE_SZ+1];
+    int status;
+
+    a = action_list;
+    resp[FB_RESPONSE_SZ] = 0;
+
+    for (a = action_list; a; a = a->next) {
+        if (a->msg) {
+            fprintf(stderr,"%s... ",a->msg);
+        }
+        if (a->op == OP_DOWNLOAD) {
+            status = fb_download_data(usb, a->data, a->size);
+            status = a->func(a, status, status ? fb_get_error() : "");
+            if (status) break;
+        } else if (a->op == OP_COMMAND) {
+            status = fb_command(usb, a->cmd);
+            status = a->func(a, status, status ? fb_get_error() : "");
+            if (status) break;
+        } else if (a->op == OP_QUERY) {
+            status = fb_command_response(usb, a->cmd, resp);
+            status = a->func(a, status, status ? fb_get_error() : resp);
+            if (status) break;
+        } else if (a->op == OP_NOTICE) {
+            fprintf(stderr,"%s\n",(char*)a->data);
+        } else {
+            die("bogus action");
+        }
+    }
+}
+
diff --git a/fastboot/engineering_key.p12 b/fastboot/engineering_key.p12
new file mode 100644
index 0000000..d8183b0
--- /dev/null
+++ b/fastboot/engineering_key.p12
Binary files differ
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
new file mode 100644
index 0000000..e220dbe
--- /dev/null
+++ b/fastboot/fastboot.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include <sys/time.h>
+#include <bootimg.h>
+#include <zipfile/zipfile.h>
+
+#include "fastboot.h"
+
+static usb_handle *usb = 0;
+static const char *serial = 0;
+static const char *product = 0;
+static const char *cmdline = 0;
+static int wipe_data = 0;
+static unsigned short vendor_id = 0;
+
+void die(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr,"error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr,"\n");
+    va_end(ap);
+    exit(1);
+}    
+
+void get_my_path(char *path);
+
+char *find_item(const char *item, const char *product)
+{
+    char *dir;
+    char *fn;
+    char path[PATH_MAX + 128];
+
+    if(!strcmp(item,"boot")) {
+        fn = "boot.img";
+    } else if(!strcmp(item,"recovery")) {
+        fn = "recovery.img";
+    } else if(!strcmp(item,"system")) {
+        fn = "system.img";
+    } else if(!strcmp(item,"userdata")) {
+        fn = "userdata.img";
+    } else if(!strcmp(item,"info")) {
+        fn = "android-info.txt";
+    } else {
+        fprintf(stderr,"unknown partition '%s'\n", item);
+        return 0;
+    }
+
+    if(product) {
+        get_my_path(path);
+        sprintf(path + strlen(path),
+                "../../../target/product/%s/%s", product, fn);
+        return strdup(path);
+    }
+        
+    dir = getenv("ANDROID_PRODUCT_OUT");
+    if((dir == 0) || (dir[0] == 0)) {
+        die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
+        return 0;
+    }
+    
+    sprintf(path, "%s/%s", dir, fn);
+    return strdup(path);
+}
+
+#ifdef _WIN32
+void *load_file(const char *fn, unsigned *_sz);
+#else
+void *load_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
+#endif
+
+int match_fastboot(usb_ifc_info *info)
+{
+    if(!(vendor_id && (info->dev_vendor == vendor_id)) &&
+       (info->dev_vendor != 0x18d1) &&
+       (info->dev_vendor != 0x0bb4)) return -1;
+    if(info->ifc_class != 0xff) return -1;
+    if(info->ifc_subclass != 0x42) return -1;
+    if(info->ifc_protocol != 0x03) return -1;
+    // require matching serial number if a serial number is specified
+    // at the command line with the -s option.
+    if (serial && strcmp(serial, info->serial_number) != 0) return -1;
+    return 0;
+}
+
+int list_devices_callback(usb_ifc_info *info)
+{
+    if (match_fastboot(info) == 0) {
+        char* serial = info->serial_number;
+        if (!serial[0]) {
+            serial = "????????????";
+        }
+        // output compatible with "adb devices"
+        printf("%s\tfastboot\n", serial);
+    }
+
+    return -1;
+}
+
+usb_handle *open_device(void)
+{
+    static usb_handle *usb = 0;
+    int announce = 1;
+
+    if(usb) return usb;
+    
+    for(;;) {
+        usb = usb_open(match_fastboot);
+        if(usb) return usb;
+        if(announce) {
+            announce = 0;    
+            fprintf(stderr,"< waiting for device >\n");
+        }
+        sleep(1);
+    }
+}
+
+void list_devices(void) {
+    // We don't actually open a USB device here,
+    // just getting our callback called so we can
+    // list all the connected devices.
+    usb_open(list_devices_callback);
+}
+
+void usage(void)
+{
+    fprintf(stderr,
+/*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+            "usage: fastboot [ <option> ] <command>\n"
+            "\n"
+            "commands:\n"
+            "  update <filename>                        reflash device from update.zip\n"
+            "  flashall                                 flash boot + recovery + system\n"
+            "  flash <partition> [ <filename> ]         write a file to a flash partition\n"
+            "  erase <partition>                        erase a flash partition\n"
+            "  getvar <variable>                        display a bootloader variable\n"
+            "  boot <kernel> [ <ramdisk> ]              download and boot kernel\n"
+            "  flash:raw boot <kernel> [ <ramdisk> ]    create bootimage and flash it\n"
+            "  devices                                  list all connected devices\n"
+            "  reboot                                   reboot device normally\n"
+            "  reboot-bootloader                        reboot device into bootloader\n"
+            "\n"
+            "options:\n"
+            "  -w                                       erase userdata and cache\n"
+            "  -s <serial number>                       specify device serial number\n"
+            "  -p <product>                             specify product name\n"
+            "  -c <cmdline>                             override kernel commandline\n"
+            "  -i <vendor id>                           specify a custom USB vendor id\n"
+        );
+    exit(1);
+}
+
+void *load_bootable_image(const char *kernel, const char *ramdisk, 
+                          unsigned *sz, const char *cmdline)
+{
+    void *kdata = 0, *rdata = 0;
+    unsigned ksize = 0, rsize = 0;
+    void *bdata;
+    unsigned bsize;
+
+    if(kernel == 0) {
+        fprintf(stderr, "no image specified\n");
+        return 0;
+    }
+
+    kdata = load_file(kernel, &ksize);
+    if(kdata == 0) {
+        fprintf(stderr, "cannot load '%s'\n", kernel);
+        return 0;
+    }
+    
+        /* is this actually a boot image? */
+    if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+        if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+        
+        if(ramdisk) {
+            fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
+            return 0;
+        }
+        
+        *sz = ksize;
+        return kdata;
+    }
+
+    if(ramdisk) {
+        rdata = load_file(ramdisk, &rsize);
+        if(rdata == 0) {
+            fprintf(stderr,"cannot load '%s'\n", ramdisk);
+            return  0;
+        }
+    }
+
+    fprintf(stderr,"creating boot image...\n");
+    bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, &bsize);
+    if(bdata == 0) {
+        fprintf(stderr,"failed to create boot.img\n");
+        return 0;
+    }
+    if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
+    fprintf(stderr,"creating boot image - %d bytes\n", bsize);
+    *sz = bsize;
+    
+    return bdata;
+}
+
+void *unzip_file(zipfile_t zip, const char *name, unsigned *sz)
+{
+    void *data;
+    zipentry_t entry;
+    unsigned datasz;
+    
+    entry = lookup_zipentry(zip, name);
+    if (entry == NULL) {
+        fprintf(stderr, "archive does not contain '%s'\n", name);
+        return 0;
+    }
+
+    *sz = get_zipentry_size(entry);
+
+    datasz = *sz * 1.001;
+    data = malloc(datasz);
+
+    if(data == 0) {
+        fprintf(stderr, "failed to allocate %d bytes\n", *sz);
+        return 0;
+    }
+
+    if (decompress_zipentry(entry, data, datasz)) {
+        fprintf(stderr, "failed to unzip '%s' from archive\n", name);
+        free(data);
+        return 0;
+    }
+
+    return data;
+}
+
+static char *strip(char *s)
+{
+    int n;
+    while(*s && isspace(*s)) s++;
+    n = strlen(s);
+    while(n-- > 0) {
+        if(!isspace(s[n])) break;
+        s[n] = 0;
+    }
+    return s;
+}
+
+#define MAX_OPTIONS 32
+static int setup_requirement_line(char *name)
+{
+    char *val[MAX_OPTIONS];
+    const char **out;
+    unsigned n, count;
+    char *x;
+    int invert = 0;
+    
+    if (!strncmp(name, "reject ", 7)) {
+        name += 7;
+        invert = 1;
+    } else if (!strncmp(name, "require ", 8)) {
+        name += 8;
+        invert = 0;
+    }
+
+    x = strchr(name, '=');
+    if (x == 0) return 0;
+    *x = 0;
+    val[0] = x + 1;
+
+    for(count = 1; count < MAX_OPTIONS; count++) {
+        x = strchr(val[count - 1],'|');
+        if (x == 0) break;
+        *x = 0;
+        val[count] = x + 1;
+    }
+    
+    name = strip(name);
+    for(n = 0; n < count; n++) val[n] = strip(val[n]);
+    
+    name = strip(name);
+    if (name == 0) return -1;
+
+        /* work around an unfortunate name mismatch */
+    if (!strcmp(name,"board")) name = "product";
+
+    out = malloc(sizeof(char*) * count);
+    if (out == 0) return -1;
+
+    for(n = 0; n < count; n++) {
+        out[n] = strdup(strip(val[n]));
+        if (out[n] == 0) return -1;
+    }
+
+    fb_queue_require(name, invert, n, out);
+    return 0;
+}
+
+static void setup_requirements(char *data, unsigned sz)
+{
+    char *s;
+
+    s = data;
+    while (sz-- > 0) {
+        if(*s == '\n') {
+            *s++ = 0;
+            if (setup_requirement_line(data)) {
+                die("out of memory");
+            }
+            data = s;
+        } else {
+            s++;
+        }
+    }
+}
+
+void queue_info_dump(void)
+{
+    fb_queue_notice("--------------------------------------------");
+    fb_queue_display("version-bootloader", "Bootloader Version...");
+    fb_queue_display("version-baseband",   "Baseband Version.....");
+    fb_queue_display("serialno",           "Serial Number........");
+    fb_queue_notice("--------------------------------------------");
+}
+
+void do_update_signature(zipfile_t zip, char *fn)
+{
+    void *data;
+    unsigned sz;
+    data = unzip_file(zip, fn, &sz);
+    if (data == 0) return;
+    fb_queue_download("signature", data, sz);
+    fb_queue_command("signature", "installing signature");
+}
+
+void do_update(char *fn)
+{
+    void *zdata;
+    unsigned zsize;
+    void *data;
+    unsigned sz;
+    zipfile_t zip;
+
+    queue_info_dump();
+
+    zdata = load_file(fn, &zsize);
+    if (zdata == 0) die("failed to load '%s'", fn);
+
+    zip = init_zipfile(zdata, zsize);
+    if(zip == 0) die("failed to access zipdata in '%s'");
+
+    data = unzip_file(zip, "android-info.txt", &sz);
+    if (data == 0) {
+        char *tmp;
+            /* fallback for older zipfiles */
+        data = unzip_file(zip, "android-product.txt", &sz);
+        if ((data == 0) || (sz < 1)) {
+            die("update package has no android-info.txt or android-product.txt");
+        }
+        tmp = malloc(sz + 128);
+        if (tmp == 0) die("out of memory");
+        sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
+        data = tmp;
+        sz = strlen(tmp);
+    }
+
+    setup_requirements(data, sz);
+
+    data = unzip_file(zip, "boot.img", &sz);
+    if (data == 0) die("update package missing boot.img");
+    do_update_signature(zip, "boot.sig");
+    fb_queue_flash("boot", data, sz);
+
+    data = unzip_file(zip, "recovery.img", &sz);
+    if (data != 0) {
+        do_update_signature(zip, "recovery.sig");
+        fb_queue_flash("recovery", data, sz);
+    }
+
+    data = unzip_file(zip, "system.img", &sz);
+    if (data == 0) die("update package missing system.img");
+    do_update_signature(zip, "system.sig");
+    fb_queue_flash("system", data, sz);
+}
+
+void do_send_signature(char *fn)
+{
+    void *data;
+    unsigned sz;
+    char *xtn;
+	
+    xtn = strrchr(fn, '.');
+    if (!xtn) return;
+    if (strcmp(xtn, ".img")) return;
+	
+    strcpy(xtn,".sig");
+    data = load_file(fn, &sz);
+    strcpy(xtn,".img");
+    if (data == 0) return;
+    fb_queue_download("signature", data, sz);
+    fb_queue_command("signature", "installing signature");
+}
+
+void do_flashall(void)
+{
+    char *fname;
+    void *data;
+    unsigned sz;
+
+    queue_info_dump();
+
+    fname = find_item("info", product);
+    if (fname == 0) die("cannot find android-info.txt");
+    data = load_file(fname, &sz);
+    if (data == 0) die("could not load android-info.txt");
+    setup_requirements(data, sz);
+
+    fname = find_item("boot", product);
+    data = load_file(fname, &sz);
+    if (data == 0) die("could not load boot.img");
+    do_send_signature(fname);
+    fb_queue_flash("boot", data, sz);
+
+    fname = find_item("recovery", product);
+    data = load_file(fname, &sz);
+    if (data != 0) {
+        do_send_signature(fname);
+        fb_queue_flash("recovery", data, sz);
+    }
+
+    fname = find_item("system", product);
+    data = load_file(fname, &sz);
+    if (data == 0) die("could not load system.img");
+    do_send_signature(fname);
+    fb_queue_flash("system", data, sz);   
+}
+
+#define skip(n) do { argc -= (n); argv += (n); } while (0)
+#define require(n) do { if (argc < (n)) usage(); } while (0)
+
+int do_oem_command(int argc, char **argv)
+{
+    int i;
+    char command[256];
+    if (argc <= 1) return 0;
+    
+    command[0] = 0;
+    while(1) {
+        strcat(command,*argv);
+        skip(1);
+        if(argc == 0) break;
+        strcat(command," ");
+    }
+
+    fb_queue_command(command,"");    
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    int wants_wipe = 0;
+    int wants_reboot = 0;
+    int wants_reboot_bootloader = 0;
+    void *data;
+    unsigned sz;
+
+    skip(1);
+    if (argc == 0) {
+        usage();
+        return 0;
+    }
+
+    if (!strcmp(*argv, "devices")) {
+        list_devices();
+        return 0;
+    }
+
+    while (argc > 0) {
+        if(!strcmp(*argv, "-w")) {
+            wants_wipe = 1;
+            skip(1);
+        } else if(!strcmp(*argv, "-s")) {
+            require(2);
+            serial = argv[1];
+            skip(2);
+        } else if(!strcmp(*argv, "-p")) {
+            require(2);
+            product = argv[1];
+            skip(2);
+        } else if(!strcmp(*argv, "-c")) {
+            require(2);
+            cmdline = argv[1];
+            skip(2);
+        } else if(!strcmp(*argv, "-i")) {
+            char *endptr = NULL;
+            unsigned long val;
+
+            require(2);
+            val = strtoul(argv[1], &endptr, 0);
+            if (!endptr || *endptr != '\0' || (val & ~0xffff))
+                die("invalid vendor id '%s'", argv[1]);
+            vendor_id = (unsigned short)val;
+            skip(2);
+        } else if(!strcmp(*argv, "getvar")) {
+            require(2);
+            fb_queue_display(argv[1], argv[1]);
+            skip(2);
+        } else if(!strcmp(*argv, "erase")) {
+            require(2);
+            fb_queue_erase(argv[1]);
+            skip(2);
+        } else if(!strcmp(*argv, "signature")) {
+            require(2);
+            data = load_file(argv[1], &sz);
+            if (data == 0) die("could not load '%s'", argv[1]);
+            if (sz != 256) die("signature must be 256 bytes");
+            fb_queue_download("signature", data, sz);
+            fb_queue_command("signature", "installing signature");
+            skip(2);
+        } else if(!strcmp(*argv, "reboot")) {
+            wants_reboot = 1;
+            skip(1);
+        } else if(!strcmp(*argv, "reboot-bootloader")) {
+            wants_reboot_bootloader = 1;
+            skip(1);
+        } else if (!strcmp(*argv, "continue")) {
+            fb_queue_command("continue", "resuming boot");
+            skip(1);
+        } else if(!strcmp(*argv, "boot")) {
+            char *kname = 0;
+            char *rname = 0;
+            skip(1);
+            if (argc > 0) {
+                kname = argv[0];
+                skip(1);
+            }
+            if (argc > 0) {
+                rname = argv[0];
+                skip(1);
+            }
+            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (data == 0) return 1;
+            fb_queue_download("boot.img", data, sz);
+            fb_queue_command("boot", "booting");
+        } else if(!strcmp(*argv, "flash")) {
+            char *pname = argv[1];
+            char *fname = 0;
+            require(2);
+            if (argc > 2) {
+                fname = argv[2];
+                skip(3);
+            } else {
+                fname = find_item(pname, product);
+                skip(2);
+            }
+            if (fname == 0) die("cannot determine image filename for '%s'", pname);
+            data = load_file(fname, &sz);
+            if (data == 0) die("cannot load '%s'\n", fname);
+            fb_queue_flash(pname, data, sz);
+        } else if(!strcmp(*argv, "flash:raw")) {
+            char *pname = argv[1];
+            char *kname = argv[2];
+            char *rname = 0;
+            require(3);
+            if(argc > 3) {
+                rname = argv[3];
+                skip(4);
+            } else {
+                skip(3);
+            }
+            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (data == 0) die("cannot load bootable image");
+            fb_queue_flash(pname, data, sz);
+        } else if(!strcmp(*argv, "flashall")) {
+            skip(1);
+            do_flashall();
+            wants_reboot = 1;
+        } else if(!strcmp(*argv, "update")) {
+            if (argc > 1) {
+                do_update(argv[1]);
+                skip(2);
+            } else {
+                do_update("update.zip");
+                skip(1);
+            }
+            wants_reboot = 1;
+        } else if(!strcmp(*argv, "oem")) {
+            argc = do_oem_command(argc, argv);
+        } else {
+            usage();
+        }
+    }
+
+    if (wants_wipe) {
+        fb_queue_erase("userdata");
+        fb_queue_erase("cache");
+    }
+    if (wants_reboot) {
+        fb_queue_reboot();
+    } else if (wants_reboot_bootloader) {
+        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
+    }
+
+    usb = open_device();
+
+    fb_execute_queue(usb);
+    return 0;
+}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
new file mode 100644
index 0000000..a36c569
--- /dev/null
+++ b/fastboot/fastboot.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_H_
+#define _FASTBOOT_H_
+
+#include "usb.h"
+
+/* protocol.c - fastboot protocol */
+int fb_command(usb_handle *usb, const char *cmd);
+int fb_command_response(usb_handle *usb, const char *cmd, char *response);
+int fb_download_data(usb_handle *usb, const void *data, unsigned size);
+char *fb_get_error(void);
+
+#define FB_COMMAND_SZ 64
+#define FB_RESPONSE_SZ 64
+
+/* engine.c - high level command queue engine */
+void fb_queue_flash(const char *ptn, void *data, unsigned sz);;
+void fb_queue_erase(const char *ptn);
+void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value);
+void fb_queue_display(const char *var, const char *prettyname);
+void fb_queue_reboot(void);
+void fb_queue_command(const char *cmd, const char *msg);
+void fb_queue_download(const char *name, void *data, unsigned size);
+void fb_queue_notice(const char *notice);
+void fb_execute_queue(usb_handle *usb);
+
+/* util stuff */
+void die(const char *fmt, ...);
+
+#endif
diff --git a/fastboot/genkey.sh b/fastboot/genkey.sh
new file mode 100755
index 0000000..011e902
--- /dev/null
+++ b/fastboot/genkey.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+if [ $# -ne 2 ]
+then
+ echo "Usage: $0 alias \"pass phrase\""
+ exit -1
+fi
+
+# Generate a 2048 bit RSA key with public exponent 3.
+# Encrypt private key with provided password.
+openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
+
+# Create a self-signed cert for this key.
+openssl req -new -x509 -key $1.pem -passin pass:"$2" \
+        -out $1-cert.pem \
+        -batch -days 10000
+
+# Create a PKCS12 store containing the generated private key.
+# Protect the keystore and the private key with the provided password.
+openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
+        -out $1.p12 -name $1 -passout pass:"$2"
+
+rm $1.pem
+rm $1-cert.pem
+
diff --git a/fastboot/p12topem.sh b/fastboot/p12topem.sh
new file mode 100755
index 0000000..f081eb5
--- /dev/null
+++ b/fastboot/p12topem.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+if [ $# -ne 2 ]
+then
+ echo "Usage: $0 alias passphrase"
+ exit -1
+fi
+
+openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem
diff --git a/fastboot/protocol.c b/fastboot/protocol.c
new file mode 100644
index 0000000..c788a12
--- /dev/null
+++ b/fastboot/protocol.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "fastboot.h"
+
+static char ERROR[128];
+
+char *fb_get_error(void)
+{
+    return ERROR;
+}
+
+static int check_response(usb_handle *usb, unsigned size, 
+                          unsigned data_okay, char *response)
+{
+    unsigned char status[65];
+    int r;
+
+    for(;;) {
+        r = usb_read(usb, status, 64);
+        if(r < 0) {
+            sprintf(ERROR, "status read failed (%s)", strerror(errno));
+            usb_close(usb);
+            return -1;
+        }
+        status[r] = 0;
+
+        if(r < 4) {
+            sprintf(ERROR, "status malformed (%d bytes)", r);
+            usb_close(usb);
+            return -1;
+        }
+
+        if(!memcmp(status, "INFO", 4)) {
+            fprintf(stderr,"%s\n", status);
+            continue;
+        }
+
+        if(!memcmp(status, "OKAY", 4)) {
+            if(response) {
+                strcpy(response, (char*) status + 4);
+            }
+            return 0;
+        }
+
+        if(!memcmp(status, "FAIL", 4)) {
+            if(r > 4) {
+                sprintf(ERROR, "remote: %s", status + 4);
+            } else {
+                strcpy(ERROR, "remote failure");
+            }
+            return -1;
+        }
+
+        if(!memcmp(status, "DATA", 4) && data_okay){
+            unsigned dsize = strtoul((char*) status + 4, 0, 16);
+            if(dsize > size) {
+                strcpy(ERROR, "data size too large");
+                usb_close(usb);
+                return -1;
+            }
+            return dsize;
+        }
+
+        strcpy(ERROR,"unknown status code");
+        usb_close(usb);
+        break;
+    }
+
+    return -1;
+}
+
+static int _command_send(usb_handle *usb, const char *cmd,
+                         const void *data, unsigned size,
+                         char *response)
+{
+    int cmdsize = strlen(cmd);
+    int r;
+    
+    if(response) {
+        response[0] = 0;
+    }
+
+    if(cmdsize > 64) {
+        sprintf(ERROR,"command too large");
+        return -1;
+    }
+
+    if(usb_write(usb, cmd, cmdsize) != cmdsize) {
+        sprintf(ERROR,"command write failed (%s)", strerror(errno));
+        usb_close(usb);
+        return -1;
+    }
+
+    if(data == 0) {
+        return check_response(usb, size, 0, response);
+    }
+
+    r = check_response(usb, size, 1, 0);
+    if(r < 0) {
+        return -1;
+    }
+    size = r;
+
+    if(size) {
+        r = usb_write(usb, data, size);
+        if(r < 0) {
+            sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
+            usb_close(usb);
+            return -1;
+        }
+        if(r != ((int) size)) {
+            sprintf(ERROR, "data transfer failure (short transfer)");
+            usb_close(usb);
+            return -1;
+        }
+    }
+    
+    r = check_response(usb, 0, 0, 0);
+    if(r < 0) {
+        return -1;
+    } else {
+        return size;
+    }
+}
+
+int fb_command(usb_handle *usb, const char *cmd)
+{
+    return _command_send(usb, cmd, 0, 0, 0);
+}
+
+int fb_command_response(usb_handle *usb, const char *cmd, char *response)
+{
+    return _command_send(usb, cmd, 0, 0, response);
+}
+
+int fb_download_data(usb_handle *usb, const void *data, unsigned size)
+{
+    char cmd[64];
+    int r;
+    
+    sprintf(cmd, "download:%08x", size);
+    r = _command_send(usb, cmd, data, size, 0);
+    
+    if(r < 0) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
diff --git a/fastboot/signfile.sh b/fastboot/signfile.sh
new file mode 100755
index 0000000..3188d2d
--- /dev/null
+++ b/fastboot/signfile.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [ $# -ne 3 ]
+then
+ echo "Usage: $0 alias filename passpharse"
+ exit -1
+fi
+
+openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign
+
diff --git a/fastboot/usb.h b/fastboot/usb.h
new file mode 100644
index 0000000..f3ec5bf
--- /dev/null
+++ b/fastboot/usb.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+typedef struct usb_handle usb_handle;
+
+typedef struct usb_ifc_info usb_ifc_info;
+
+struct usb_ifc_info
+{
+        /* from device descriptor */
+    unsigned short dev_vendor;
+    unsigned short dev_product;
+
+    unsigned char dev_class;
+    unsigned char dev_subclass;
+    unsigned char dev_protocol;
+    
+    unsigned char ifc_class;
+    unsigned char ifc_subclass;
+    unsigned char ifc_protocol;
+
+    unsigned char has_bulk_in;
+    unsigned char has_bulk_out;
+    
+    char serial_number[256];
+};
+  
+typedef int (*ifc_match_func)(usb_ifc_info *ifc);
+
+usb_handle *usb_open(ifc_match_func callback);
+int usb_close(usb_handle *h);
+int usb_read(usb_handle *h, void *_data, int len);
+int usb_write(usb_handle *h, const void *_data, int len);
+
+
+#endif
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
new file mode 100644
index 0000000..06c62b8
--- /dev/null
+++ b/fastboot/usb_linux.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+#include <asm/byteorder.h>
+
+#include "usb.h"
+
+#if TRACE_USB
+#define DBG1(x...) fprintf(stderr, x)
+#define DBG(x...) fprintf(stderr, x)
+#else
+#define DBG(x...)
+#define DBG1(x...)
+#endif
+
+struct usb_handle 
+{
+    char fname[64];
+    int desc;
+    unsigned char ep_in;
+    unsigned char ep_out;
+};
+
+static inline int badname(const char *name)
+{
+    while(*name) {
+        if(!isdigit(*name++)) return 1;
+    }
+    return 0;
+}
+
+static int check(void *_desc, int len, unsigned type, int size)
+{
+    unsigned char *desc = _desc;
+    
+    if(len < size) return -1;
+    if(desc[0] < size) return -1;
+    if(desc[0] > len) return -1;
+    if(desc[1] != type) return -1;
+    
+    return 0;
+}
+
+static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback,
+                             int *ept_in_id, int *ept_out_id, int *ifc_id)
+{
+    struct usb_device_descriptor *dev;
+    struct usb_config_descriptor *cfg;
+    struct usb_interface_descriptor *ifc;
+    struct usb_endpoint_descriptor *ept;
+    struct usb_ifc_info info;
+    
+    int in, out;
+    unsigned i;
+    unsigned e;
+    
+    if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
+        return -1;
+    dev = (void*) ptr;
+    len -= dev->bLength;
+    ptr += dev->bLength;
+    
+    if(check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))
+        return -1;
+    cfg = (void*) ptr;
+    len -= cfg->bLength;
+    ptr += cfg->bLength;
+    
+    info.dev_vendor = dev->idVendor;
+    info.dev_product = dev->idProduct;
+    info.dev_class = dev->bDeviceClass;
+    info.dev_subclass = dev->bDeviceSubClass;
+    info.dev_protocol = dev->bDeviceProtocol;
+
+    // read device serial number (if there is one)
+    info.serial_number[0] = 0;
+    if (dev->iSerialNumber) {
+        struct usbdevfs_ctrltransfer  ctrl;
+        __u16 buffer[128];
+        int result;
+
+        memset(buffer, 0, sizeof(buffer));
+
+        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+        ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
+        ctrl.wIndex = 0;
+        ctrl.wLength = sizeof(buffer);
+        ctrl.data = buffer;
+
+        result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
+        if (result > 0) {
+            int i;
+            // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+            result /= 2;
+            for (i = 1; i < result; i++)
+                info.serial_number[i - 1] = buffer[i];
+            info.serial_number[i - 1] = 0;
+        }
+    }
+
+    for(i = 0; i < cfg->bNumInterfaces; i++) {
+        if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE))
+            return -1;
+        ifc = (void*) ptr;
+        len -= ifc->bLength;
+        ptr += ifc->bLength;
+        
+        in = -1;
+        out = -1;
+        info.ifc_class = ifc->bInterfaceClass;
+        info.ifc_subclass = ifc->bInterfaceSubClass;
+        info.ifc_protocol = ifc->bInterfaceProtocol;
+        
+        for(e = 0; e < ifc->bNumEndpoints; e++) {
+            if(check(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE))
+                return -1;
+            ept = (void*) ptr;
+            len -= ept->bLength;
+            ptr += ept->bLength;
+    
+            if((ept->bmAttributes & 0x03) != 0x02)
+                continue;
+            
+            if(ept->bEndpointAddress & 0x80) {
+                in = ept->bEndpointAddress;
+            } else {
+                out = ept->bEndpointAddress;
+            }
+        }
+
+        info.has_bulk_in = (in != -1);
+        info.has_bulk_out = (out != -1);
+        
+        if(callback(&info) == 0) {
+            *ept_in_id = in;
+            *ept_out_id = out;
+            *ifc_id = ifc->bInterfaceNumber;
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
+{
+    usb_handle *usb = 0;
+    char busname[64], devname[64];
+    char desc[1024];
+    int n, in, out, ifc;
+    
+    DIR *busdir, *devdir;
+    struct dirent *de;
+    int fd;
+    
+    busdir = opendir(base);
+    if(busdir == 0) return 0;
+
+    while((de = readdir(busdir)) && (usb == 0)) {
+        if(badname(de->d_name)) continue;
+        
+        sprintf(busname, "%s/%s", base, de->d_name);
+        devdir = opendir(busname);
+        if(devdir == 0) continue;
+        
+//        DBG("[ scanning %s ]\n", busname);
+        while((de = readdir(devdir)) && (usb == 0)) {
+            
+            if(badname(de->d_name)) continue;
+            sprintf(devname, "%s/%s", busname, de->d_name);
+
+//            DBG("[ scanning %s ]\n", devname);
+            if((fd = open(devname, O_RDWR)) < 0) {
+                continue;
+            }
+
+            n = read(fd, desc, sizeof(desc));
+            
+            if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){
+                usb = calloc(1, sizeof(usb_handle));
+                strcpy(usb->fname, devname);
+                usb->ep_in = in;
+                usb->ep_out = out;
+                usb->desc = fd;
+
+                n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
+                if(n != 0) {
+                    close(fd);
+                    free(usb);
+                    usb = 0;
+                    continue;
+                }
+            } else {
+                close(fd);
+            }
+        }
+        closedir(devdir);
+    }
+    closedir(busdir);
+
+    return usb;
+}
+
+int usb_write(usb_handle *h, const void *_data, int len)
+{
+    unsigned char *data = (unsigned char*) _data;
+    unsigned count = 0;
+    struct usbdevfs_bulktransfer bulk;
+    int n;
+
+    if(h->ep_out == 0) {
+        return -1;
+    }
+    
+    if(len == 0) {
+        bulk.ep = h->ep_out;
+        bulk.len = 0;
+        bulk.data = data;
+        bulk.timeout = 0;
+        
+        n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+        if(n != 0) {
+            fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
+                    n, errno, strerror(errno));
+            return -1;
+        }
+        return 0;
+    }
+    
+    while(len > 0) {
+        int xfer;
+        xfer = (len > 4096) ? 4096 : len;
+        
+        bulk.ep = h->ep_out;
+        bulk.len = xfer;
+        bulk.data = data;
+        bulk.timeout = 0;
+        
+        n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+        if(n != xfer) {
+            DBG("ERROR: n = %d, errno = %d (%s)\n",
+                n, errno, strerror(errno));
+            return -1;
+        }
+
+        count += xfer;
+        len -= xfer;
+        data += xfer;
+    }
+
+    return count;
+}
+
+int usb_read(usb_handle *h, void *_data, int len)
+{
+    unsigned char *data = (unsigned char*) _data;
+    unsigned count = 0;
+    struct usbdevfs_bulktransfer bulk;
+    int n;
+
+    if(h->ep_in == 0) {
+        return -1;
+    }
+    
+    while(len > 0) {
+        int xfer = (len > 4096) ? 4096 : len;
+        
+        bulk.ep = h->ep_in;
+        bulk.len = xfer;
+        bulk.data = data;
+        bulk.timeout = 0;
+        
+        DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+        n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
+        DBG("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
+
+        if(n < 0) {
+            DBG1("ERROR: n = %d, errno = %d (%s)\n",
+                n, errno, strerror(errno));
+            return -1;
+        }
+
+        count += n;
+        len -= n;
+        data += n;
+        
+        if(n < xfer) {
+            break;
+        }
+    }
+    
+    return count;
+}
+
+void usb_kick(usb_handle *h)
+{
+    int fd;
+
+    fd = h->desc;
+    h->desc = -1;
+    if(fd >= 0) {
+        close(fd);
+        DBG("[ usb closed %d ]\n", fd);
+    }
+}
+
+int usb_close(usb_handle *h)
+{
+    int fd;
+    
+    fd = h->desc;
+    h->desc = -1;
+    if(fd >= 0) {
+        close(fd);
+        DBG("[ usb closed %d ]\n", fd);
+    }
+
+    return 0;
+}
+
+usb_handle *usb_open(ifc_match_func callback)
+{
+    return find_usb_device("/dev/bus/usb", callback);
+}
+
+
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
new file mode 100644
index 0000000..d6a8260
--- /dev/null
+++ b/fastboot/usb_osx.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOMessage.h>
+#include <mach/mach_port.h>
+
+#include "usb.h"
+
+
+/*
+ * Internal helper functions and associated definitions.
+ */
+
+#if TRACE_USB
+#define WARN(x...) fprintf(stderr, x)
+#else
+#define WARN(x...)
+#endif
+
+#define ERR(x...) fprintf(stderr, "ERROR: " x)
+
+/** An open usb device */
+struct usb_handle
+{
+    int success;
+    ifc_match_func callback;
+    usb_ifc_info info;
+    
+    UInt8 bulkIn;
+    UInt8 bulkOut;
+    IOUSBInterfaceInterface190 **interface;
+    unsigned int zero_mask;
+};
+
+/** Try out all the interfaces and see if there's a match. Returns 0 on
+ * success, -1 on failure. */
+static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) {
+    IOReturn kr;
+    IOUSBFindInterfaceRequest request;
+    io_iterator_t iterator;
+    io_service_t usbInterface;
+    IOCFPlugInInterface **plugInInterface;
+    IOUSBInterfaceInterface190 **interface = NULL;
+    HRESULT result;
+    SInt32 score;
+    UInt8 interfaceNumEndpoints;
+    UInt8 endpoint;
+    UInt8 configuration;
+
+    // Placing the constant KIOUSBFindInterfaceDontCare into the following
+    // fields of the IOUSBFindInterfaceRequest structure will allow us to
+    // find all of the interfaces
+    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+    // SetConfiguration will kill an existing UMS connection, so let's
+    // not do this if not necessary.
+    configuration = 0;
+    (*dev)->GetConfiguration(dev, &configuration);
+    if (configuration != 1)
+        (*dev)->SetConfiguration(dev, 1);
+
+    // Get an iterator for the interfaces on the device
+    kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
+
+    if (kr != 0) {
+        ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
+        return -1;
+    }
+
+    while ((usbInterface = IOIteratorNext(iterator))) {
+        // Create an intermediate plugin
+        kr = IOCreatePlugInInterfaceForService(
+                usbInterface,
+                kIOUSBInterfaceUserClientTypeID,
+                kIOCFPlugInInterfaceID,
+                &plugInInterface,
+                &score);
+
+        // No longer need the usbInterface object now that we have the plugin
+        (void) IOObjectRelease(usbInterface);
+
+        if ((kr != 0) || (!plugInInterface)) {
+            WARN("Unable to create plugin (%08x)\n", kr);
+            continue;
+        }
+
+        // Now create the interface interface for the interface
+        result = (*plugInInterface)->QueryInterface(
+                plugInInterface,
+                CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+                (LPVOID) &interface);
+
+        // No longer need the intermediate plugin
+        (*plugInInterface)->Release(plugInInterface);
+
+        if (result || !interface) {
+            ERR("Couldn't create interface interface: (%08x)\n",
+               (unsigned int) result);
+            // continue so we can try the next interface
+            continue;
+        }
+        
+        /*
+         * Now open the interface. This will cause the pipes
+         * associated with the endpoints in the interface descriptor
+         * to be instantiated.
+         */
+
+        /*
+         * TODO: Earlier comments here indicated that it was a bad
+         * idea to just open any interface, because opening "mass
+         * storage endpoints" is bad. However, the only way to find
+         * out if an interface does bulk in or out is to open it, and
+         * the framework in this application wants to be told about
+         * bulk in / out before deciding whether it actually wants to
+         * use the interface. Maybe something needs to be done about
+         * this situation.
+         */
+        
+        kr = (*interface)->USBInterfaceOpen(interface);
+
+        if (kr != 0) {
+            WARN("Could not open interface: (%08x)\n", kr);
+            (void) (*interface)->Release(interface);
+            // continue so we can try the next interface
+            continue;
+        }
+        
+        // Get the number of endpoints associated with this interface.
+        kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
+
+        if (kr != 0) {
+            ERR("Unable to get number of endpoints: (%08x)\n", kr);
+            goto next_interface;
+        }
+
+        // Get interface class, subclass and protocol
+        if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
+            (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
+            (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
+        {
+            ERR("Unable to get interface class, subclass and protocol\n");
+            goto next_interface;
+        }
+
+        handle->info.has_bulk_in = 0;
+        handle->info.has_bulk_out = 0;
+
+        // Iterate over the endpoints for this interface and see if there
+        // are any that do bulk in/out.
+        for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+            UInt8   transferType;
+            UInt16  maxPacketSize;
+            UInt8   interval;
+            UInt8   number;
+            UInt8   direction;
+
+            kr = (*interface)->GetPipeProperties(interface, endpoint,
+                    &direction,
+                    &number, &transferType, &maxPacketSize, &interval);
+
+            if (kr == 0) {
+                if (transferType != kUSBBulk) {
+                    continue;
+                }
+
+                if (direction == kUSBIn) {
+                    handle->info.has_bulk_in = 1;
+                    handle->bulkIn = endpoint;
+                } else if (direction == kUSBOut) {
+                    handle->info.has_bulk_out = 1;
+                    handle->bulkOut = endpoint;
+                }
+
+                if (handle->info.ifc_protocol == 0x01) {
+                    handle->zero_mask = maxPacketSize - 1;
+                }
+            } else {
+                ERR("could not get pipe properties\n");
+            }
+
+            if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
+                break;
+            }
+        }
+
+        if (handle->callback(&handle->info) == 0) {
+            handle->interface = interface;
+            handle->success = 1;
+
+            /*
+             * Clear both the endpoints, because it has been observed
+             * that the Mac may otherwise (incorrectly) start out with
+             * them in bad state.
+             */
+
+            if (handle->info.has_bulk_in) {
+                kr = (*interface)->ClearPipeStallBothEnds(interface,
+                        handle->bulkIn);
+                if (kr != 0) {
+                    ERR("could not clear input pipe; result %d", kr);
+                    return -1;
+                }
+            }
+
+            if (handle->info.has_bulk_out) {
+                kr = (*interface)->ClearPipeStallBothEnds(interface,
+                        handle->bulkOut);
+                if (kr != 0) {
+                    ERR("could not clear output pipe; result %d", kr);
+                    return -1;
+                }
+            }
+            
+            return 0;
+        }
+        
+next_interface:
+        (*interface)->USBInterfaceClose(interface);
+        (*interface)->Release(interface);
+    }
+
+    return 0;
+}
+
+/** Try out the given device and see if there's a match. Returns 0 on
+ * success, -1 on failure.
+ */
+static int try_device(io_service_t device, usb_handle *handle) {
+    kern_return_t kr;
+    IOCFPlugInInterface **plugin = NULL;
+    IOUSBDeviceInterface182 **dev = NULL;
+    SInt32 score;
+    HRESULT result;
+    UInt8 serialIndex;
+
+    // Create an intermediate plugin.
+    kr = IOCreatePlugInInterfaceForService(device,
+            kIOUSBDeviceUserClientTypeID,
+            kIOCFPlugInInterfaceID,
+            &plugin, &score);
+
+    if ((kr != 0) || (plugin == NULL)) {
+        ERR("Unable to create a plug-in (%08x)\n", kr);
+        goto error;
+    }
+
+    // Now create the device interface.
+    result = (*plugin)->QueryInterface(plugin,
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+    if ((result != 0) || (dev == NULL)) {
+        ERR("Couldn't create a device interface (%08x)\n", (int) result);
+        goto error;
+    }
+
+    /* 
+     * We don't need the intermediate interface after the device interface
+     * is created.
+     */
+    IODestroyPlugInInterface(plugin);
+
+    // So, we have a device, finally. Grab its vitals.
+
+    kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
+    if (kr != 0) {
+        ERR("GetDeviceVendor");
+        goto error;
+    }
+
+    kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
+    if (kr != 0) {
+        ERR("GetDeviceProduct");
+        goto error;
+    }
+
+    kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
+    if (kr != 0) {
+        ERR("GetDeviceClass");
+        goto error;
+    }
+
+    kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
+    if (kr != 0) {
+        ERR("GetDeviceSubClass");
+        goto error;
+    }
+
+    kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
+    if (kr != 0) {
+        ERR("GetDeviceProtocol");
+        goto error;
+    }
+
+    kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+    if (serialIndex > 0) {
+        IOUSBDevRequest req;
+        UInt16  buffer[256];
+
+        req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+        req.bRequest = kUSBRqGetDescriptor;
+        req.wValue = (kUSBStringDesc << 8) | serialIndex;
+        req.wIndex = 0;
+        req.pData = buffer;
+        req.wLength = sizeof(buffer);
+        kr = (*dev)->DeviceRequest(dev, &req);
+
+        if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+            int i, count;
+            
+            // skip first word, and copy the rest to the serial string, changing shorts to bytes.
+            count = (req.wLenDone - 1) / 2;
+            for (i = 0; i < count; i++)
+              handle->info.serial_number[i] = buffer[i + 1];
+            handle->info.serial_number[i] = 0;
+        }
+    } else {
+        // device has no serial number
+        handle->info.serial_number[0] = 0;
+    }
+
+    if (try_interfaces(dev, handle)) {
+        goto error;
+    }
+
+    (*dev)->Release(dev);
+    return 0;
+
+    error:
+
+    if (dev != NULL) {
+        (*dev)->Release(dev);
+    }
+    
+    return -1;    
+}
+
+
+/** Initializes the USB system. Returns 0 on success, -1 on error. */
+static int init_usb(ifc_match_func callback, usb_handle **handle) {
+    int ret = -1;
+    CFMutableDictionaryRef matchingDict;
+    kern_return_t result;
+    io_iterator_t iterator;
+    usb_handle h;
+
+    h.success = 0;
+    h.callback = callback;
+
+    /*
+     * Create our matching dictionary to find appropriate devices.
+     * IOServiceAddMatchingNotification consumes the reference, so we
+     * do not need to release it.
+     */
+    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+
+    if (matchingDict == NULL) {
+        ERR("Couldn't create USB matching dictionary.\n");
+        return -1;
+    }
+
+    result = IOServiceGetMatchingServices(
+            kIOMasterPortDefault, matchingDict, &iterator);
+
+    if (result != 0) {
+        ERR("Could not create iterator.");
+        return -1;
+    }
+    
+    for (;;) {
+        if (! IOIteratorIsValid(iterator)) {
+            /*
+             * Apple documentation advises resetting the iterator if
+             * it should become invalid during iteration.
+             */
+            IOIteratorReset(iterator);
+            continue;
+        }
+                
+        io_service_t device = IOIteratorNext(iterator);
+
+        if (device == 0) {
+            break;
+        }
+
+        usb_ifc_info info;
+
+        if (try_device(device, &h) != 0) {
+            IOObjectRelease(device);
+            ret = -1;
+            break;
+        }
+
+        if (h.success) {
+            *handle = calloc(1, sizeof(usb_handle));
+            memcpy(*handle, &h, sizeof(usb_handle));
+            ret = 0;
+            break;
+        }
+
+        IOObjectRelease(device);
+    }
+
+    IOObjectRelease(iterator);
+
+    return ret;
+}
+
+
+
+/*
+ * Definitions of this file's public functions.
+ */
+
+usb_handle *usb_open(ifc_match_func callback) {
+    usb_handle *handle = NULL;
+
+    if (init_usb(callback, &handle) < 0) {
+        /* Something went wrong initializing USB. */
+        return NULL;
+    }
+
+    return handle;
+}
+
+int usb_close(usb_handle *h) {
+    /* TODO: Something better here? */
+    return 0;
+}
+
+int usb_read(usb_handle *h, void *data, int len) {
+    IOReturn result;
+    UInt32 numBytes = len;
+
+    if (len == 0) {
+        return 0;
+    }
+
+    if (h == NULL) {
+        return -1;
+    }
+
+    if (h->interface == NULL) {
+        ERR("usb_read interface was null\n");
+        return -1;
+    }
+
+    if (h->bulkIn == 0) {
+        ERR("bulkIn endpoint not assigned\n");
+        return -1;
+    }
+
+    result = (*h->interface)->ReadPipe(
+            h->interface, h->bulkIn, data, &numBytes);
+
+    if (result == 0) {
+        return (int) numBytes;
+    } else {
+        ERR("usb_read failed with status %x\n", result);
+    }
+
+    return -1;
+}
+
+int usb_write(usb_handle *h, const void *data, int len) {
+    IOReturn result;
+
+    if (len == 0) {
+        return 0;
+    }
+
+    if (h == NULL) {
+        return -1;
+    }
+
+    if (h->interface == NULL) {
+        ERR("usb_write interface was null\n");
+        return -1;
+    }
+
+    if (h->bulkOut == 0) {
+        ERR("bulkOut endpoint not assigned\n");
+        return -1;
+    }
+
+    result = (*h->interface)->WritePipe(
+            h->interface, h->bulkOut, (void *)data, len);
+
+    #if 0
+    if ((result == 0) && (h->zero_mask)) {
+        /* we need 0-markers and our transfer */
+        if(!(len & h->zero_mask)) {
+            result = (*h->interface)->WritePipe(
+                    h->interface, h->bulkOut, (void *)data, 0);
+        }
+    }
+    #endif
+
+    if (result != 0) {
+        ERR("usb_write failed with status %x\n", result);
+        return -1;
+    }
+
+    return len;
+}
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c
new file mode 100644
index 0000000..9c0a9cb
--- /dev/null
+++ b/fastboot/usb_windows.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <winerror.h>
+#include <errno.h>
+#include <usb100.h>
+#include <adb_api.h>
+#include <stdio.h>
+
+#include "usb.h"
+
+//#define TRACE_USB 1
+#if TRACE_USB
+#define DBG(x...) fprintf(stderr, x)
+#else
+#define DBG(x...)
+#endif
+
+
+/** Structure usb_handle describes our connection to the usb device via
+  AdbWinApi.dll. This structure is returned from usb_open() routine and
+  is expected in each subsequent call that is accessing the device.
+*/
+struct usb_handle {
+    /// Handle to USB interface
+    ADBAPIHANDLE  adb_interface;
+    
+    /// Handle to USB read pipe (endpoint)
+    ADBAPIHANDLE  adb_read_pipe;
+    
+    /// Handle to USB write pipe (endpoint)
+    ADBAPIHANDLE  adb_write_pipe;
+    
+    /// Interface name
+    char*         interface_name;
+};
+
+/// Class ID assigned to the device by androidusb.sys
+static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
+
+
+/// Checks if interface (device) matches certain criteria
+int recognized_device(usb_handle* handle, ifc_match_func callback);
+
+/// Opens usb interface (device) by interface (device) name.
+usb_handle* do_usb_open(const wchar_t* interface_name);
+
+/// Writes data to the opened usb handle
+int usb_write(usb_handle* handle, const void* data, int len);
+
+/// Reads data using the opened usb handle
+int usb_read(usb_handle *handle, void* data, int len);
+
+/// Cleans up opened usb handle
+void usb_cleanup_handle(usb_handle* handle);
+
+/// Cleans up (but don't close) opened usb handle
+void usb_kick(usb_handle* handle);
+
+/// Closes opened usb handle
+int usb_close(usb_handle* handle);
+
+
+usb_handle* do_usb_open(const wchar_t* interface_name) {
+    // Allocate our handle
+    usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
+    if (NULL == ret)
+        return NULL;
+
+    // Create interface.
+    ret->adb_interface = AdbCreateInterfaceByName(interface_name);
+
+    if (NULL == ret->adb_interface) {
+        free(ret);
+        errno = GetLastError();
+        return NULL;
+    }
+
+    // Open read pipe (endpoint)
+    ret->adb_read_pipe =
+        AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
+                                   AdbOpenAccessTypeReadWrite,
+                                   AdbOpenSharingModeReadWrite);
+    if (NULL != ret->adb_read_pipe) {
+        // Open write pipe (endpoint)
+        ret->adb_write_pipe =
+            AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+                                      AdbOpenAccessTypeReadWrite,
+                                      AdbOpenSharingModeReadWrite);
+        if (NULL != ret->adb_write_pipe) {
+            // Save interface name
+            unsigned long name_len = 0;
+
+            // First get expected name length
+            AdbGetInterfaceName(ret->adb_interface,
+                          NULL,
+                          &name_len,
+                          true);
+            if (0 != name_len) {
+                ret->interface_name = (char*)malloc(name_len);
+
+                if (NULL != ret->interface_name) {
+                    // Now save the name
+                    if (AdbGetInterfaceName(ret->adb_interface,
+                                  ret->interface_name,
+                                  &name_len,
+                                  true)) {
+                        // We're done at this point
+                        return ret;
+                    }
+                } else {
+                    SetLastError(ERROR_OUTOFMEMORY);
+                }
+            }
+        }
+    }
+
+    // Something went wrong.
+    errno = GetLastError();
+    usb_cleanup_handle(ret);
+    free(ret);
+    SetLastError(errno);
+
+    return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+    unsigned long time_out = 500 + len * 8;
+    unsigned long written = 0;
+    unsigned count = 0;
+    int ret;
+
+    DBG("usb_write %d\n", len);
+    if (NULL != handle) {
+        // Perform write
+        while(len > 0) {
+            int xfer = (len > 4096) ? 4096 : len;
+            ret = AdbWriteEndpointSync(handle->adb_write_pipe,
+                                   (void*)data,
+                                   (unsigned long)xfer,
+                                   &written,
+                                   time_out);
+            errno = GetLastError();
+            DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
+            if (ret == 0) {
+                // assume ERROR_INVALID_HANDLE indicates we are disconnected
+                if (errno == ERROR_INVALID_HANDLE)
+                usb_kick(handle);
+                return -1;
+            }
+
+            count += written;
+            len -= written;
+            data += written;
+
+            if (len == 0)
+                return count;
+        }
+    } else {
+        DBG("usb_write NULL handle\n");
+        SetLastError(ERROR_INVALID_HANDLE);
+    }
+
+    DBG("usb_write failed: %d\n", errno);
+
+    return -1;
+}
+
+int usb_read(usb_handle *handle, void* data, int len) {
+    unsigned long time_out = 500 + len * 8;
+    unsigned long read = 0;
+    int ret;
+
+    DBG("usb_read %d\n", len);
+    if (NULL != handle) {
+        while (1) {
+            int xfer = (len > 4096) ? 4096 : len;
+
+	        ret = AdbReadEndpointSync(handle->adb_read_pipe,
+	                              (void*)data,
+	                              (unsigned long)xfer,
+	                              &read,
+	                              time_out);
+            errno = GetLastError();
+            DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
+            if (ret) {
+                return read;
+            } else if (errno != ERROR_SEM_TIMEOUT) {
+                // assume ERROR_INVALID_HANDLE indicates we are disconnected
+                if (errno == ERROR_INVALID_HANDLE)
+                    usb_kick(handle);
+                break;
+            }
+            // else we timed out - try again
+        }
+    } else {
+        DBG("usb_read NULL handle\n");
+        SetLastError(ERROR_INVALID_HANDLE);
+    }
+
+    DBG("usb_read failed: %d\n", errno);
+
+    return -1;
+}
+
+void usb_cleanup_handle(usb_handle* handle) {
+    if (NULL != handle) {
+        if (NULL != handle->interface_name)
+            free(handle->interface_name);
+        if (NULL != handle->adb_write_pipe)
+            AdbCloseHandle(handle->adb_write_pipe);
+        if (NULL != handle->adb_read_pipe)
+            AdbCloseHandle(handle->adb_read_pipe);
+        if (NULL != handle->adb_interface)
+            AdbCloseHandle(handle->adb_interface);
+
+        handle->interface_name = NULL;
+        handle->adb_write_pipe = NULL;
+        handle->adb_read_pipe = NULL;
+        handle->adb_interface = NULL;
+    }
+}
+
+void usb_kick(usb_handle* handle) {
+    if (NULL != handle) {
+        usb_cleanup_handle(handle);
+    } else {
+        SetLastError(ERROR_INVALID_HANDLE);
+        errno = ERROR_INVALID_HANDLE;
+    }
+}
+
+int usb_close(usb_handle* handle) {
+    DBG("usb_close\n");
+
+    if (NULL != handle) {
+        // Cleanup handle
+        usb_cleanup_handle(handle);
+        free(handle);
+    }
+
+    return 0;
+}
+
+int recognized_device(usb_handle* handle, ifc_match_func callback) {
+    struct usb_ifc_info info;
+    USB_DEVICE_DESCRIPTOR device_desc;
+    USB_INTERFACE_DESCRIPTOR interf_desc;
+
+    if (NULL == handle)
+        return 0;
+
+    // Check vendor and product id first
+    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
+                                 &device_desc)) {
+        return 0;
+    }
+
+    // Then check interface properties
+    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
+                                    &interf_desc)) {
+        return 0;
+    }
+
+    // Must have two endpoints
+    if (2 != interf_desc.bNumEndpoints) {
+        return 0;
+    }
+
+    info.dev_vendor = device_desc.idVendor;
+    info.dev_product = device_desc.idProduct;
+    info.dev_class = device_desc.bDeviceClass;
+    info.dev_subclass = device_desc.bDeviceSubClass;
+    info.dev_protocol = device_desc.bDeviceProtocol;
+    info.ifc_class = interf_desc.bInterfaceClass;
+    info.ifc_subclass = interf_desc.bInterfaceSubClass;
+    info.ifc_protocol = interf_desc.bInterfaceProtocol;
+    
+    // read serial number (if there is one)
+    unsigned long serial_number_len = sizeof(info.serial_number);
+    if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number,
+                    &serial_number_len, true)) {
+        info.serial_number[0] = 0;
+    }
+
+    if (callback(&info) == 0) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static usb_handle *find_usb_device(ifc_match_func callback) {
+	usb_handle* handle = NULL;
+    char entry_buffer[2048];
+    char interf_name[2048];
+    AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
+    unsigned long entry_buffer_size = sizeof(entry_buffer);
+    char* copy_name;
+
+    // Enumerate all present and active interfaces.
+    ADBAPIHANDLE enum_handle =
+        AdbEnumInterfaces(usb_class_id, true, true, true);
+
+    if (NULL == enum_handle)
+        return NULL;
+
+    while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
+        // TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char.
+        // It would be better to change AdbNextInterface so it will return
+        // interface name as single char string.
+        const wchar_t* wchar_name = next_interface->device_name;
+        for(copy_name = interf_name;
+                L'\0' != *wchar_name;
+                wchar_name++, copy_name++) {
+            *copy_name = (char)(*wchar_name);
+        }
+        *copy_name = '\0';
+
+        handle = do_usb_open(next_interface->device_name);
+        if (NULL != handle) {
+            // Lets see if this interface (device) belongs to us
+            if (recognized_device(handle, callback)) {
+                // found it!
+                break;
+            } else {
+                usb_cleanup_handle(handle);
+                free(handle);
+                handle = NULL;
+            }
+        }
+
+        entry_buffer_size = sizeof(entry_buffer);
+    }
+
+    AdbCloseHandle(enum_handle);
+    return handle;
+}
+
+usb_handle *usb_open(ifc_match_func callback)
+{
+    return find_usb_device(callback);
+}
+
+// called from fastboot.c
+void sleep(int seconds)
+{
+    Sleep(seconds * 1000);
+}
diff --git a/fastboot/usbtest.c b/fastboot/usbtest.c
new file mode 100644
index 0000000..e34d7e6
--- /dev/null
+++ b/fastboot/usbtest.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/time.h>
+
+#include "usb.h"
+
+static unsigned arg_size = 4096;
+static unsigned arg_count = 4096;
+
+long long NOW(void) 
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    
+    return (((long long) tv.tv_sec) * ((long long) 1000000)) +
+        (((long long) tv.tv_usec));
+}
+
+int printifc(usb_ifc_info *info)
+{
+    printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x  ",
+           info->dev_class, info->dev_subclass, info->dev_protocol,
+           info->dev_vendor, info->dev_product);
+    printf("ifc: csp=%02x/%02x/%02x%s%s\n",
+           info->ifc_class, info->ifc_subclass, info->ifc_protocol,
+           info->has_bulk_in ? " in" : "",
+           info->has_bulk_out ? " out" : "");
+    return -1;
+}
+
+int match_null(usb_ifc_info *info)
+{
+    if(info->dev_vendor != 0x18d1) return -1;
+    if(info->ifc_class != 0xff) return -1;
+    if(info->ifc_subclass != 0xfe) return -1;
+    if(info->ifc_protocol != 0x01) return -1;
+    return 0;
+}
+
+int match_zero(usb_ifc_info *info)
+{
+    if(info->dev_vendor != 0x18d1) return -1;
+    if(info->ifc_class != 0xff) return -1;
+    if(info->ifc_subclass != 0xfe) return -1;
+    if(info->ifc_protocol != 0x02) return -1;
+    return 0;
+}
+
+int match_loop(usb_ifc_info *info)
+{
+    if(info->dev_vendor != 0x18d1) return -1;
+    if(info->ifc_class != 0xff) return -1;
+    if(info->ifc_subclass != 0xfe) return -1;
+    if(info->ifc_protocol != 0x03) return -1;
+    return 0;
+}
+
+int test_null(usb_handle *usb)
+{
+    int i;
+    unsigned char buf[4096];
+    memset(buf, 0xee, 4096);
+    long long t0, t1;
+
+    t0 = NOW();
+    for(i = 0; i < arg_count; i++) {
+        if(usb_write(usb, buf, arg_size) != arg_size) {
+            fprintf(stderr,"write failed (%s)\n", strerror(errno));
+            return -1;
+        }
+    }
+    t1 = NOW();
+    fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
+    return 0;
+}
+
+int test_zero(usb_handle *usb)
+{
+    int i;
+    unsigned char buf[4096];
+    long long t0, t1;
+    
+    t0 = NOW();
+    for(i = 0; i < arg_count; i++) {
+        if(usb_read(usb, buf, arg_size) != arg_size) {
+            fprintf(stderr,"read failed (%s)\n", strerror(errno));
+            return -1;
+        }
+    }
+    t1 = NOW();
+    fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
+    return 0;
+}
+
+struct 
+{
+    const char *cmd;
+    ifc_match_func match;
+    int (*test)(usb_handle *usb);
+    const char *help;
+} tests[] = {
+    { "list", printifc,   0,         "list interfaces" },
+    { "send", match_null, test_null, "send to null interface" },
+    { "recv", match_zero, test_zero, "recv from zero interface" },
+    { "loop", match_loop, 0,         "exercise loopback interface" },
+    {},
+};
+
+int usage(void)
+{
+    int i;
+
+    fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n");
+    for(i = 0; tests[i].cmd; i++) {
+        fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help);
+    }
+    return -1;
+}
+
+int process_args(int argc, char **argv)
+{
+    while(argc-- > 0) {
+        char *arg = *argv++;
+        if(!strncmp(arg,"count=",6)) {
+            arg_count = atoi(arg + 6);
+        } else if(!strncmp(arg,"size=",5)) {
+            arg_size = atoi(arg + 5);
+        } else {
+            fprintf(stderr,"unknown argument: %s\n", arg);
+            return -1;
+        }
+    }
+
+    if(arg_count == 0) {
+        fprintf(stderr,"count may not be zero\n");
+        return -1;
+    }
+
+    if(arg_size > 4096) {
+        fprintf(stderr,"size may not be greater than 4096\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    usb_handle *usb;
+    int i;
+    
+    if(argc < 2)
+        return usage();
+
+    if(argc > 2) {
+        if(process_args(argc - 2, argv + 2)) 
+            return -1;
+    }
+
+    for(i = 0; tests[i].cmd; i++) {
+        if(!strcmp(argv[1], tests[i].cmd)) {
+            usb = usb_open(tests[i].match);
+            if(tests[i].test) {
+                if(usb == 0) {
+                    fprintf(stderr,"usbtest: %s: could not find interface\n",
+                            tests[i].cmd);
+                    return -1;
+                }
+                if(tests[i].test(usb)) {
+                    fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd);
+                    return -1;
+                } else {
+                    fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd);
+                }
+            }
+            return 0;
+        }
+    }
+
+    return usage();
+}
diff --git a/fastboot/util_linux.c b/fastboot/util_linux.c
new file mode 100644
index 0000000..912e16f
--- /dev/null
+++ b/fastboot/util_linux.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+void get_my_path(char *path)
+{
+    char proc[64];
+    char *x;
+    
+    sprintf(proc, "/proc/%d/exe", getpid());
+    int err = readlink(proc, path, PATH_MAX - 1);
+
+    if(err <= 0) {
+        path[0] = 0;
+    } else {
+        path[err] = 0;
+        x = strrchr(path,'/');
+        if(x) x[1] = 0;
+    }
+}
diff --git a/fastboot/util_osx.c b/fastboot/util_osx.c
new file mode 100644
index 0000000..068241c
--- /dev/null
+++ b/fastboot/util_osx.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <utils/executablepath.h>
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void get_my_path(char s[PATH_MAX])
+{
+    char *x;
+    ProcessSerialNumber psn;
+    GetCurrentProcess(&psn);
+    CFDictionaryRef dict;
+    dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+    CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
+                CFSTR("CFBundleExecutable"));
+    CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
+    x = strrchr(s, '/');
+    if(x) x[1] = 0;
+}
+
+
diff --git a/fastboot/util_windows.c b/fastboot/util_windows.c
new file mode 100644
index 0000000..37077a4
--- /dev/null
+++ b/fastboot/util_windows.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <windows.h>
+
+void get_my_path(char exe[PATH_MAX])
+{
+	char*  r;
+
+	GetModuleFileName( NULL, exe, PATH_MAX-1 );
+	exe[PATH_MAX-1] = 0;
+	r = strrchr( exe, '\\' );
+	if (r)
+		*r = 0;
+}
+
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+    HANDLE    file;
+    char     *data;
+    DWORD     file_size;
+
+    file = CreateFile( fn,
+                       GENERIC_READ,
+                       FILE_SHARE_READ,
+                       NULL,
+                       OPEN_EXISTING,
+                       0,
+                       NULL );
+
+    if (file == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    file_size = GetFileSize( file, NULL );
+    data      = NULL;
+
+    if (file_size > 0) {
+        data = (char*) malloc( file_size );
+        if (data == NULL) {
+            fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size );
+            file_size = 0;
+        } else {
+            DWORD  out_bytes;
+
+            if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
+                 out_bytes != file_size )
+            {
+                fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+                free(data);
+                data      = NULL;
+                file_size = 0;
+            }
+        }
+    }
+    CloseHandle( file );
+
+    *_sz = (unsigned) file_size;
+    return  data;
+}
diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h
new file mode 100644
index 0000000..49f04e5
--- /dev/null
+++ b/include/arch/darwin-x86/AndroidConfig.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android config -- "Darwin".  Used for PPC Mac OS X.
+ *
+ * TODO: split this into "x86" and "ppc" versions
+ */
+#ifndef _ANDROID_CONFIG_H
+#define _ANDROID_CONFIG_H
+
+/*
+ * ===========================================================================
+ *                              !!! IMPORTANT !!!
+ * ===========================================================================
+ *
+ * This file is included by ALL C/C++ source files.  Don't put anything in
+ * here unless you are absolutely certain it can't go anywhere else.
+ *
+ * Any C++ stuff must be wrapped with "#ifdef __cplusplus".  Do not use "//"
+ * comments.
+ */
+
+/*
+ * Threading model.  Choose one:
+ *
+ * HAVE_PTHREADS - use the pthreads library.
+ * HAVE_WIN32_THREADS - use Win32 thread primitives.
+ *  -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
+ */
+#define HAVE_PTHREADS
+
+/*
+ * Do we have the futex syscall?
+ */
+
+/* #define HAVE_FUTEX */
+
+/*
+ * Process creation model.  Choose one:
+ *
+ * HAVE_FORKEXEC - use fork() and exec()
+ * HAVE_WIN32_PROC - use CreateProcess()
+ */
+#define HAVE_FORKEXEC
+
+/*
+ * Process out-of-memory adjustment.  Set if running on Linux,
+ * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
+ * badness adjustment.
+ */
+/* #define HAVE_OOM_ADJ */
+
+/*
+ * IPC model.  Choose one:
+ *
+ * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
+ * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
+ * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
+ * HAVE_ANDROID_IPC - use Android versions (?, mmap).
+ */
+#define HAVE_MACOSX_IPC
+
+/*
+ * Memory-mapping model. Choose one:
+ *
+ * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
+ * HAVE_WIN32_FILEMAP - use Win32 filemaps
+ */
+#define  HAVE_POSIX_FILEMAP
+
+/*
+ * Define this if you have <termio.h>
+ */
+#define  HAVE_TERMIO_H
+
+/*
+ * Define this if you build against MSVCRT.DLL
+ */
+/* #define HAVE_MS_C_RUNTIME */
+
+/*
+ * Define this if you have sys/uio.h
+ */
+#define  HAVE_SYS_UIO_H
+
+/*
+ * Define this if your platforms implements symbolic links
+ * in its filesystems
+ */
+#define HAVE_SYMLINKS
+
+/*
+ * Define this if we have localtime_r().
+ */
+#define HAVE_LOCALTIME_R
+
+/*
+ * Define this if we have gethostbyname_r().
+ */
+/* #define HAVE_GETHOSTBYNAME_R */
+
+/*
+ * Define this if we have ioctl().
+ */
+/* #define HAVE_IOCTL */
+
+/*
+ * Define this if we want to use WinSock.
+ */
+/* #define HAVE_WINSOCK */
+
+/*
+ * Define this if have clock_gettime() and friends
+ */
+/* #define HAVE_POSIX_CLOCKS */
+
+/*
+ * Define this if we have pthread_cond_timedwait_monotonic() and
+ * clock_gettime(CLOCK_MONOTONIC).
+ */
+/* #define HAVE_TIMEDWAIT_MONOTONIC */
+
+/*
+ * Endianness of the target machine.  Choose one:
+ *
+ * HAVE_ENDIAN_H -- have endian.h header we can include.
+ * HAVE_LITTLE_ENDIAN -- we are little endian.
+ * HAVE_BIG_ENDIAN -- we are big endian.
+ */
+#if (defined(__ppc__) || defined(__ppc64__))
+#   define HAVE_BIG_ENDIAN
+#elif defined(__i386__)
+#   define HAVE_LITTLE_ENDIAN
+#endif
+
+/*
+ * We need to choose between 32-bit and 64-bit off_t.  All of our code should
+ * agree on the same size.  For desktop systems, use 64-bit values,
+ * because some of our libraries (e.g. wxWidgets) expect to be built that way.
+ */
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE_SOURCE 1
+
+/*
+ * Defined if we have the backtrace() call for retrieving a stack trace.
+ * Needed for CallStack to operate; if not defined, CallStack is
+ * non-functional.
+ */
+#define HAVE_BACKTRACE 0
+
+/*
+ * Defined if we have the dladdr() call for retrieving the symbol associated
+ * with a memory address.  If not defined, stack crawls will not have symbolic
+ * information.
+ */
+#define HAVE_DLADDR 0
+
+/*
+ * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
+ * not defined, stack crawls will be displayed with raw mangled symbols
+ */
+#define HAVE_CXXABI 0
+
+/*
+ * Defined if we have the gettid() system call.
+ */
+/* #define HAVE_GETTID */
+
+
+/*
+ * Add any extra platform-specific defines here.
+ */
+#define _THREAD_SAFE
+
+/*
+ * Define if we have <malloc.h> header
+ */
+/* #define HAVE_MALLOC_H */
+
+/*
+ * Define if tm struct has tm_gmtoff field
+ */
+#define HAVE_TM_GMTOFF 1
+
+/*
+ * Define if dirent struct has d_type field
+ */
+#define HAVE_DIRENT_D_TYPE 1
+
+/*
+ * Define if we have madvise() in <sys/mman.h>
+ */
+#define HAVE_MADVISE 1
+
+/*
+ * Define if we include <sys/mount.h> for statfs()
+ */
+#define INCLUDE_SYS_MOUNT_FOR_STATFS 1
+
+/*
+ * What CPU architecture does this platform use?
+ */
+#if (defined(__ppc__) || defined(__ppc64__))
+#   define ARCH_PPC
+#elif defined(__i386__)
+#   define ARCH_X86
+#endif
+
+/*
+ * sprintf() format string for shared library naming.
+ */
+#define OS_SHARED_LIB_FORMAT_STR    "lib%s.dylib"
+
+/*
+ * type for the third argument to mincore().
+ */
+#define MINCORE_POINTER_TYPE char *
+
+/*
+ * The default path separator for the platform
+ */
+#define OS_PATH_SEPARATOR '/'
+
+/*
+ * Is the filesystem case sensitive?
+ *
+ * For tools apps, we'll treat is as not case sensitive.
+ */
+/* #define OS_CASE_SENSITIVE */
+
+/*
+ * Define if <sys/socket.h> exists.
+ */
+#define HAVE_SYS_SOCKET_H 1
+
+/*
+ * Define if the strlcpy() function exists on the system.
+ */
+#define HAVE_STRLCPY 1
+
+/*
+ * Define if writev() exists
+ */
+#define HAVE_WRITEV 1
+
+#endif /*_ANDROID_CONFIG_H*/
diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h
new file mode 100644
index 0000000..f322127
--- /dev/null
+++ b/include/arch/linux-arm/AndroidConfig.h
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android config -- "android-arm".  Used for ARM device builds.
+ */
+#ifndef _ANDROID_CONFIG_H
+#define _ANDROID_CONFIG_H
+
+/*
+ * ===========================================================================
+ *                              !!! IMPORTANT !!!
+ * ===========================================================================
+ *
+ * This file is included by ALL C/C++ source files.  Don't put anything in
+ * here unless you are absolutely certain it can't go anywhere else.
+ *
+ * Any C++ stuff must be wrapped with "#ifdef __cplusplus".  Do not use "//"
+ * comments.
+ */
+
+/*
+ * Threading model.  Choose one:
+ *
+ * HAVE_PTHREADS - use the pthreads library.
+ * HAVE_WIN32_THREADS - use Win32 thread primitives.
+ *  -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
+ */
+#define HAVE_PTHREADS
+
+/*
+ * Do we have the futex syscall?
+ */
+
+#define HAVE_FUTEX
+
+/*
+ * Define if we already have the futex wrapper functions defined. Yes if
+ * compiling against bionic.
+ */
+#define HAVE_FUTEX_WRAPPERS 1
+
+/*
+ * Process creation model.  Choose one:
+ *
+ * HAVE_FORKEXEC - use fork() and exec()
+ * HAVE_WIN32_PROC - use CreateProcess()
+ */
+#define HAVE_FORKEXEC
+
+/*
+ * Process out-of-memory adjustment.  Set if running on Linux,
+ * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
+ * badness adjustment.
+ */
+#define HAVE_OOM_ADJ
+
+/*
+ * IPC model.  Choose one:
+ *
+ * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
+ * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
+ * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
+ * HAVE_ANDROID_IPC - use Android versions (?, mmap).
+ */
+#define HAVE_ANDROID_IPC
+
+/*
+ * Memory-mapping model. Choose one:
+ *
+ * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
+ * HAVE_WIN32_FILEMAP - use Win32 filemaps
+ */
+#define  HAVE_POSIX_FILEMAP
+
+/*
+ * Define this if you have <termio.h>
+ */
+#define  HAVE_TERMIO_H
+
+/*
+ * Define this if you build against MSVCRT.DLL
+ */
+/* #define HAVE_MS_C_RUNTIME */
+
+/*
+ * Define this if you have sys/uio.h
+ */
+#define  HAVE_SYS_UIO_H
+
+/*
+ * Define this if your platforms implements symbolic links
+ * in its filesystems
+ */
+#define HAVE_SYMLINKS
+
+/*
+ * Define this if we have localtime_r().
+ */
+/* #define HAVE_LOCALTIME_R */
+
+/*
+ * Define this if we have gethostbyname_r().
+ */
+/* #define HAVE_GETHOSTBYNAME_R */
+
+/*
+ * Define this if we have ioctl().
+ */
+#define HAVE_IOCTL
+
+/*
+ * Define this if we want to use WinSock.
+ */
+/* #define HAVE_WINSOCK */
+
+/*
+ * Define this if have clock_gettime() and friends
+ */
+#define HAVE_POSIX_CLOCKS
+
+/*
+ * Define this if we have pthread_cond_timedwait_monotonic() and
+ * clock_gettime(CLOCK_MONOTONIC).
+ */
+#define HAVE_TIMEDWAIT_MONOTONIC
+
+/*
+ * Define this if we have linux style epoll()
+ */
+#define HAVE_EPOLL
+
+/*
+ * Endianness of the target machine.  Choose one:
+ *
+ * HAVE_ENDIAN_H -- have endian.h header we can include.
+ * HAVE_LITTLE_ENDIAN -- we are little endian.
+ * HAVE_BIG_ENDIAN -- we are big endian.
+ */
+#define HAVE_ENDIAN_H
+#define HAVE_LITTLE_ENDIAN
+
+/*
+ * We need to choose between 32-bit and 64-bit off_t.  All of our code should
+ * agree on the same size.  For desktop systems, use 64-bit values,
+ * because some of our libraries (e.g. wxWidgets) expect to be built that way.
+ */
+/* #define _FILE_OFFSET_BITS 64 */
+/* #define _LARGEFILE_SOURCE 1 */
+
+/*
+ * Defined if we have the backtrace() call for retrieving a stack trace.
+ * Needed for CallStack to operate; if not defined, CallStack is
+ * non-functional.
+ */
+#define HAVE_BACKTRACE 0
+
+/*
+ * Defined if we have the dladdr() call for retrieving the symbol associated
+ * with a memory address.  If not defined, stack crawls will not have symbolic
+ * information.
+ */
+#define HAVE_DLADDR 0
+
+/*
+ * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
+ * not defined, stack crawls will be displayed with raw mangled symbols
+ */
+#define HAVE_CXXABI 0
+
+/*
+ * Defined if we have the gettid() system call.
+ */
+#define HAVE_GETTID
+
+/* 
+ * Defined if we have the sched_setscheduler() call
+ */
+#define HAVE_SCHED_SETSCHEDULER
+
+/*
+ * Add any extra platform-specific defines here.
+ */
+#define __linux__
+
+/*
+ * Define if we have <malloc.h> header
+ */
+#define HAVE_MALLOC_H
+
+/* 
+ * Define if we're running on *our* linux on device or emulator.
+ */
+#define HAVE_ANDROID_OS 1
+
+/*
+ * Define if we have Linux-style non-filesystem Unix Domain Sockets
+ */
+#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1
+
+/*
+ * Define if we have Linux's inotify in <sys/inotify.h>.
+ */
+#define HAVE_INOTIFY 1
+
+/*
+ * Define if we have madvise() in <sys/mman.h>
+ */
+#define HAVE_MADVISE 1
+
+/*
+ * Define if tm struct has tm_gmtoff field
+ */
+#define HAVE_TM_GMTOFF 1
+
+/*
+ * Define if dirent struct has d_type field
+ */
+#define HAVE_DIRENT_D_TYPE 1
+
+/*
+ * Define if libc includes Android system properties implementation.
+ */
+#define HAVE_LIBC_SYSTEM_PROPERTIES 1
+
+/*
+ * Define if system provides a system property server (should be
+ * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
+ */
+/* #define HAVE_SYSTEM_PROPERTY_SERVER */
+
+/*
+ * What CPU architecture does this platform use?
+ */
+#define ARCH_ARM
+
+/*
+ * Define if the size of enums is as short as possible,
+ */
+/* #define HAVE_SHORT_ENUMS */
+
+/*
+ * sprintf() format string for shared library naming.
+ */
+#define OS_SHARED_LIB_FORMAT_STR    "lib%s.so"
+
+/*
+ * Do we have __memcmp16()?
+ */
+#define HAVE__MEMCMP16  1
+
+/*
+ * type for the third argument to mincore().
+ */
+#define MINCORE_POINTER_TYPE unsigned char *
+
+/*
+ * Do we have the sigaction flag SA_NOCLDWAIT?
+ */
+#define HAVE_SA_NOCLDWAIT
+
+/*
+ * The default path separator for the platform
+ */
+#define OS_PATH_SEPARATOR '/'
+
+/*
+ * Is the filesystem case sensitive?
+ */
+#define OS_CASE_SENSITIVE
+
+/*
+ * Define if <sys/socket.h> exists.
+ */
+#define HAVE_SYS_SOCKET_H 1
+
+/*
+ * Define if the strlcpy() function exists on the system.
+ */
+#define HAVE_STRLCPY 1
+
+/*
+ * Define if prctl() exists
+ */
+#define HAVE_PRCTL 1
+
+/*
+ * Define if writev() exists
+ */
+#define HAVE_WRITEV 1
+
+#endif /* _ANDROID_CONFIG_H */
diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h
new file mode 100644
index 0000000..6de75f8
--- /dev/null
+++ b/include/arch/linux-x86/AndroidConfig.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android config -- "Linux".  Used for desktop x86 Linux.
+ */
+#ifndef _ANDROID_CONFIG_H
+#define _ANDROID_CONFIG_H
+
+/*
+ * ===========================================================================
+ *                              !!! IMPORTANT !!!
+ * ===========================================================================
+ *
+ * This file is included by ALL C/C++ source files.  Don't put anything in
+ * here unless you are absolutely certain it can't go anywhere else.
+ *
+ * Any C++ stuff must be wrapped with "#ifdef __cplusplus".  Do not use "//"
+ * comments.
+ */
+
+/*
+ * Threading model.  Choose one:
+ *
+ * HAVE_PTHREADS - use the pthreads library.
+ * HAVE_WIN32_THREADS - use Win32 thread primitives.
+ *  -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
+ */
+#define HAVE_PTHREADS
+
+/*
+ * Do we have the futex syscall?
+ */
+
+#define HAVE_FUTEX
+
+/*
+ * Process creation model.  Choose one:
+ *
+ * HAVE_FORKEXEC - use fork() and exec()
+ * HAVE_WIN32_PROC - use CreateProcess()
+ */
+#define HAVE_FORKEXEC
+
+/*
+ * Process out-of-memory adjustment.  Set if running on Linux,
+ * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
+ * badness adjustment.
+ */
+#define HAVE_OOM_ADJ
+
+/*
+ * IPC model.  Choose one:
+ *
+ * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
+ * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
+ * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
+ * HAVE_ANDROID_IPC - use Android versions (?, mmap).
+ */
+#define HAVE_SYSV_IPC
+
+/*
+ * Memory-mapping model. Choose one:
+ *
+ * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
+ * HAVE_WIN32_FILEMAP - use Win32 filemaps
+ */
+#define  HAVE_POSIX_FILEMAP
+
+/*
+ * Define this if you have <termio.h>
+ */
+#define  HAVE_TERMIO_H
+
+/*
+ * Define this if you build against MSVCRT.DLL
+ */
+/* #define HAVE_MS_C_RUNTIME */
+
+/*
+ * Define this if you have sys/uio.h
+ */
+#define  HAVE_SYS_UIO_H
+
+/*
+ * Define this if your platforms implements symbolic links
+ * in its filesystems
+ */
+#define HAVE_SYMLINKS
+
+/*
+ * Define this if we have localtime_r().
+ */
+#define HAVE_LOCALTIME_R
+
+/*
+ * Define this if we have gethostbyname_r().
+ */
+#define HAVE_GETHOSTBYNAME_R
+
+/*
+ * Define this if we have ioctl().
+ */
+#define HAVE_IOCTL
+
+/*
+ * Define this if we want to use WinSock.
+ */
+/* #define HAVE_WINSOCK */
+
+/*
+ * Define this if have clock_gettime() and friends
+ *
+ * Desktop Linux has this in librt, but it's broken in goobuntu, yielding
+ * mildly or wildly inaccurate results.
+ */
+/*#define HAVE_POSIX_CLOCKS*/
+
+/*
+ * Define this if we have pthread_cond_timedwait_monotonic() and
+ * clock_gettime(CLOCK_MONOTONIC).
+ */
+/* #define HAVE_TIMEDWAIT_MONOTONIC */
+
+/*
+ * Define this if we have linux style epoll()
+ */
+#define HAVE_EPOLL
+
+/*
+ * Endianness of the target machine.  Choose one:
+ *
+ * HAVE_ENDIAN_H -- have endian.h header we can include.
+ * HAVE_LITTLE_ENDIAN -- we are little endian.
+ * HAVE_BIG_ENDIAN -- we are big endian.
+ */
+#define HAVE_ENDIAN_H
+#define HAVE_LITTLE_ENDIAN
+
+/*
+ * We need to choose between 32-bit and 64-bit off_t.  All of our code should
+ * agree on the same size.  For desktop systems, use 64-bit values,
+ * because some of our libraries (e.g. wxWidgets) expect to be built that way.
+ */
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE_SOURCE 1
+
+/*
+ * Defined if we have the backtrace() call for retrieving a stack trace.
+ * Needed for CallStack to operate; if not defined, CallStack is
+ * non-functional.
+ */
+#define HAVE_BACKTRACE 1
+
+/*
+ * Defined if we have the dladdr() call for retrieving the symbol associated
+ * with a memory address.  If not defined, stack crawls will not have symbolic
+ * information.
+ */
+#define HAVE_DLADDR 1
+
+/*
+ * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
+ * not defined, stack crawls will be displayed with raw mangled symbols
+ */
+#define HAVE_CXXABI 0
+
+/*
+ * Defined if we have the gettid() system call.
+ */
+/* #define HAVE_GETTID */
+
+/* 
+ * Defined if we have the sched_setscheduler() call
+ */
+#define HAVE_SCHED_SETSCHEDULER
+
+/*
+ * Add any extra platform-specific defines here.
+ */
+
+/*
+ * Define if we have <malloc.h> header
+ */
+#define HAVE_MALLOC_H
+
+/*
+ * Define if we have Linux-style non-filesystem Unix Domain Sockets
+ */
+
+/*
+ * What CPU architecture does this platform use?
+ */
+#define ARCH_X86
+
+
+/*
+ * Define if we have Linux's inotify in <sys/inotify.h>.
+ */
+/*#define HAVE_INOTIFY 1*/
+
+/*
+ * Define if we have madvise() in <sys/mman.h>
+ */
+#define HAVE_MADVISE 1
+
+/*
+ * Define if tm struct has tm_gmtoff field
+ */
+#define HAVE_TM_GMTOFF 1
+
+/*
+ * Define if dirent struct has d_type field
+ */
+#define HAVE_DIRENT_D_TYPE 1
+
+/*
+ * Define if libc includes Android system properties implementation.
+ */
+/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
+
+/*
+ * Define if system provides a system property server (should be
+ * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
+ */
+#define HAVE_SYSTEM_PROPERTY_SERVER
+
+/*
+ * sprintf() format string for shared library naming.
+ */
+#define OS_SHARED_LIB_FORMAT_STR    "lib%s.so"
+
+/*
+ * type for the third argument to mincore().
+ */
+#define MINCORE_POINTER_TYPE unsigned char *
+
+/*
+ * Do we have the sigaction flag SA_NOCLDWAIT?
+ */
+#define HAVE_SA_NOCLDWAIT
+
+/*
+ * The default path separator for the platform
+ */
+#define OS_PATH_SEPARATOR '/'
+
+/*
+ * Is the filesystem case sensitive?
+ */
+#define OS_CASE_SENSITIVE
+
+/*
+ * Define if <sys/socket.h> exists.
+ */
+#define HAVE_SYS_SOCKET_H 1
+
+/*
+ * Define if the strlcpy() function exists on the system.
+ */
+/* #define HAVE_STRLCPY 1 */
+
+/*
+ * Define if prctl() exists
+ */
+#define HAVE_PRCTL 1
+
+/*
+ * Define if writev() exists
+ */
+#define HAVE_WRITEV 1
+
+#endif /*_ANDROID_CONFIG_H*/
diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h
new file mode 100644
index 0000000..4aa44f8
--- /dev/null
+++ b/include/arch/target_linux-x86/AndroidConfig.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Android config -- "target_linux-x86".  Used for x86 linux target devices.
+ */
+#ifndef _ANDROID_CONFIG_H
+#define _ANDROID_CONFIG_H
+
+/*
+ * ===========================================================================
+ *                              !!! IMPORTANT !!!
+ * ===========================================================================
+ *
+ * This file is included by ALL C/C++ source files.  Don't put anything in
+ * here unless you are absolutely certain it can't go anywhere else.
+ *
+ * Any C++ stuff must be wrapped with "#ifdef __cplusplus".  Do not use "//"
+ * comments.
+ */
+
+/*
+ * Threading model.  Choose one:
+ *
+ * HAVE_PTHREADS - use the pthreads library.
+ * HAVE_WIN32_THREADS - use Win32 thread primitives.
+ *  -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
+ */
+#define HAVE_PTHREADS
+
+/*
+ * Do we have the futex syscall?
+ */
+
+#define HAVE_FUTEX
+
+/*
+ * Define if we already have the futex wrapper functions defined. Yes if
+ * compiling against bionic.
+ */
+#define HAVE_FUTEX_WRAPPERS 1
+
+/*
+ * Process creation model.  Choose one:
+ *
+ * HAVE_FORKEXEC - use fork() and exec()
+ * HAVE_WIN32_PROC - use CreateProcess()
+ */
+#define HAVE_FORKEXEC
+
+/*
+ * Process out-of-memory adjustment.  Set if running on Linux,
+ * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
+ * badness adjustment.
+ */
+#define HAVE_OOM_ADJ
+
+/*
+ * IPC model.  Choose one:
+ *
+ * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
+ * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
+ * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
+ * HAVE_ANDROID_IPC - use Android versions (?, mmap).
+ */
+#define HAVE_ANDROID_IPC 1
+
+/*
+ * Memory-mapping model. Choose one:
+ *
+ * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
+ * HAVE_WIN32_FILEMAP - use Win32 filemaps
+ */
+#define  HAVE_POSIX_FILEMAP 1
+
+/*
+ * Define this if you have <termio.h>
+ */
+#define  HAVE_TERMIO_H 1
+
+/*
+ * Define this if you build against have Microsoft C runtime (MSVCRT.DLL)
+ */
+/* #define HAVE_MS_C_RUNTIME */
+
+/*
+ * Define this if you have sys/uio.h
+ */
+#define  HAVE_SYS_UIO_H 1
+
+/*
+ * Define this if your platforms implements symbolic links
+ * in its filesystems
+ */
+#define HAVE_SYMLINKS 1
+
+/*
+ * Define this if we have localtime_r().
+ */
+/* #define HAVE_LOCALTIME_R */
+
+/*
+ * Define this if we have gethostbyname_r().
+ */
+/* #define HAVE_GETHOSTBYNAME_R */
+
+/*
+ * Define this if we have ioctl().
+ */
+#define HAVE_IOCTL
+
+/*
+ * Define this if we want to use WinSock.
+ */
+/* #define HAVE_WINSOCK */
+
+/*
+ * Define this if have clock_gettime() and friends
+ *
+ */
+#define HAVE_POSIX_CLOCKS
+
+/*
+ * Define this if we have pthread_cond_timedwait_monotonic() and
+ * clock_gettime(CLOCK_MONOTONIC).
+ */
+#define HAVE_TIMEDWAIT_MONOTONIC
+
+/*
+ * Define this if we have linux style epoll()
+ */
+#define HAVE_EPOLL
+
+/*
+ * Endianness of the target machine.  Choose one:
+ *
+ * HAVE_ENDIAN_H -- have endian.h header we can include.
+ * HAVE_LITTLE_ENDIAN -- we are little endian.
+ * HAVE_BIG_ENDIAN -- we are big endian.
+ */
+#define HAVE_ENDIAN_H
+#define HAVE_LITTLE_ENDIAN
+
+/*
+ * We need to choose between 32-bit and 64-bit off_t.  All of our code should
+ * agree on the same size.  For desktop systems, use 64-bit values,
+ * because some of our libraries (e.g. wxWidgets) expect to be built that way.
+ */
+/*
+ * #define _FILE_OFFSET_BITS 64
+ * #define _LARGEFILE_SOURCE 1
+ */
+
+/*
+ * Defined if we have the backtrace() call for retrieving a stack trace.
+ * Needed for CallStack to operate; if not defined, CallStack is
+ * non-functional.
+ */
+#define HAVE_BACKTRACE 0
+
+/*
+ * Defined if we have the dladdr() call for retrieving the symbol associated
+ * with a memory address.  If not defined, stack crawls will not have symbolic
+ * information.
+ */
+#define HAVE_DLADDR 0
+
+/*
+ * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
+ * not defined, stack crawls will be displayed with raw mangled symbols
+ */
+#define HAVE_CXXABI 0
+
+/*
+ * Defined if we have the gettid() system call.
+ */
+#define HAVE_GETTID
+
+/* 
+ * Defined if we have the sched_setscheduler() call
+ */
+#define HAVE_SCHED_SETSCHEDULER
+
+/*
+ * Add any extra platform-specific defines here.
+ */
+#ifndef __linux__
+#define __linux__
+#endif
+
+/*
+ * Define if we have <malloc.h> header
+ */
+#define HAVE_MALLOC_H
+
+/* 
+ * Define if we're running on *our* linux on device or emulator.
+ */
+#define HAVE_ANDROID_OS 1
+
+/*
+ * Define if we have Linux-style non-filesystem Unix Domain Sockets
+ */
+#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1
+
+/*
+ * Define if we have Linux's inotify in <sys/inotify.h>.
+ */
+#define HAVE_INOTIFY 1
+
+/*
+ * Define if we have madvise() in <sys/mman.h>
+ */
+#define HAVE_MADVISE 1
+
+/*
+ * Define if we have Linux's dbus 
+ */
+#define HAVE_DBUS 1
+
+/*
+ * Define if tm struct has tm_gmtoff field
+ */
+#define HAVE_TM_GMTOFF 1
+
+/*
+ * Define if dirent struct has d_type field
+ */
+#define HAVE_DIRENT_D_TYPE 1
+
+/*
+ * Define if libc includes Android system properties implementation.
+ */
+#define HAVE_LIBC_SYSTEM_PROPERTIES 1
+
+/*
+ * Define if system provides a system property server (should be
+ * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
+ */
+/* #define HAVE_SYSTEM_PROPERTY_SERVER */
+
+/*
+ * What CPU architecture does this platform use?
+ */
+#define ARCH_X86
+
+/*
+ * sprintf() format string for shared library naming.
+ */
+#define OS_SHARED_LIB_FORMAT_STR    "lib%s.so"
+
+/*
+ * Do we have __memcmp16()?
+ */
+/* #define HAVE__MEMCMP16  1 */
+
+/*
+ * type for the third argument to mincore().
+ */
+#define MINCORE_POINTER_TYPE unsigned char *
+
+/*
+ * Do we have the sigaction flag SA_NOCLDWAIT?
+ */
+#define HAVE_SA_NOCLDWAIT
+
+/*
+ * The default path separator for the platform
+ */
+#define OS_PATH_SEPARATOR '/'
+
+/*
+ * Is the filesystem case sensitive?
+ */
+#define OS_CASE_SENSITIVE
+
+/*
+ * Define if <sys/socket.h> exists.
+ */
+#define HAVE_SYS_SOCKET_H 1
+
+/*
+ * Define if the strlcpy() function exists on the system.
+ */
+#define HAVE_STRLCPY 1
+
+/*
+ * Define if prctl() exists
+ */
+#define HAVE_PRCTL 1
+
+/*
+ * Whether or not _Unwind_Context is defined as a struct.
+ */
+#define HAVE_UNWIND_CONTEXT_STRUCT
+
+#endif /* _ANDROID_CONFIG_H */
diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h
new file mode 100644
index 0000000..c3c6ff1
--- /dev/null
+++ b/include/arch/windows/AndroidConfig.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android config -- "CYGWIN_NT-5.1".  
+ *
+ * Cygwin has pthreads, but GDB seems to get confused if you use it to
+ * create threads.  By "confused", I mean it freezes up the first time the
+ * debugged process creates a thread, even if you use CreateThread.  The
+ * mere presence of pthreads linkage seems to cause problems.
+ */
+#ifndef _ANDROID_CONFIG_H
+#define _ANDROID_CONFIG_H
+
+/*
+ * ===========================================================================
+ *                              !!! IMPORTANT !!!
+ * ===========================================================================
+ *
+ * This file is included by ALL C/C++ source files.  Don't put anything in
+ * here unless you are absolutely certain it can't go anywhere else.
+ *
+ * Any C++ stuff must be wrapped with "#ifdef __cplusplus".  Do not use "//"
+ * comments.
+ */
+
+/*
+ * Threading model.  Choose one:
+ *
+ * HAVE_PTHREADS - use the pthreads library.
+ * HAVE_WIN32_THREADS - use Win32 thread primitives.
+ */
+#define HAVE_WIN32_THREADS
+
+/*
+ * Do we have the futex syscall?
+ */
+
+/* #define HAVE_FUTEX */
+
+
+/*
+ * Process creation model.  Choose one:
+ *
+ * HAVE_FORKEXEC - use fork() and exec()
+ * HAVE_WIN32_PROC - use CreateProcess()
+ */
+#ifdef __CYGWIN__
+#  define HAVE_FORKEXEC
+#else
+#  define HAVE_WIN32_PROC
+#endif
+
+/*
+ * Process out-of-memory adjustment.  Set if running on Linux,
+ * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
+ * badness adjustment.
+ */
+/* #define HAVE_OOM_ADJ */
+
+/*
+ * IPC model.  Choose one:
+ *
+ * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
+ * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
+ * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
+ * HAVE_ANDROID_IPC - use Android versions (?, mmap).
+ */
+#define HAVE_WIN32_IPC
+
+/*
+ * Memory-mapping model. Choose one:
+ *
+ * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
+ * HAVE_WIN32_FILEMAP - use Win32 filemaps
+ */
+#ifdef __CYGWIN__
+#define  HAVE_POSIX_FILEMAP
+#else
+#define  HAVE_WIN32_FILEMAP
+#endif
+
+/*
+ * Define this if you have <termio.h>
+ */
+#ifdef __CYGWIN__
+#  define  HAVE_TERMIO_H
+#endif
+
+/*
+ * Define this if you build against MSVCRT.DLL
+ */
+#ifndef __CYGWIN__
+#  define HAVE_MS_C_RUNTIME
+#endif
+
+/*
+ * Define this if you have sys/uio.h
+ */
+#ifdef __CYGWIN__
+#define  HAVE_SYS_UIO_H
+#endif
+
+
+/*
+ * Define this if we have localtime_r().
+ */
+/* #define HAVE_LOCALTIME_R */
+
+/*
+ * Define this if we have gethostbyname_r().
+ */
+/* #define HAVE_GETHOSTBYNAME_R */
+
+/*
+ * Define this if we have ioctl().
+ */
+/* #define HAVE_IOCTL */
+
+/*
+ * Define this if we want to use WinSock.
+ */
+#ifndef __CYGWIN__
+#define HAVE_WINSOCK
+#endif
+
+/*
+ * Define this if your platforms implements symbolic links
+ * in its filesystems
+ */
+/* #define HAVE_SYMLINKS */
+
+/*
+ * Define this if have clock_gettime() and friends
+ */
+/* #define HAVE_POSIX_CLOCKS */
+
+/*
+ * Endianness of the target machine.  Choose one:
+ *
+ * HAVE_ENDIAN_H -- have endian.h header we can include.
+ * HAVE_LITTLE_ENDIAN -- we are little endian.
+ * HAVE_BIG_ENDIAN -- we are big endian.
+ */
+#ifdef __CYGWIN__
+#define HAVE_ENDIAN_H
+#endif
+
+#define HAVE_LITTLE_ENDIAN
+
+/*
+ * We need to choose between 32-bit and 64-bit off_t.  All of our code should
+ * agree on the same size.  For desktop systems, use 64-bit values,
+ * because some of our libraries (e.g. wxWidgets) expect to be built that way.
+ */
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE_SOURCE 1
+
+/*
+ * Defined if we have the backtrace() call for retrieving a stack trace.
+ * Needed for CallStack to operate; if not defined, CallStack is
+ * non-functional.
+ */
+#define HAVE_BACKTRACE 0
+
+/*
+ * Defined if we have the dladdr() call for retrieving the symbol associated
+ * with a memory address.  If not defined, stack crawls will not have symbolic
+ * information.
+ */
+#define HAVE_DLADDR 0
+
+/*
+ * Defined if we have the cxxabi.h header for demangling C++ symbols.  If
+ * not defined, stack crawls will be displayed with raw mangled symbols
+ */
+#define HAVE_CXXABI 0
+
+/*
+ * Define if tm struct has tm_gmtoff field
+ */
+/* #define HAVE_TM_GMTOFF 1 */
+
+/*
+ * Define if dirent struct has d_type field
+ */
+/* #define HAVE_DIRENT_D_TYPE 1 */
+
+/*
+ * Define if libc includes Android system properties implementation.
+ */
+/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
+
+/*
+ * Define if system provides a system property server (should be
+ * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
+ */
+/* #define HAVE_SYSTEM_PROPERTY_SERVER */
+
+/*
+ * Define if we have madvise() in <sys/mman.h>
+ */
+/*#define HAVE_MADVISE 1*/
+
+/*
+ * Add any extra platform-specific defines here.
+ */
+#define WIN32 1                 /* stock Cygwin doesn't define these */
+#define _WIN32 1
+#define _WIN32_WINNT 0x0500     /* admit to using >= Win2K */
+
+#define HAVE_WINDOWS_PATHS      /* needed by simulator */
+
+/*
+ * What CPU architecture does this platform use?
+ */
+#define ARCH_X86
+
+/*
+ * sprintf() format string for shared library naming.
+ */
+#define OS_SHARED_LIB_FORMAT_STR    "lib%s.dll"
+
+/*
+ * type for the third argument to mincore().
+ */
+#define MINCORE_POINTER_TYPE unsigned char *
+
+/*
+ * The default path separator for the platform
+ */
+#define OS_PATH_SEPARATOR '\\'
+
+/*
+ * Is the filesystem case sensitive?
+ */
+/* #define OS_CASE_SENSITIVE */
+
+/*
+ * Define if <sys/socket.h> exists.
+ * Cygwin has it, but not MinGW.
+ */
+#ifdef USE_MINGW
+/* #define HAVE_SYS_SOCKET_H */
+#else
+#define HAVE_SYS_SOCKET_H 1
+#endif
+
+/*
+ * Define if the strlcpy() function exists on the system.
+ */
+/* #define HAVE_STRLCPY 1 */
+
+/*
+ * Define if <winsock2.h> exists.
+ * Only MinGW has it.
+ */
+#ifdef USE_MINGW
+#define HAVE_WINSOCK2_H 1
+#else
+/* #define HAVE_WINSOCK2_H */
+#endif
+
+/*
+ * Various definitions missing in MinGW
+ */
+#ifdef USE_MINGW
+#define S_IRGRP 0
+#define sleep _sleep
+#endif
+
+/*
+ * Define if writev() exists.
+ */
+/* #define HAVE_WRITEV */
+
+#endif /*_ANDROID_CONFIG_H*/
diff --git a/include/ctest/ctest.h b/include/ctest/ctest.h
new file mode 100644
index 0000000..1a83b20
--- /dev/null
+++ b/include/ctest/ctest.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Very simple unit testing framework. 
+ */
+
+#ifndef __CUTILS_TEST_H
+#define __CUTILS_TEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Adds a test to the test suite.
+ */
+#define addTest(test) addNamedTest(#test, &test)
+   
+/**
+ * Asserts that a condition is true. The test fails if it isn't.
+ */
+#define assertTrue(value, message) assertTrueWithSource(value, __FILE__, __LINE__, message);
+
+/**
+ * Asserts that a condition is false. The test fails if the value is true.
+ */
+#define assertFalse(value, message) assertTrueWithSource(!value, __FILE__, __LINE__, message);
+
+/** Fails a test with the given message. */
+#define fail(message) assertTrueWithSource(0, __FILE__, __LINE__, message);
+
+/**
+ * Asserts that two values are ==.
+ */
+#define assertSame(a, b) assertTrueWithSource(a == b, __FILE__, __LINE__, "Expected same value.");
+    
+/**
+ * Asserts that two values are !=.
+ */
+#define assertNotSame(a, b) assertTrueWithSource(a != b, __FILE__, __LINE__,\
+        "Expected different values");
+    
+/**
+ * Runs a test suite.
+ */
+void runTests(void);
+
+// Do not call these functions directly. Use macros above instead.
+void addNamedTest(const char* name, void (*test)(void));
+void assertTrueWithSource(int value, const char* file, int line, char* message);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_TEST_H */ 
diff --git a/include/cutils/adb_networking.h b/include/cutils/adb_networking.h
new file mode 100755
index 0000000..409d577
--- /dev/null
+++ b/include/cutils/adb_networking.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ADB_NETWORKING_H
+#define _ADB_NETWORKING_H 1
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address);
+extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_ADB_NETWORKING_H*/
+
diff --git a/include/cutils/array.h b/include/cutils/array.h
new file mode 100644
index 0000000..c97ff34
--- /dev/null
+++ b/include/cutils/array.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A pointer array which intelligently expands its capacity ad needed.
+ */
+
+#ifndef __ARRAY_H
+#define __ARRAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/** An array. */
+typedef struct Array Array;
+
+/** Constructs a new array. Returns NULL if we ran out of memory. */
+Array* arrayCreate();
+
+/** Frees an array. Does not free elements themselves. */
+void arrayFree(Array* array);
+
+/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */
+int arrayAdd(Array* array, void* pointer);
+
+/** Gets the pointer at the specified index. */
+void* arrayGet(Array* array, int index);
+
+/** Removes the pointer at the given index and returns it. */
+void* arrayRemove(Array* array, int index);
+
+/** Sets pointer at the given index. Returns old pointer. */
+void* arraySet(Array* array, int index, void* pointer);
+
+/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */
+int arraySetSize(Array* array, int size);
+
+/** Returns the size of the given array. */
+int arraySize(Array* array);
+
+/** 
+ * Returns a pointer to a C-style array which will be valid until this array 
+ * changes.
+ */
+const void** arrayUnwrap(Array* array);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARRAY_H */ 
diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h
new file mode 100644
index 0000000..fd71352
--- /dev/null
+++ b/include/cutils/ashmem.h
@@ -0,0 +1,42 @@
+/* cutils/ashmem.h
+ **
+ ** Copyright 2008 The Android Open Source Project
+ **
+ ** This file is dual licensed.  It may be redistributed and/or modified
+ ** under the terms of the Apache 2.0 License OR version 2 of the GNU
+ ** General Public License.
+ */
+
+#ifndef _CUTILS_ASHMEM_H
+#define _CUTILS_ASHMEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ashmem_create_region(const char *name, size_t size);
+int ashmem_set_prot_region(int fd, int prot);
+int ashmem_pin_region(int fd, size_t offset, size_t len);
+int ashmem_unpin_region(int fd, size_t offset, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef __ASHMEMIOC	/* in case someone included <linux/ashmem.h> too */
+
+#define ASHMEM_NAME_LEN		256
+
+#define ASHMEM_NAME_DEF		"dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED	0
+#define ASHMEM_WAS_PURGED	1
+
+/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
+#define ASHMEM_IS_UNPINNED	0
+#define ASHMEM_IS_PINNED	1
+
+#endif	/* ! __ASHMEMIOC */
+
+#endif	/* _CUTILS_ASHMEM_H */
diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h
new file mode 100644
index 0000000..5694d66
--- /dev/null
+++ b/include/cutils/atomic.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_ATOMIC_H
+#define ANDROID_CUTILS_ATOMIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * NOTE: memory shared between threads is synchronized by all atomic operations
+ * below, this means that no explicit memory barrier is required: all reads or 
+ * writes issued before android_atomic_* operations are guaranteed to complete
+ * before the atomic operation takes place.
+ */
+
+void android_atomic_write(int32_t value, volatile int32_t* addr);
+
+/*
+ * all these atomic operations return the previous value
+ */
+
+
+int32_t android_atomic_inc(volatile int32_t* addr);
+int32_t android_atomic_dec(volatile int32_t* addr);
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr);
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr);
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr);
+
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr);
+
+/*
+ * NOTE: Two "quasiatomic" operations on the exact same memory address
+ * are guaranteed to operate atomically with respect to each other,
+ * but no guarantees are made about quasiatomic operations mixed with
+ * non-quasiatomic operations on the same address, nor about
+ * quasiatomic operations that are performed on partially-overlapping
+ * memory.
+ */
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr);
+int64_t android_quasiatomic_read_64(volatile int64_t* addr);
+    
+/*
+ * cmpxchg return a non zero value if the exchange was NOT performed,
+ * in other words if oldvalue != *addr
+ */
+
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
+        volatile int32_t* addr);
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr);
+
+
+    
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_CUTILS_ATOMIC_H
diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h
new file mode 100644
index 0000000..f3fb370
--- /dev/null
+++ b/include/cutils/config_utils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_CONFIG_UTILS_H
+#define __CUTILS_CONFIG_UTILS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+typedef struct cnode cnode;
+
+
+struct cnode
+{
+    cnode *next;
+    cnode *first_child;
+    cnode *last_child;
+    const char *name;
+    const char *value;
+};
+
+/* parse a text string into a config node tree */
+void config_load(cnode *root, char *data);
+
+/* parse a file into a config node tree */
+void config_load_file(cnode *root, const char *fn);
+
+/* create a single config node */
+cnode* config_node(const char *name, const char *value);
+
+/* locate a named child of a config node */
+cnode* config_find(cnode *root, const char *name);
+
+/* look up a child by name and return the boolean value */
+int config_bool(cnode *root, const char *name, int _default);
+
+/* look up a child by name and return the string value */
+const char* config_str(cnode *root, const char *name, const char *_default);
+
+/* add a named child to a config node (or modify it if it already exists) */
+void config_set(cnode *root, const char *name, const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/cpu_info.h b/include/cutils/cpu_info.h
new file mode 100644
index 0000000..78c1884
--- /dev/null
+++ b/include/cutils/cpu_info.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_CPU_INFO_H
+#define __CUTILS_CPU_INFO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* returns a string contiaining an ASCII representation of the CPU serial number, 
+** or NULL if cpu info not available.
+** The string is a static variable, so don't call free() on it.
+*/
+extern const char* get_cpu_serial_number(void);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_CPU_INFO_H */ 
diff --git a/include/cutils/dir_hash.h b/include/cutils/dir_hash.h
new file mode 100644
index 0000000..fbb4d02
--- /dev/null
+++ b/include/cutils/dir_hash.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+typedef enum {
+    SHA_1,
+} HashAlgorithm;
+
+int get_file_hash(HashAlgorithm algorithm, const char *path,
+                  char *output_string, size_t max_output_string);
+
+int get_recursive_hash_manifest(HashAlgorithm algorithm,
+                                const char *directory_path,
+                                char **output_string);
diff --git a/include/cutils/event_tag_map.h b/include/cutils/event_tag_map.h
new file mode 100644
index 0000000..1653c61
--- /dev/null
+++ b/include/cutils/event_tag_map.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
+#define _LIBS_CUTILS_EVENTTAGMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EVENT_TAG_MAP_FILE  "/system/etc/event-log-tags"
+
+struct EventTagMap;
+typedef struct EventTagMap EventTagMap;
+
+/*
+ * Open the specified file as an event log tag map.
+ *
+ * Returns NULL on failure.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName);
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map);
+
+/*
+ * Look up a tag by index.  Returns the tag string, or NULL if not found.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/include/cutils/fdevent.h b/include/cutils/fdevent.h
new file mode 100644
index 0000000..7a442d4
--- /dev/null
+++ b/include/cutils/fdevent.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+/* events that may be observed */
+#define FDE_READ              0x0001
+#define FDE_WRITE             0x0002
+#define FDE_ERROR             0x0004
+
+/* features that may be set (via the events set/add/del interface) */
+#define FDE_DONT_CLOSE        0x0080
+
+typedef struct fdevent fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+
+/* Allocate and initialize a new fdevent object
+*/
+fdevent *fdevent_create(int fd, fd_func func, void *arg);
+
+/* Uninitialize and deallocate an fdevent object that was
+** created by fdevent_create()
+*/
+void fdevent_destroy(fdevent *fde);
+
+/* Initialize an fdevent object that was externally allocated
+*/
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
+
+/* Uninitialize an fdevent object that was initialized by
+** fdevent_install()
+*/
+void fdevent_remove(fdevent *item);
+
+/* Change which events should cause notifications
+*/
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+
+/* loop forever, handling events.
+*/
+void fdevent_loop();
+
+struct fdevent 
+{
+    fdevent *next;
+    fdevent *prev;
+
+    int fd;
+    unsigned short state;
+    unsigned short events;
+    
+    fd_func func;
+    void *arg;
+};
+
+
+#endif
diff --git a/include/cutils/hashmap.h b/include/cutils/hashmap.h
new file mode 100644
index 0000000..5cb344c
--- /dev/null
+++ b/include/cutils/hashmap.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Hash map.
+ */
+
+#ifndef __HASHMAP_H
+#define __HASHMAP_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A hash map. */
+typedef struct Hashmap Hashmap;
+
+/**
+ * Creates a new hash map. Returns NULL if memory allocation fails.
+ *
+ * @param initialCapacity number of expected entries
+ * @param hash function which hashes keys
+ * @param equals function which compares keys for equality
+ */
+Hashmap* hashmapCreate(size_t initialCapacity,
+        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));
+
+/**
+ * Frees the hash map. Does not free the keys or values themselves.
+ */
+void hashmapFree(Hashmap* map);
+
+/**
+ * Hashes the memory pointed to by key with the given size. Useful for
+ * implementing hash functions.
+ */
+int hashmapHash(void* key, size_t keySize);
+
+/**
+ * Puts value for the given key in the map. Returns pre-existing value if
+ * any.
+ *
+ * If memory allocation fails, this function returns NULL, the map's size
+ * does not increase, and errno is set to ENOMEM.
+ */
+void* hashmapPut(Hashmap* map, void* key, void* value);
+
+/**
+ * Gets a value from the map. Returns NULL if no entry for the given key is
+ * found or if the value itself is NULL.
+ */
+void* hashmapGet(Hashmap* map, void* key);
+
+/**
+ * Returns true if the map contains an entry for the given key.
+ */
+bool hashmapContainsKey(Hashmap* map, void* key);
+
+/**
+ * Gets the value for a key. If a value is not found, this function gets a 
+ * value and creates an entry using the given callback.
+ *
+ * If memory allocation fails, the callback is not called, this function
+ * returns NULL, and errno is set to ENOMEM.
+ */
+void* hashmapMemoize(Hashmap* map, void* key, 
+        void* (*initialValue)(void* key, void* context), void* context);
+
+/**
+ * Removes an entry from the map. Returns the removed value or NULL if no
+ * entry was present.
+ */
+void* hashmapRemove(Hashmap* map, void* key);
+
+/**
+ * Gets the number of entries in this map.
+ */
+size_t hashmapSize(Hashmap* map);
+
+/**
+ * Invokes the given callback on each entry in the map. Stops iterating if
+ * the callback returns false.
+ */
+void hashmapForEach(Hashmap* map, 
+        bool (*callback)(void* key, void* value, void* context),
+        void* context);
+
+/**
+ * Concurrency support.
+ */
+
+/**
+ * Locks the hash map so only the current thread can access it.
+ */
+void hashmapLock(Hashmap* map);
+
+/**
+ * Unlocks the hash map so other threads can access it.
+ */
+void hashmapUnlock(Hashmap* map);
+
+/**
+ * Key utilities.
+ */
+
+/**
+ * Hashes int keys. 'key' is a pointer to int.
+ */
+int hashmapIntHash(void* key);
+
+/**
+ * Compares two int keys for equality.
+ */
+bool hashmapIntEquals(void* keyA, void* keyB);
+
+/**
+ * For debugging.
+ */
+
+/**
+ * Gets current capacity.
+ */
+size_t hashmapCurrentCapacity(Hashmap* map);
+
+/**
+ * Counts the number of entry collisions.
+ */
+size_t hashmapCountCollisions(Hashmap* map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HASHMAP_H */ 
diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h
new file mode 100644
index 0000000..ee0018f
--- /dev/null
+++ b/include/cutils/jstring.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_STRING16_H
+#define __CUTILS_STRING16_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint16_t char16_t;
+
+extern char * strndup16to8 (const char16_t* s, size_t n);
+extern size_t strnlen16to8 (const char16_t* s, size_t n);
+extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len);
+extern size_t strlen8to16 (const char* utf8Str);
+extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
+extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
+    size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_STRING16_H */
diff --git a/include/cutils/log.h b/include/cutils/log.h
new file mode 100644
index 0000000..ec3cac8
--- /dev/null
+++ b/include/cutils/log.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// C/C++ logging functions.  See the logging documentation for API details.
+//
+// We'd like these to be available from C code (in case we import some from
+// somewhere), so this has a C interface.
+//
+// The output will be correct when the log file is shared between multiple
+// threads and/or multiple processes so long as the operating system
+// supports O_APPEND.  These calls have mutex-protected data structures
+// and so are NOT reentrant.  Do not use LOG in a signal handler.
+//
+#ifndef _LIBS_CUTILS_LOG_H
+#define _LIBS_CUTILS_LOG_H
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <stdarg.h>
+
+#include <cutils/uio.h>
+#include <cutils/logd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Normally we strip LOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros.  You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef LOG_TAG
+#define LOG_TAG NULL
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Simplified macro to send a verbose log message using the current LOG_TAG.
+ */
+#ifndef LOGV
+#if LOG_NDEBUG
+#define LOGV(...)   ((void)0)
+#else
+#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#define CONDITION(cond)     (__builtin_expect((cond)!=0, 0))
+
+#ifndef LOGV_IF
+#if LOG_NDEBUG
+#define LOGV_IF(cond, ...)   ((void)0)
+#else
+#define LOGV_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current LOG_TAG.
+ */
+#ifndef LOGD
+#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGD_IF
+#define LOGD_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current LOG_TAG.
+ */
+#ifndef LOGI
+#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGI_IF
+#define LOGI_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current LOG_TAG.
+ */
+#ifndef LOGW
+#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGW_IF
+#define LOGW_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current LOG_TAG.
+ */
+#ifndef LOGE
+#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef LOGE_IF
+#define LOGE_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * verbose priority.
+ */
+#ifndef IF_LOGV
+#if LOG_NDEBUG
+#define IF_LOGV() if (false)
+#else
+#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
+#endif
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * debug priority.
+ */
+#ifndef IF_LOGD
+#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * info priority.
+ */
+#ifndef IF_LOGI
+#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * warn priority.
+ */
+#ifndef IF_LOGW
+#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
+#endif
+
+/*
+ * Conditional based on whether the current LOG_TAG is enabled at
+ * error priority.
+ */
+#ifndef IF_LOGE
+#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Log a fatal error.  If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds.  Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#define LOG_ALWAYS_FATAL_IF(cond, ...) \
+    ( (CONDITION(cond)) \
+    ? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
+    : (void)0 )
+
+#define LOG_ALWAYS_FATAL(...) \
+    ( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
+
+/*
+ * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if LOG_NDEBUG
+
+#define LOG_FATAL_IF(cond, ...) ((void)0)
+#define LOG_FATAL(...) ((void)0)
+
+#else
+
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
+#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
+
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds.  Uses the current LOG_TAG.
+ */
+#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__)
+//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
+
+// ---------------------------------------------------------------------
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ *  LOG(LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef LOG
+#define LOG(priority, tag, ...) \
+    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef LOG_PRI
+#define LOG_PRI(priority, tag, ...) \
+    android_printLog(priority, tag, __VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef LOG_PRI_VA
+#define LOG_PRI_VA(priority, tag, fmt, args) \
+    android_vprintLog(priority, NULL, tag, fmt, args)
+#endif
+
+/*
+ * Conditional given a desired logging priority and tag.
+ */
+#ifndef IF_LOG
+#define IF_LOG(priority, tag) \
+    if (android_testLog(ANDROID_##priority, tag))
+#endif
+
+// ---------------------------------------------------------------------
+
+/*
+ * Event logging.
+ */
+
+/*
+ * Event log entry types.  These must match up with the declarations in
+ * java/android/android/util/EventLog.java.
+ */
+typedef enum {
+    EVENT_TYPE_INT      = 0,
+    EVENT_TYPE_LONG     = 1,
+    EVENT_TYPE_STRING   = 2,
+    EVENT_TYPE_LIST     = 3,
+} AndroidEventLogType;
+
+
+#define LOG_EVENT_INT(_tag, _value) {                                       \
+        int intBuf = _value;                                                \
+        (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf,            \
+            sizeof(intBuf));                                                \
+    }
+#define LOG_EVENT_LONG(_tag, _value) {                                      \
+        long long longBuf = _value;                                         \
+        (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf,          \
+            sizeof(longBuf));                                               \
+    }
+#define LOG_EVENT_STRING(_tag, _value)                                      \
+    ((void) 0)  /* not implemented -- must combine len with string */
+/* TODO: something for LIST */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+#define android_printLog(prio, tag, fmt...) \
+    __android_log_print(prio, tag, fmt)
+
+#define android_vprintLog(prio, cond, tag, fmt...) \
+    __android_log_vprint(prio, tag, fmt)
+
+#define android_printAssert(cond, tag, fmt...) \
+    __android_log_assert(cond, tag, fmt)
+
+#define android_writeLog(prio, tag, text) \
+    __android_log_write(prio, tag, text)
+
+#define android_bWriteLog(tag, payload, len) \
+    __android_log_bwrite(tag, payload, len)
+#define android_btWriteLog(tag, type, payload, len) \
+    __android_log_btwrite(tag, type, payload, len)
+	
+// TODO: remove these prototypes and their users
+#define android_testLog(prio, tag) (1)
+#define android_writevLog(vec,num) do{}while(0)
+#define android_write1Log(str,len) do{}while (0)
+#define android_setMinPriority(tag, prio) do{}while(0)
+//#define android_logToCallback(func) do{}while(0)
+#define android_logToFile(tag, file) (0)
+#define android_logToFd(tag, fd) (0)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBS_CUTILS_LOG_H
diff --git a/include/cutils/logd.h b/include/cutils/logd.h
new file mode 100644
index 0000000..a1cb012
--- /dev/null
+++ b/include/cutils/logd.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_CUTILS_LOGD_H
+#define _ANDROID_CUTILS_LOGD_H
+
+#include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <cutils/uio.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Priority values, in ascending priority order.
+ */
+typedef enum android_LogPriority {
+    ANDROID_LOG_UNKNOWN = 0,
+    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
+    ANDROID_LOG_VERBOSE,
+    ANDROID_LOG_DEBUG,
+    ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,
+    ANDROID_LOG_ERROR,
+    ANDROID_LOG_FATAL,
+    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+int __android_log_write(int prio, const char *tag, const char *text);
+
+int __android_log_vprint(int prio, const char *tag,
+			 const char *fmt, va_list ap);
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+    size_t len);
+
+int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+
+void __android_log_assert(const char *cond, const char *tag,
+			  const char *fmt, ...)    
+#if defined(__GNUC__)
+    __attribute__ ((noreturn))
+    __attribute__ ((format(printf, 3, 4)))
+#endif
+    ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LOGD_H */
diff --git a/include/cutils/logger.h b/include/cutils/logger.h
new file mode 100644
index 0000000..3a08019
--- /dev/null
+++ b/include/cutils/logger.h
@@ -0,0 +1,46 @@
+/* utils/logger.h
+** 
+** Copyright 2007, The Android Open Source Project
+**
+** This file is dual licensed.  It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _UTILS_LOGGER_H
+#define _UTILS_LOGGER_H
+
+#include <stdint.h>
+
+struct logger_entry {
+    uint16_t    len;    /* length of the payload */
+    uint16_t    __pad;  /* no matter what, we get 2 bytes of padding */
+    int32_t     pid;    /* generating process's pid */
+    int32_t     tid;    /* generating process's tid */
+    int32_t     sec;    /* seconds since Epoch */
+    int32_t     nsec;   /* nanoseconds */
+    char        msg[0]; /* the entry's payload */
+};
+
+#define LOGGER_LOG_MAIN		"log/main"
+#define LOGGER_LOG_RADIO	"log/radio"
+#define LOGGER_LOG_EVENTS	"log/events"
+
+#define LOGGER_ENTRY_MAX_LEN		(4*1024)
+#define LOGGER_ENTRY_MAX_PAYLOAD	\
+	(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
+
+#ifdef HAVE_IOCTL
+
+#include <sys/ioctl.h>
+
+#define __LOGGERIO	0xAE
+
+#define LOGGER_GET_LOG_BUF_SIZE		_IO(__LOGGERIO, 1) /* size of log */
+#define LOGGER_GET_LOG_LEN		_IO(__LOGGERIO, 2) /* used log len */
+#define LOGGER_GET_NEXT_ENTRY_LEN	_IO(__LOGGERIO, 3) /* next entry len */
+#define LOGGER_FLUSH_LOG		_IO(__LOGGERIO, 4) /* flush log */
+
+#endif // HAVE_IOCTL
+
+#endif /* _UTILS_LOGGER_H */
diff --git a/include/cutils/logprint.h b/include/cutils/logprint.h
new file mode 100644
index 0000000..d6ec480
--- /dev/null
+++ b/include/cutils/logprint.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPRINT_H
+#define _LOGPRINT_H
+
+#include <cutils/log.h>
+#include <cutils/logger.h>
+#include <cutils/event_tag_map.h>
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    FORMAT_OFF = 0,
+    FORMAT_BRIEF,
+    FORMAT_PROCESS,
+    FORMAT_TAG,
+    FORMAT_THREAD,
+    FORMAT_RAW,
+    FORMAT_TIME,
+    FORMAT_THREADTIME,
+    FORMAT_LONG,
+} AndroidLogPrintFormat;
+
+typedef struct AndroidLogFormat_t AndroidLogFormat;
+
+typedef struct AndroidLogEntry_t {
+    time_t tv_sec;
+    long tv_nsec;
+    android_LogPriority priority;
+    pid_t pid;
+    pthread_t tid;
+    const char * tag;
+    size_t messageLen;
+    const char * message;
+} AndroidLogEntry;
+
+AndroidLogFormat *android_log_format_new();
+
+void android_log_format_free(AndroidLogFormat *p_format);
+
+void android_log_setPrintFormat(AndroidLogFormat *p_format, 
+        AndroidLogPrintFormat format);
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char *s);
+
+/** 
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format, 
+        const char *filterExpression);
+
+
+/** 
+ * filterString: a whitespace-separated set of filter expressions 
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+        const char *filterString);
+
+
+/** 
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+        AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
+
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+                                 AndroidLogEntry *entry);
+
+/**
+ * Like android_log_processLogBuffer, but for binary logs.
+ *
+ * If "map" is non-NULL, it will be used to convert the log tag number
+ * into a string.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+    AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+    int messageBufLen);
+
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (    
+    AndroidLogFormat *p_format,
+    char *defaultBuffer,
+    size_t defaultBufferSize,
+    const AndroidLogEntry *p_line,
+    size_t *p_outLength);
+
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Assumes single threaded execution
+ *
+ */
+int android_log_filterAndPrintLogLine(
+    AndroidLogFormat *p_format,
+    int fd,
+    const AndroidLogEntry *entry);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_LOGPRINT_H*/
diff --git a/include/cutils/memory.h b/include/cutils/memory.h
new file mode 100644
index 0000000..e725cdd
--- /dev/null
+++ b/include/cutils/memory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CUTILS_MEMORY_H
+#define ANDROID_CUTILS_MEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* size is given in bytes and must be multiple of 2 */
+void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+
+/* size is given in bytes and must be multiple of 4 */
+void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+
+#if !HAVE_STRLCPY
+/* Declaration of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_CUTILS_MEMORY_H
diff --git a/include/cutils/misc.h b/include/cutils/misc.h
new file mode 100644
index 0000000..2c48dfa
--- /dev/null
+++ b/include/cutils/misc.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_MISC_H
+#define __CUTILS_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+        /* Load an entire file into a malloc'd chunk of memory
+         * that is length_of_file + 1 (null terminator).  If
+         * sz is non-zero, return the size of the file via sz.
+         * Returns 0 on failure.
+         */
+extern void *load_file(const char *fn, unsigned *sz);
+
+        /* Connects your process to the system debugger daemon
+         * so that on a crash it may be logged or interactively
+         * debugged (depending on system settings).
+         */
+extern void debuggerd_connect(void);
+
+
+        /* This is the range of UIDs (and GIDs) that are reserved
+         * for assigning to applications.
+         */
+#define FIRST_APPLICATION_UID 10000
+#define LAST_APPLICATION_UID 99999
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_MISC_H */ 
diff --git a/include/cutils/mq.h b/include/cutils/mq.h
new file mode 100644
index 0000000..b27456d
--- /dev/null
+++ b/include/cutils/mq.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * IPC messaging library.
+ */
+
+#ifndef __MQ_H
+#define __MQ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** A message. */
+typedef struct MqMessage MqMessage;
+
+/** A destination to which messages can be sent. */
+typedef struct MqDestination MqDestination;
+
+/* Array of bytes. */
+typedef struct MqBytes MqBytes;
+
+/** 
+ * Hears messages. 
+ * 
+ * @param destination to which the message was sent
+ * @param message the message to hear
+ */
+typedef void MqMessageListener(MqDestination* destination, MqMessage* message);
+
+/** 
+ * Hears a destination close.
+ * 
+ * @param destination that closed
+ */
+typedef void MqCloseListener(MqDestination* destination);
+
+/** Message functions. */
+
+/** 
+ * Creates a new Message.
+ * 
+ * @param header as defined by user
+ * @param body as defined by user
+ * @param replyTo destination to which replies should be sent, NULL if none
+ */
+MqMessage* mqCreateMessage(MqBytes header, MqBytes body, 
+        MqDestination* replyTo);
+
+/** Sends a message to a destination. */
+void mqSendMessage(MqMessage* message, MqDestination* destination);
+
+/** Destination functions. */
+
+/** 
+ * Creates a new destination. Acquires a reference implicitly.
+ *
+ * @param messageListener function to call when a message is recieved
+ * @param closeListener function to call when the destination closes
+ * @param userData user-specific data to associate with the destination.
+ *  Retrieve using mqGetDestinationUserData().
+ */
+MqDestination* mqCreateDestination(MqMessageListener* messageListener, 
+        MqCloseListener* closeListener, void* userData);
+
+/**
+ * Gets user data which was associated with the given destination at 
+ * construction time. 
+ * 
+ * It is only valid to call this function in the same process that the 
+ * given destination was created in.
+ * This function returns a null pointer if you call it on a destination
+ * created in a remote process.
+ */
+void* mqGetUserData(MqDestination* destination);
+
+/**
+ * Returns 1 if the destination was created in this process, or 0 if
+ * the destination was created in a different process, in which case you have
+ * a remote stub.
+ */
+int mqIsDestinationLocal(MqDestination* destination);
+
+/**
+ * Increments the destination's reference count.
+ */
+void mqKeepDestination(MqDesintation* destination);
+
+/**
+ * Decrements the destination's reference count. 
+ */
+void mqFreeDestination(MqDestination* desintation);
+
+/** Registry API. */
+
+/**
+ * Gets the destination bound to a name.
+ */
+MqDestination* mqGetDestination(char* name);
+
+/**
+ * Binds a destination to a name.
+ */
+void mqPutDestination(char* name, MqDestination* desintation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MQ_H */ 
diff --git a/include/cutils/mspace.h b/include/cutils/mspace.h
new file mode 100644
index 0000000..33410c1
--- /dev/null
+++ b/include/cutils/mspace.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* A wrapper file for dlmalloc.h that defines prototypes for the
+ * mspace_*() functions, which provide an interface for creating
+ * multiple heaps.
+ */
+
+#ifndef MSPACE_H_
+#define MSPACE_H_
+
+/* It's a pain getting the mallinfo stuff to work
+ * with Linux, OSX, and klibc, so just turn it off
+ * for now.
+ * TODO: make mallinfo work
+ */
+#define NO_MALLINFO 1
+
+/* Allow setting the maximum heap footprint.
+ */
+#define USE_MAX_ALLOWED_FOOTPRINT 1
+
+#define USE_CONTIGUOUS_MSPACES 1
+#if USE_CONTIGUOUS_MSPACES
+#define HAVE_MMAP 0
+#define HAVE_MORECORE 1
+#define MORECORE_CONTIGUOUS 0
+#endif
+
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#include "../../../../bionic/libc/bionic/dlmalloc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+  mspace_usable_size(void* p);
+
+  Returns the number of bytes you can actually use in
+  an allocated chunk, which may be more than you requested (although
+  often not) due to alignment and minimum size constraints.
+  You can use this many bytes without worrying about
+  overwriting other allocated objects. This is not a particularly great
+  programming practice. mspace_usable_size can be more useful in
+  debugging and assertions, for example:
+
+  p = mspace_malloc(msp, n);
+  assert(mspace_usable_size(msp, p) >= 256);
+*/
+size_t mspace_usable_size(mspace, const void*);
+
+#if USE_CONTIGUOUS_MSPACES
+/*
+  Similar to create_mspace(), but the underlying memory is
+  guaranteed to be contiguous.  No more than max_capacity
+  bytes is ever allocated to the mspace.
+ */
+mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity,
+    int locked);
+
+/*
+   Identical to create_contiguous_mspace, but labels the mapping 'mspace/name'
+   instead of 'mspace'
+*/
+mspace create_contiguous_mspace_with_name(size_t starting_capacity,
+    size_t max_capacity, int locked, const char *name);
+
+size_t destroy_contiguous_mspace(mspace msp);
+#endif
+
+/*
+  Call the handler for each block in the specified mspace.
+  chunkptr and chunklen refer to the heap-level chunk including
+  the chunk overhead, and userptr and userlen refer to the
+  user-usable part of the chunk.  If the chunk is free, userptr
+  will be NULL and userlen will be 0.  userlen is not guaranteed
+  to be the same value passed into malloc() for a given chunk;
+  it is >= the requested size.
+ */
+void mspace_walk_heap(mspace msp,
+    void(*handler)(const void *chunkptr, size_t chunklen,
+        const void *userptr, size_t userlen, void *arg), void *harg);
+
+/*
+  mspace_walk_free_pages(handler, harg)
+
+  Calls the provided handler on each free region in the specified
+  mspace.  The memory between start and end are guaranteed not to
+  contain any important data, so the handler is free to alter the
+  contents in any way.  This can be used to advise the OS that large
+  free regions may be swapped out.
+
+  The value in harg will be passed to each call of the handler.
+ */
+void mspace_walk_free_pages(mspace msp,
+    void(*handler)(void *start, void *end, void *arg), void *harg);
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif /* MSPACE_H_ */
diff --git a/include/cutils/native_handle.h b/include/cutils/native_handle.h
new file mode 100644
index 0000000..2b64893
--- /dev/null
+++ b/include/cutils/native_handle.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVE_HANDLE_H_
+#define NATIVE_HANDLE_H_
+
+typedef struct
+{
+    int version;        /* sizeof(native_handle) */
+    int numFds;         /* number of file-descriptors at &data[0] */
+    int numInts;        /* number of ints at &data[numFds] */
+    int data[0];        /* numFds + numInts ints */
+} native_handle;
+
+#endif /* NATIVE_HANDLE_H_ */
diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h
new file mode 100644
index 0000000..1e72e5c
--- /dev/null
+++ b/include/cutils/process_name.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Gives the current process a name.
+ */
+
+#ifndef __PROCESS_NAME_H
+#define __PROCESS_NAME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Sets the current process name.
+ *
+ * Warning: This leaks a string every time you call it. Use judiciously!
+ */
+void set_process_name(const char* process_name);
+
+/** Gets the current process name. */
+const char* get_process_name(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PROCESS_NAME_H */ 
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
new file mode 100644
index 0000000..25fd67a
--- /dev/null
+++ b/include/cutils/properties.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_PROPERTIES_H
+#define __CUTILS_PROPERTIES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* System properties are *small* name value pairs managed by the
+** property service.  If your data doesn't fit in the provided
+** space it is not appropriate for a system property.
+**
+** WARNING: system/bionic/include/sys/system_properties.h also defines
+**          these, but with different names.  (TODO: fix that)
+*/
+#define PROPERTY_KEY_MAX   32
+#define PROPERTY_VALUE_MAX  92
+
+/* property_get: returns the length of the value which will never be
+** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
+** (the length does not include the terminating zero).
+**
+** If the property read fails or returns an empty value, the default
+** value is used (if nonnull).
+*/
+int property_get(const char *key, char *value, const char *default_value);
+
+/* property_set: returns 0 on success, < 0 on failure
+*/
+int property_set(const char *key, const char *value);
+    
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);    
+
+
+#ifdef HAVE_SYSTEM_PROPERTY_SERVER
+/*
+ * We have an external property server instead of built-in libc support.
+ * Used by the simulator.
+ */
+#define SYSTEM_PROPERTY_PIPE_NAME       "/tmp/android-sysprop"
+
+enum {
+    kSystemPropertyUnknown = 0,
+    kSystemPropertyGet,
+    kSystemPropertySet,
+    kSystemPropertyList
+};
+#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h
new file mode 100644
index 0000000..bfac87a
--- /dev/null
+++ b/include/cutils/record_stream.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * A simple utility for reading fixed records out of a stream fd
+ */
+
+#ifndef _CUTILS_RECORD_STREAM_H
+#define _CUTILS_RECORD_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct RecordStream RecordStream;
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen);
+extern void record_stream_free(RecordStream *p_rs);
+
+extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, 
+                                    size_t *p_outRecordLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /*_CUTILS_RECORD_STREAM_H*/
+
diff --git a/include/cutils/selector.h b/include/cutils/selector.h
new file mode 100644
index 0000000..dfc2a9d
--- /dev/null
+++ b/include/cutils/selector.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Framework for multiplexing I/O. A selector manages a set of file
+ * descriptors and calls out to user-provided callback functions to read and
+ * write data and handle errors.
+ */
+
+#ifndef __SELECTOR_H
+#define __SELECTOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+    
+/** 
+ * Manages SelectableFds and invokes their callbacks at appropriate times. 
+ */
+typedef struct Selector Selector;
+
+/** 
+ * A selectable descriptor. Contains callbacks which the selector can invoke
+ * before calling select(), when the descriptor is readable or writable, and 
+ * when the descriptor contains out-of-band data. Simply set a callback to 
+ * NULL if you're not interested in that particular event.
+ *
+ * A selectable descriptor can indicate that it needs to be removed from the
+ * selector by setting the 'remove' flag. The selector will remove the
+ * descriptor at a later time and invoke the onRemove() callback.
+ * 
+ * SelectableFd fields should only be modified from the selector loop.
+ */
+typedef struct SelectableFd SelectableFd;
+struct SelectableFd {
+
+    /** The file descriptor itself. */
+    int fd;
+    
+    /** Pointer to user-specific data. Can be NULL. */
+    void* data;
+    
+    /** 
+     * Set this flag when you no longer wish to be selected. The selector
+     * will invoke onRemove() when the descriptor is actually removed.
+     */
+    bool remove;
+
+    /** 
+     * Invoked by the selector before calling select. You can set up other
+     * callbacks from here as necessary.
+     */
+    void (*beforeSelect)(SelectableFd* self);
+
+    /** 
+     * Invoked by the selector when the descriptor has data available. Set to
+     * NULL to indicate that you're not interested in reading.
+     */
+    void (*onReadable)(SelectableFd* self);
+
+    /** 
+     * Invoked by the selector when the descriptor can accept data. Set to
+     * NULL to indicate that you're not interested in writing.
+     */
+    void (*onWritable)(SelectableFd* self);
+
+    /** 
+     * Invoked by the selector when out-of-band (OOB) data is available. Set to
+     * NULL to indicate that you're not interested in OOB data.
+     */
+    void (*onExcept)(SelectableFd* self);
+
+    /**
+     * Invoked by the selector after the descriptor is removed from the
+     * selector but before the selector frees the SelectableFd memory.
+     */
+    void (*onRemove)(SelectableFd* self);
+
+    /**
+     * The selector which selected this fd. Set by the selector itself.
+     */
+    Selector* selector;
+};
+
+/** 
+ * Creates a new selector. 
+ */
+Selector* selectorCreate(void);
+
+/** 
+ * Creates a new selectable fd, adds it to the given selector and returns a 
+ * pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL 
+ * by default.
+ * 
+ * The selectable fd should only be modified from the selector loop thread.
+ */
+SelectableFd* selectorAdd(Selector* selector, int fd);
+
+/**
+ * Wakes up the selector even though no I/O events occurred. Use this
+ * to indicate that you're ready to write to a descriptor.
+ */
+void selectorWakeUp(Selector* selector);
+    
+/** 
+ * Loops continuously selecting file descriptors and firing events. 
+ * Does not return. 
+ */
+void selectorLoop(Selector* selector);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SELECTOR_H */ 
diff --git a/include/cutils/sockets.h b/include/cutils/sockets.h
new file mode 100644
index 0000000..aa8682e
--- /dev/null
+++ b/include/cutils/sockets.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_SOCKETS_H
+#define __CUTILS_SOCKETS_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+typedef int  socklen_t;
+#elif HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#define ANDROID_SOCKET_ENV_PREFIX	"ANDROID_SOCKET_"
+#define ANDROID_SOCKET_DIR		"/dev/socket"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_socket - simple helper function to get the file
+ * descriptor of our init-managed Unix domain socket. `name' is the name of the
+ * socket, as given in init.rc. Returns -1 on error.
+ *
+ * This is inline and not in libcutils proper because we want to use this in
+ * third-party daemons with minimal modification.
+ */
+static inline int android_get_control_socket(const char *name)
+{
+	char key[64] = ANDROID_SOCKET_ENV_PREFIX;
+	const char *val;
+	int fd;
+
+	/* build our environment variable, counting cycles like a wolf ... */
+#if HAVE_STRLCPY
+	strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+		name,
+		sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+#else	/* for the host, which may lack the almightly strncpy ... */
+	strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+		name,
+		sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+	key[sizeof(key)-1] = '\0';
+#endif
+
+	val = getenv(key);
+	if (!val)
+		return -1;
+
+	errno = 0;
+	fd = strtol(val, NULL, 10);
+	if (errno)
+		return -1;
+
+	return fd;
+}
+
+/*
+ * See also android.os.LocalSocketAddress.Namespace
+ */
+// Linux "abstract" (non-filesystem) namespace
+#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
+// Android "reserved" (/dev/socket) namespace
+#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
+// Normal filesystem namespace
+#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
+
+extern int socket_loopback_client(int port, int type);
+extern int socket_network_client(const char *host, int port, int type);
+extern int socket_loopback_server(int port, int type);
+extern int socket_local_server(const char *name, int namespaceId, int type);
+extern int socket_local_server_bind(int s, const char *name, int namespaceId);
+extern int socket_local_client_connect(int fd, 
+        const char *name, int namespaceId, int type);
+extern int socket_local_client(const char *name, int namespaceId, int type);
+extern int socket_inaddr_any_server(int port, int type);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_SOCKETS_H */ 
diff --git a/include/cutils/threads.h b/include/cutils/threads.h
new file mode 100644
index 0000000..acf8f48
--- /dev/null
+++ b/include/cutils/threads.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_THREADS_H
+#define _LIBS_CUTILS_THREADS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************************************/
+/***********************************************************************/
+/*****                                                             *****/
+/*****         local thread storage                                *****/
+/*****                                                             *****/
+/***********************************************************************/
+/***********************************************************************/
+
+#ifdef HAVE_PTHREADS
+
+#include  <pthread.h>
+
+typedef struct {
+    pthread_mutex_t   lock;
+    int               has_tls;
+    pthread_key_t     tls;
+
+} thread_store_t;
+
+#define  THREAD_STORE_INITIALIZER  { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
+
+#elif defined HAVE_WIN32_THREADS
+
+#include <windows.h>
+
+typedef struct {
+    int               lock_init;
+    int               has_tls;
+    DWORD             tls;
+    CRITICAL_SECTION  lock;
+
+} thread_store_t;
+
+#define  THREAD_STORE_INITIALIZER  { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
+
+#else
+#  error  "no thread_store_t implementation for your platform !!"
+#endif
+
+typedef void  (*thread_store_destruct_t)(void*  value);
+
+extern void*  thread_store_get(thread_store_t*  store);
+
+extern void   thread_store_set(thread_store_t*          store, 
+                               void*                    value,
+                               thread_store_destruct_t  destroy);
+
+/***********************************************************************/
+/***********************************************************************/
+/*****                                                             *****/
+/*****         mutexes                                             *****/
+/*****                                                             *****/
+/***********************************************************************/
+/***********************************************************************/
+
+#ifdef HAVE_PTHREADS
+
+typedef pthread_mutex_t   mutex_t;
+
+#define  MUTEX_INITIALIZER  PTHREAD_MUTEX_INITIALIZER
+
+static __inline__ void  mutex_lock(mutex_t*  lock)
+{
+    pthread_mutex_lock(lock);
+}
+static __inline__ void  mutex_unlock(mutex_t*  lock)
+{
+    pthread_mutex_unlock(lock);
+}
+static __inline__ int  mutex_init(mutex_t*  lock)
+{
+    return pthread_mutex_init(lock, NULL);
+}
+static __inline__ void mutex_destroy(mutex_t*  lock)
+{
+    pthread_mutex_destroy(lock);
+}
+#endif
+
+#ifdef HAVE_WIN32_THREADS
+typedef struct { 
+    int                init;
+    CRITICAL_SECTION   lock[1];
+} mutex_t;
+
+#define  MUTEX_INITIALIZER  { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
+
+static __inline__ void  mutex_lock(mutex_t*  lock)
+{
+    if (!lock->init) {
+        lock->init = 1;
+        InitializeCriticalSection( lock->lock );
+        lock->init = 2;
+    } else while (lock->init != 2)
+        Sleep(10);
+
+    EnterCriticalSection(lock->lock);
+}
+
+static __inline__ void  mutex_unlock(mutex_t*  lock)
+{
+    LeaveCriticalSection(lock->lock);
+}
+static __inline__ int  mutex_init(mutex_t*  lock)
+{
+    InitializeCriticalSection(lock->lock);
+    lock->init = 2;
+    return 0;
+}
+static __inline__ void  mutex_destroy(mutex_t*  lock)
+{
+    if (lock->init) {
+        lock->init = 0;
+        DeleteCriticalSection(lock->lock); 
+    }
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_CUTILS_THREADS_H */
diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h
new file mode 100644
index 0000000..9b3ece8
--- /dev/null
+++ b/include/cutils/tztime.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CUTILS_TZTIME_H
+#define _CUTILS_TZTIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+time_t mktime_tz(struct tm * const tmp, char const * tz);
+void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
+
+struct strftime_locale {
+    const char *mon[12];    /* short names */
+    const char *month[12];  /* long names */
+    const char *wday[7];    /* short names */
+    const char *weekday[7]; /* long names */
+    const char *X_fmt;
+    const char *x_fmt;
+    const char *c_fmt;
+    const char *am;
+    const char *pm;
+    const char *date_fmt;
+};
+
+size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_TZTIME_H */ 
+
diff --git a/include/cutils/uio.h b/include/cutils/uio.h
new file mode 100644
index 0000000..01a74d2
--- /dev/null
+++ b/include/cutils/uio.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// implementation of sys/uio.h for platforms that don't have it (Win32)
+//
+#ifndef _LIBS_CUTILS_UIO_H
+#define _LIBS_CUTILS_UIO_H
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+struct iovec {
+    const void*  iov_base;
+    size_t       iov_len;
+};
+
+extern int  readv( int  fd, struct iovec*  vecs, int  count );
+extern int  writev( int  fd, const struct iovec*  vecs, int  count );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !HAVE_SYS_UIO_H */
+
+#endif /* _LIBS_UTILS_UIO_H */
+
diff --git a/include/cutils/zygote.h b/include/cutils/zygote.h
new file mode 100644
index 0000000..22721a6
--- /dev/null
+++ b/include/cutils/zygote.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_ZYGOTE_H
+#define __CUTILS_ZYGOTE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int zygote_run_oneshot(int sendStdio, int argc, const char **argv);
+int zygote_run(int argc, const char **argv);
+int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_ZYGOTE_H */
diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h
new file mode 100644
index 0000000..7d7d571
--- /dev/null
+++ b/include/mincrypt/rsa.h
@@ -0,0 +1,56 @@
+/* rsa.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of Google Inc. nor the names of its contributors may
+**       be used to endorse or promote products derived from this software
+**       without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR 
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _EMBEDDED_RSA_H_
+#define _EMBEDDED_RSA_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RSANUMBYTES 256           /* 2048 bit key length */
+#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t))
+
+typedef struct RSAPublicKey {
+    int len;                  /* Length of n[] in number of uint32_t */
+    uint32_t n0inv;           /* -1 / n[0] mod 2^32 */
+    uint32_t n[RSANUMWORDS];  /* modulus as little endian array */
+    uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */
+} RSAPublicKey;
+
+int RSA_verify(const RSAPublicKey *key,
+               const uint8_t* signature,
+               const int len,
+               const uint8_t* sha);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h
new file mode 100644
index 0000000..c523460
--- /dev/null
+++ b/include/mincrypt/sha.h
@@ -0,0 +1,56 @@
+/* sha.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of Google Inc. nor the names of its contributors may
+**       be used to endorse or promote products derived from this software
+**       without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR 
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _EMBEDDED_SHA_H_
+#define _EMBEDDED_SHA_H_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct SHA_CTX {
+    uint64_t count;
+    uint8_t buf[64];
+    uint32_t state[5];
+} SHA_CTX;
+
+void SHA_init(SHA_CTX *ctx);
+void SHA_update(SHA_CTX *ctx, const void* data, int len);
+const uint8_t* SHA_final(SHA_CTX *ctx);
+
+/* Convenience method. Returns digest parameter value. */
+const uint8_t* SHA(const void *data, int len, uint8_t *digest);
+
+#define SHA_DIGEST_SIZE 20
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/pixelflinger/format.h b/include/pixelflinger/format.h
new file mode 100644
index 0000000..6b2050c
--- /dev/null
+++ b/include/pixelflinger/format.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_FORMAT_H
+#define ANDROID_PIXELFLINGER_FORMAT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+enum GGLPixelFormat {
+    // these constants need to match those
+    // in graphics/PixelFormat.java, ui/PixelFormat.h, BlitHardware.h
+    GGL_PIXEL_FORMAT_UNKNOWN    =   0,
+    GGL_PIXEL_FORMAT_NONE       =   0,
+
+    GGL_PIXEL_FORMAT_RGBA_8888   =   1,  // 4x8-bit ARGB
+    GGL_PIXEL_FORMAT_RGBX_8888   =   2,  // 3x8-bit RGB stored in 32-bit chunks
+    GGL_PIXEL_FORMAT_RGB_888     =   3,  // 3x8-bit RGB
+    GGL_PIXEL_FORMAT_RGB_565     =   4,  // 16-bit RGB
+    GGL_PIXEL_FORMAT_BGRA_8888   =   5,  // 4x8-bit BGRA
+    GGL_PIXEL_FORMAT_RGBA_5551   =   6,  // 16-bit RGBA
+    GGL_PIXEL_FORMAT_RGBA_4444   =   7,  // 16-bit RGBA
+
+    GGL_PIXEL_FORMAT_A_8         =   8,  // 8-bit A
+    GGL_PIXEL_FORMAT_L_8         =   9,  // 8-bit L (R=G=B = L)
+    GGL_PIXEL_FORMAT_LA_88       = 0xA,  // 16-bit LA
+    GGL_PIXEL_FORMAT_RGB_332     = 0xB,  // 8-bit RGB (non paletted)
+
+    // YCbCr formats (SP=semi-planar, P=planar)
+    GGL_PIXEL_FORMAT_YCbCr_422_SP= 0x10,
+    GGL_PIXEL_FORMAT_YCbCr_420_SP= 0x11,
+    GGL_PIXEL_FORMAT_YCbCr_422_P = 0x12,
+    GGL_PIXEL_FORMAT_YCbCr_420_P = 0x13,
+    GGL_PIXEL_FORMAT_YCbCr_422_I = 0x14,
+    GGL_PIXEL_FORMAT_YCbCr_420_I = 0x15,
+
+    // reserved/special formats
+    GGL_PIXEL_FORMAT_Z_16       =  0x18,
+    GGL_PIXEL_FORMAT_S_8        =  0x19,
+    GGL_PIXEL_FORMAT_SZ_24      =  0x1A,
+    GGL_PIXEL_FORMAT_SZ_8       =  0x1B,
+};
+
+enum GGLFormatComponents {
+	GGL_STENCIL_INDEX		= 0x1901,
+	GGL_DEPTH_COMPONENT		= 0x1902,
+	GGL_ALPHA				= 0x1906,
+	GGL_RGB					= 0x1907,
+	GGL_RGBA				= 0x1908,
+	GGL_LUMINANCE			= 0x1909,
+	GGL_LUMINANCE_ALPHA		= 0x190A,
+    GGL_Y_CB_CR_SP          = 0x8000,
+    GGL_Y_CB_CR             = GGL_Y_CB_CR_SP,
+    GGL_Y_CB_CR_P           = 0x8001,
+    GGL_Y_CB_CR_I           = 0x8002,
+};
+
+enum GGLFormatComponentIndex {
+    GGL_INDEX_ALPHA   = 0,
+    GGL_INDEX_RED     = 1,
+    GGL_INDEX_GREEN   = 2,
+    GGL_INDEX_BLUE    = 3,
+    GGL_INDEX_STENCIL = 0,
+    GGL_INDEX_DEPTH   = 1,
+    GGL_INDEX_Y       = 0,
+    GGL_INDEX_CB      = 1,
+    GGL_INDEX_CR      = 2,
+};
+
+typedef struct {
+#ifdef __cplusplus
+    enum {
+        ALPHA   = GGL_INDEX_ALPHA,
+        RED     = GGL_INDEX_RED,
+        GREEN   = GGL_INDEX_GREEN,
+        BLUE    = GGL_INDEX_BLUE,
+        STENCIL = GGL_INDEX_STENCIL,
+        DEPTH   = GGL_INDEX_DEPTH,
+        LUMA    = GGL_INDEX_Y,
+        CHROMAB = GGL_INDEX_CB,
+        CHROMAR = GGL_INDEX_CR,
+    };
+    inline uint32_t mask(int i) const {
+            return ((1<<(c[i].h-c[i].l))-1)<<c[i].l;
+    }
+    inline uint32_t bits(int i) const {
+            return c[i].h - c[i].l;
+    }
+#endif
+	uint8_t     size;	// bytes per pixel
+    uint8_t     bitsPerPixel;
+    union {    
+        struct {
+            uint8_t     ah;		// alpha high bit position + 1
+            uint8_t     al;		// alpha low bit position
+            uint8_t     rh;		// red high bit position + 1
+            uint8_t     rl;		// red low bit position
+            uint8_t     gh;		// green high bit position + 1
+            uint8_t     gl;		// green low bit position
+            uint8_t     bh;		// blue high bit position + 1
+            uint8_t     bl;		// blue low bit position
+        };
+        struct {
+            uint8_t h;
+            uint8_t l;
+        } __attribute__((__packed__)) c[4];        
+    } __attribute__((__packed__));
+	uint16_t    components;	// GGLFormatComponents
+} GGLFormat;
+
+
+#ifdef __cplusplus
+extern "C" const GGLFormat* gglGetPixelFormatTable(size_t* numEntries = 0);
+#else
+const GGLFormat* gglGetPixelFormatTable(size_t* numEntries);
+#endif
+
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_FORMAT_H
diff --git a/include/pixelflinger/pixelflinger.h b/include/pixelflinger/pixelflinger.h
new file mode 100644
index 0000000..dca0b90
--- /dev/null
+++ b/include/pixelflinger/pixelflinger.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PIXELFLINGER_H
+#define ANDROID_PIXELFLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <pixelflinger/format.h>
+
+// GGL types
+
+typedef int8_t			GGLbyte;		// b
+typedef int16_t			GGLshort;		// s
+typedef int32_t			GGLint;			// i
+typedef ssize_t			GGLsizei;		// i
+typedef int32_t			GGLfixed;		// x
+typedef int32_t			GGLclampx;		// x
+typedef float			GGLfloat;		// f
+typedef float			GGLclampf;		// f
+typedef double			GGLdouble;		// d
+typedef double			GGLclampd;		// d
+typedef uint8_t			GGLubyte;		// ub
+typedef uint8_t			GGLboolean;		// ub
+typedef uint16_t		GGLushort;		// us
+typedef uint32_t		GGLuint;		// ui
+typedef unsigned int	GGLenum;		// ui
+typedef unsigned int	GGLbitfield;	// ui
+typedef void			GGLvoid;
+typedef int32_t         GGLfixed32;
+typedef	int32_t         GGLcolor;
+typedef int32_t         GGLcoord;
+
+// ----------------------------------------------------------------------------
+
+#define GGL_MAX_VIEWPORT_DIMS           4096
+#define GGL_MAX_TEXTURE_SIZE            4096
+#define GGL_MAX_ALIASED_POINT_SIZE      0x7FFFFFF
+#define GGL_MAX_SMOOTH_POINT_SIZE       2048
+#define GGL_MAX_SMOOTH_LINE_WIDTH       2048
+
+// ----------------------------------------------------------------------------
+
+// All these names are compatible with their OpenGL equivalents
+// some of them are listed only for completeness
+enum GGLNames {
+	GGL_FALSE						= 0,
+	GGL_TRUE						= 1,
+
+	// enable/disable
+    GGL_SCISSOR_TEST                = 0x0C11,
+	GGL_TEXTURE_2D					= 0x0DE1,
+	GGL_ALPHA_TEST					= 0x0BC0,
+	GGL_BLEND						= 0x0BE2,
+	GGL_COLOR_LOGIC_OP				= 0x0BF2,
+	GGL_DITHER						= 0x0BD0,
+	GGL_STENCIL_TEST				= 0x0B90,
+	GGL_DEPTH_TEST					= 0x0B71,
+    GGL_AA                          = 0x80000001,
+    GGL_W_LERP                      = 0x80000004,
+    GGL_POINT_SMOOTH_NICE           = 0x80000005,
+
+    // buffers, pixel drawing/reading
+    GGL_COLOR                       = 0x1800,
+    
+    // fog
+    GGL_FOG                         = 0x0B60,
+    
+	// shade model
+	GGL_FLAT						= 0x1D00,
+	GGL_SMOOTH						= 0x1D01,
+
+	// Texture parameter name
+	GGL_TEXTURE_MIN_FILTER			= 0x2801,
+	GGL_TEXTURE_MAG_FILTER			= 0x2800,
+	GGL_TEXTURE_WRAP_S				= 0x2802,
+	GGL_TEXTURE_WRAP_T				= 0x2803,
+	GGL_TEXTURE_WRAP_R				= 0x2804,
+
+	// Texture Filter	
+	GGL_NEAREST						= 0x2600,
+	GGL_LINEAR						= 0x2601,
+	GGL_NEAREST_MIPMAP_NEAREST		= 0x2700,
+	GGL_LINEAR_MIPMAP_NEAREST		= 0x2701,
+	GGL_NEAREST_MIPMAP_LINEAR		= 0x2702,
+	GGL_LINEAR_MIPMAP_LINEAR		= 0x2703,
+
+	// Texture Wrap Mode
+	GGL_CLAMP						= 0x2900,
+	GGL_REPEAT						= 0x2901,
+    GGL_CLAMP_TO_EDGE               = 0x812F,
+
+	// Texture Env Mode
+	GGL_REPLACE						= 0x1E01,
+	GGL_MODULATE					= 0x2100,
+	GGL_DECAL						= 0x2101,
+	GGL_ADD							= 0x0104,
+
+	// Texture Env Parameter
+	GGL_TEXTURE_ENV_MODE			= 0x2200,
+	GGL_TEXTURE_ENV_COLOR			= 0x2201,
+
+	// Texture Env Target
+	GGL_TEXTURE_ENV					= 0x2300,
+
+    // Texture coord generation
+    GGL_TEXTURE_GEN_MODE            = 0x2500,
+    GGL_S                           = 0x2000,
+    GGL_T                           = 0x2001,
+    GGL_R                           = 0x2002,
+    GGL_Q                           = 0x2003,
+    GGL_ONE_TO_ONE                  = 0x80000002,
+    GGL_AUTOMATIC                   = 0x80000003,
+
+    // AlphaFunction
+    GGL_NEVER                       = 0x0200,
+    GGL_LESS                        = 0x0201,
+    GGL_EQUAL                       = 0x0202,
+    GGL_LEQUAL                      = 0x0203,
+    GGL_GREATER                     = 0x0204,
+    GGL_NOTEQUAL                    = 0x0205,
+    GGL_GEQUAL                      = 0x0206,
+    GGL_ALWAYS                      = 0x0207,
+
+    // LogicOp
+    GGL_CLEAR                       = 0x1500,   // 0
+    GGL_AND                         = 0x1501,   // s & d
+    GGL_AND_REVERSE                 = 0x1502,   // s & ~d
+    GGL_COPY                        = 0x1503,   // s
+    GGL_AND_INVERTED                = 0x1504,   // ~s & d
+    GGL_NOOP                        = 0x1505,   // d
+    GGL_XOR                         = 0x1506,   // s ^ d
+    GGL_OR                          = 0x1507,   // s | d
+    GGL_NOR                         = 0x1508,   // ~(s | d)
+    GGL_EQUIV                       = 0x1509,   // ~(s ^ d)
+    GGL_INVERT                      = 0x150A,   // ~d
+    GGL_OR_REVERSE                  = 0x150B,   // s | ~d
+    GGL_COPY_INVERTED               = 0x150C,   // ~s 
+    GGL_OR_INVERTED                 = 0x150D,   // ~s | d
+    GGL_NAND                        = 0x150E,   // ~(s & d)
+    GGL_SET                         = 0x150F,   // 1
+
+	// blending equation & function
+	GGL_ZERO                        = 0,		// SD
+	GGL_ONE                         = 1,		// SD
+	GGL_SRC_COLOR                   = 0x0300,	//  D
+	GGL_ONE_MINUS_SRC_COLOR         = 0x0301,	//	D
+	GGL_SRC_ALPHA                   = 0x0302,	// SD
+	GGL_ONE_MINUS_SRC_ALPHA			= 0x0303,	// SD
+	GGL_DST_ALPHA					= 0x0304,	// SD
+	GGL_ONE_MINUS_DST_ALPHA			= 0x0305,	// SD
+	GGL_DST_COLOR					= 0x0306,	// S
+	GGL_ONE_MINUS_DST_COLOR			= 0x0307,	// S
+	GGL_SRC_ALPHA_SATURATE			= 0x0308,	// S
+    
+    // clear bits
+    GGL_DEPTH_BUFFER_BIT            = 0x00000100,
+    GGL_STENCIL_BUFFER_BIT          = 0x00000400,
+    GGL_COLOR_BUFFER_BIT            = 0x00004000,
+
+    // errors
+    GGL_NO_ERROR                    = 0,
+    GGL_INVALID_ENUM                = 0x0500,
+    GGL_INVALID_VALUE               = 0x0501,
+    GGL_INVALID_OPERATION           = 0x0502,
+    GGL_STACK_OVERFLOW              = 0x0503,
+    GGL_STACK_UNDERFLOW             = 0x0504,
+    GGL_OUT_OF_MEMORY               = 0x0505
+};
+
+// ----------------------------------------------------------------------------
+
+typedef struct {
+    GGLsizei    version;    // always set to sizeof(GGLSurface)
+    GGLuint     width;      // width in pixels
+    GGLuint     height;     // height in pixels
+    GGLint      stride;     // stride in pixels
+    GGLubyte*   data;       // pointer to the bits
+    GGLubyte    format;     // pixel format
+    GGLubyte    rfu[3];     // must be zero
+    // these values are dependent on the used format
+    union {
+        GGLint  compressedFormat;
+        GGLint  vstride;
+    };
+    void*       reserved;
+} GGLSurface;
+
+
+typedef struct {
+    // immediate rendering
+    void (*pointx)(void *con, const GGLcoord* v, GGLcoord r);
+    void (*linex)(void *con, 
+            const GGLcoord* v0, const GGLcoord* v1, GGLcoord width);
+    void (*recti)(void* c, GGLint l, GGLint t, GGLint r, GGLint b); 
+    void (*trianglex)(void* c,
+            GGLcoord const* v0, GGLcoord const* v1, GGLcoord const* v2);
+
+    // scissor
+    void (*scissor)(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height);
+
+    // Set the textures and color buffers
+    void (*activeTexture)(void* c, GGLuint tmu);
+    void (*bindTexture)(void* c, const GGLSurface* surface);
+    void (*colorBuffer)(void* c, const GGLSurface* surface);
+    void (*readBuffer)(void* c, const GGLSurface* surface);
+    void (*depthBuffer)(void* c, const GGLSurface* surface);
+    void (*bindTextureLod)(void* c, GGLuint tmu, const GGLSurface* surface);
+
+    // enable/disable features
+    void (*enable)(void* c, GGLenum name);
+    void (*disable)(void* c, GGLenum name);
+    void (*enableDisable)(void* c, GGLenum name, GGLboolean en);
+
+    // specify the fragment's color
+    void (*shadeModel)(void* c, GGLenum mode);
+    void (*color4xv)(void* c, const GGLclampx* color);
+    // specify color iterators (16.16)
+    void (*colorGrad12xv)(void* c, const GGLcolor* grad);
+
+    // specify Z coordinate iterators (0.32)
+    void (*zGrad3xv)(void* c, const GGLfixed32* grad);
+
+    // specify W coordinate iterators (16.16)
+    void (*wGrad3xv)(void* c, const GGLfixed* grad);
+
+    // specify fog iterator & color (16.16)
+    void (*fogGrad3xv)(void* c, const GGLfixed* grad);
+    void (*fogColor3xv)(void* c, const GGLclampx* color);
+
+    // specify blending parameters
+    void (*blendFunc)(void* c, GGLenum src, GGLenum dst);
+    void (*blendFuncSeparate)(void* c,  GGLenum src, GGLenum dst,
+                                        GGLenum srcAlpha, GGLenum dstAplha);
+
+    // texture environnement (REPLACE / MODULATE / DECAL / BLEND)
+    void (*texEnvi)(void* c,    GGLenum target,
+                                GGLenum pname,
+                                GGLint param);
+
+    void (*texEnvxv)(void* c, GGLenum target,
+            GGLenum pname, const GGLfixed* params);
+
+    // texture parameters (Wrapping, filter)
+    void (*texParameteri)(void* c,  GGLenum target,
+                                    GGLenum pname,
+                                    GGLint param);
+
+    // texture iterators (16.16)
+    void (*texCoord2i)(void* c, GGLint s, GGLint t);
+    void (*texCoord2x)(void* c, GGLfixed s, GGLfixed t);
+    
+    // s, dsdx, dsdy, scale, t, dtdx, dtdy, tscale
+    // This api uses block floating-point for S and T texture coordinates.
+    // All values are given in 16.16, scaled by 'scale'. In other words,
+    // set scale to 0, for 16.16 values.
+    void (*texCoordGradScale8xv)(void* c, GGLint tmu, const int32_t* grad8);
+    
+    void (*texGeni)(void* c, GGLenum coord, GGLenum pname, GGLint param);
+
+    // masking
+    void (*colorMask)(void* c,  GGLboolean red,
+                                GGLboolean green,
+                                GGLboolean blue,
+                                GGLboolean alpha);
+
+    void (*depthMask)(void* c, GGLboolean flag);
+
+    void (*stencilMask)(void* c, GGLuint mask);
+
+    // alpha func
+    void (*alphaFuncx)(void* c, GGLenum func, GGLclampx ref);
+
+    // depth func
+    void (*depthFunc)(void* c, GGLenum func);
+
+    // logic op
+    void (*logicOp)(void* c, GGLenum opcode); 
+
+    // clear
+    void (*clear)(void* c, GGLbitfield mask);
+    void (*clearColorx)(void* c,
+            GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a);
+    void (*clearDepthx)(void* c, GGLclampx depth);
+    void (*clearStencil)(void* c, GGLint s);
+
+    // framebuffer operations
+    void (*copyPixels)(void* c, GGLint x, GGLint y,
+            GGLsizei width, GGLsizei height, GGLenum type);
+    void (*rasterPos2x)(void* c, GGLfixed x, GGLfixed y);
+    void (*rasterPos2i)(void* c, GGLint x, GGLint y);
+} GGLContext;
+
+// ----------------------------------------------------------------------------
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// construct / destroy the context
+ssize_t gglInit(GGLContext** context);
+ssize_t gglUninit(GGLContext* context);
+
+GGLint gglBitBlti(
+        GGLContext* c,
+        int tmu,
+        GGLint crop[4],
+        GGLint where[4]);
+
+#ifdef __cplusplus
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_PIXELFLINGER_H
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
new file mode 100644
index 0000000..c855187
--- /dev/null
+++ b/include/private/android_filesystem_config.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
+#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
+#define _ANDROID_FILESYSTEM_CONFIG_H_
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+/* This is the master Users and Groups config for the platform.
+** DO NOT EVER RENUMBER.
+*/
+
+#define AID_ROOT             0  /* traditional unix root user */
+
+#define AID_SYSTEM        1000  /* system server */
+
+#define AID_RADIO         1001  /* telephony subsystem, RIL */
+#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
+#define AID_GRAPHICS      1003  /* graphics devices */
+#define AID_INPUT         1004  /* input devices */
+#define AID_AUDIO         1005  /* audio devices */
+#define AID_CAMERA        1006  /* camera devices */
+#define AID_LOG           1007  /* log devices */
+#define AID_COMPASS       1008  /* compass device */
+#define AID_MOUNT         1009  /* mountd socket */
+#define AID_WIFI          1010  /* wifi subsystem */
+#define AID_ADB           1011  /* android debug bridge (adbd) */
+#define AID_INSTALL       1012  /* group for installing packages */
+#define AID_MEDIA         1013  /* mediaserver process */
+#define AID_DHCP          1014  /* dhcp client */
+
+#define AID_SHELL         2000  /* adb and debug shell user */
+#define AID_CACHE         2001  /* cache access */
+#define AID_DIAG          2002  /* access to diagnostic resources */
+
+/* The 3000 series are intended for use as supplemental group id's only.
+ * They indicate special Android capabilities that the kernel is aware of. */
+#define AID_NET_BT_ADMIN  3001  /* bluetooth: create any socket */
+#define AID_NET_BT        3002  /* bluetooth: create sco, rfcomm or l2cap sockets */
+#define AID_INET          3003  /* can create AF_INET and AF_INET6 sockets */
+#define AID_NET_RAW       3004  /* can create raw INET sockets */
+
+#define AID_MISC          9998  /* access to misc storage */
+#define AID_NOBODY        9999
+
+#define AID_APP          10000 /* first app user */
+
+#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+struct android_id_info {
+    const char *name;
+    unsigned aid;
+};
+
+static struct android_id_info android_ids[] = {
+    { "root",      AID_ROOT, },
+    { "system",    AID_SYSTEM, },
+    { "radio",     AID_RADIO, },
+    { "bluetooth", AID_BLUETOOTH, },
+    { "graphics",  AID_GRAPHICS, },
+    { "input",     AID_INPUT, },
+    { "audio",     AID_AUDIO, },
+    { "camera",    AID_CAMERA, },
+    { "log",       AID_LOG, },
+    { "compass",   AID_COMPASS, },
+    { "mount",     AID_MOUNT, },
+    { "wifi",      AID_WIFI, },
+    { "dhcp",      AID_DHCP, },
+    { "adb",       AID_ADB, },
+    { "install",   AID_INSTALL, },
+    { "media",     AID_MEDIA, },
+    { "shell",     AID_SHELL, },
+    { "cache",     AID_CACHE, },
+    { "diag",      AID_DIAG, },
+    { "net_bt_admin", AID_NET_BT_ADMIN, },
+    { "net_bt",    AID_NET_BT, },
+    { "inet",      AID_INET, }, 
+    { "net_raw",   AID_NET_RAW, },
+    { "misc",      AID_MISC, },
+    { "nobody",    AID_NOBODY, },
+};
+
+#define android_id_count \
+    (sizeof(android_ids) / sizeof(android_ids[0]))
+    
+struct fs_path_config {
+    unsigned mode;
+    unsigned uid;
+    unsigned gid;
+    const char *prefix;
+};
+
+/* Rules for directories.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root.
+*/
+
+static struct fs_path_config android_dirs[] = {
+    { 00770, AID_SYSTEM, AID_CACHE,  "cache" },
+    { 00771, AID_SYSTEM, AID_SYSTEM, "data/app" },
+    { 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" },
+    { 00771, AID_SYSTEM, AID_SYSTEM, "data/dalvik-cache" },
+    { 00771, AID_SYSTEM, AID_SYSTEM, "data/data" },
+    { 00771, AID_SHELL,  AID_SHELL,  "data/local/tmp" },
+    { 00771, AID_SHELL,  AID_SHELL,  "data/local" },
+    { 01771, AID_SYSTEM, AID_MISC,   "data/misc" },
+    { 00770, AID_DHCP,   AID_DHCP,   "data/misc/dhcp" },
+    { 00771, AID_SYSTEM, AID_SYSTEM, "data" },
+    { 00750, AID_ROOT,   AID_SHELL,  "sbin" },
+    { 00755, AID_ROOT,   AID_SHELL,  "system/bin" },
+    { 00755, AID_ROOT,   AID_SHELL,  "system/xbin" },
+    { 00777, AID_ROOT,   AID_ROOT,   "system/etc/ppp" }, /* REMOVE */
+    { 00777, AID_ROOT,   AID_ROOT,   "sdcard" },
+    { 00755, AID_ROOT,   AID_ROOT,   0 },
+};
+
+/* Rules for files.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root. Prefixes ending in * denotes wildcard
+** and will allow partial matches.
+*/
+static struct fs_path_config android_files[] = {
+    { 00555, AID_ROOT,      AID_ROOT,      "system/etc/ppp/ip-up" },
+    { 00555, AID_ROOT,      AID_ROOT,      "system/etc/ppp/ip-down" },
+    { 00440, AID_ROOT,      AID_SHELL,     "system/etc/init.goldfish.rc" },
+    { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.goldfish.sh" },
+    { 00440, AID_ROOT,      AID_SHELL,     "system/etc/init.trout.rc" },
+    { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.ril" },
+    { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.testmenu" },
+    { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.gprs-pppd" },
+    { 00550, AID_DHCP,      AID_SHELL,     "system/etc/dhcpcd/dhcpcd-run-hooks" },
+    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" },
+    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/hcid.conf" },
+    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/input.conf" },
+    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/audio.conf" },
+    { 00440, AID_RADIO,     AID_AUDIO,     "/system/etc/AudioPara4.csv" },
+    { 00644, AID_SYSTEM,    AID_SYSTEM,    "data/app/*" },
+    { 00644, AID_SYSTEM,    AID_SYSTEM,    "data/app-private/*" },
+    { 00644, AID_APP,       AID_APP,       "data/data/*" },
+        /* the following two files are INTENTIONALLY set-gid and not set-uid.
+         * Do not change. */
+    { 02755, AID_ROOT,      AID_NET_RAW,   "system/bin/ping" },
+    { 02755, AID_ROOT,      AID_INET,      "system/bin/netcfg" },
+    	/* the following four files are INTENTIONALLY set-uid, but they
+	 * are NOT included on user builds. */
+    { 06755, AID_ROOT,      AID_ROOT,      "system/xbin/su" },
+    { 06755, AID_ROOT,      AID_ROOT,      "system/xbin/librank" },
+    { 06755, AID_ROOT,      AID_ROOT,      "system/xbin/procrank" },
+    { 06755, AID_ROOT,      AID_ROOT,      "system/xbin/procmem" },
+    { 00755, AID_ROOT,      AID_SHELL,     "system/bin/*" },
+    { 00755, AID_ROOT,      AID_SHELL,     "system/xbin/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     "sbin/*" },
+    { 00755, AID_ROOT,      AID_ROOT,      "bin/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     "init*" },
+    { 00644, AID_ROOT,      AID_ROOT,       0 },
+};
+
+static inline void fs_config(const char *path, int dir,
+                             unsigned *uid, unsigned *gid, unsigned *mode)
+{
+    struct fs_path_config *pc;
+    int plen;
+    
+    pc = dir ? android_dirs : android_files;
+    plen = strlen(path);
+    for(; pc->prefix; pc++){
+        int len = strlen(pc->prefix);
+        if (dir) {
+            if(plen < len) continue;
+            if(!strncmp(pc->prefix, path, len)) break;
+            continue;
+        }
+        /* If name ends in * then allow partial matches. */
+        if (pc->prefix[len -1] == '*') {
+            if(!strncmp(pc->prefix, path, len - 1)) break;
+        } else if (plen == len){
+            if(!strncmp(pc->prefix, path, len)) break;
+        }
+    }
+    *uid = pc->uid;
+    *gid = pc->gid;
+    *mode = (*mode & (~07777)) | pc->mode;
+    
+#if 0
+    fprintf(stderr,"< '%s' '%s' %d %d %o >\n", 
+            path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode);
+#endif
+}
+#endif
+#endif
diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h
new file mode 100644
index 0000000..3a030c5
--- /dev/null
+++ b/include/private/pixelflinger/ggl_context.h
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GGL_CONTEXT_H
+#define ANDROID_GGL_CONTEXT_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Endian.h>
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_fixed.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
+    return v;
+}
+inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
+    return v;
+}
+
+#else
+
+inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
+    return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
+    return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+
+const int GGL_DITHER_BITS = 6;  // dither weights stored on 6 bits
+const int GGL_DITHER_ORDER_SHIFT= 3;
+const int GGL_DITHER_ORDER      = (1<<GGL_DITHER_ORDER_SHIFT);
+const int GGL_DITHER_SIZE       = GGL_DITHER_ORDER * GGL_DITHER_ORDER;
+const int GGL_DITHER_MASK       = GGL_DITHER_ORDER-1;
+
+// ----------------------------------------------------------------------------
+
+const int GGL_SUBPIXEL_BITS = 4;
+
+// TRI_FRACTION_BITS defines the number of bits we want to use
+// for the sub-pixel coordinates during the edge stepping, the
+// value shouldn't be more than 7, or bad things are going to
+// happen when drawing large triangles (8 doesn't work because
+// 32 bit muls will loose the sign bit)
+
+#define  TRI_FRACTION_BITS  (GGL_SUBPIXEL_BITS)
+#define  TRI_ONE            (1 << TRI_FRACTION_BITS)
+#define  TRI_HALF           (1 << (TRI_FRACTION_BITS-1))
+#define  TRI_FROM_INT(x)    ((x) << TRI_FRACTION_BITS)
+#define  TRI_FRAC(x)        ((x)                 &  (TRI_ONE-1))
+#define  TRI_FLOOR(x)       ((x)                 & ~(TRI_ONE-1))
+#define  TRI_CEIL(x)        (((x) + (TRI_ONE-1)) & ~(TRI_ONE-1))
+#define  TRI_ROUND(x)       (((x) +  TRI_HALF  ) & ~(TRI_ONE-1))
+
+#define  TRI_ROUDNING       (1 << (16 - TRI_FRACTION_BITS - 1))
+#define  TRI_FROM_FIXED(x)  (((x)+TRI_ROUDNING) >> (16-TRI_FRACTION_BITS))
+
+#define  TRI_SNAP_NEXT_HALF(x)   (TRI_CEIL((x)+TRI_HALF) - TRI_HALF)
+#define  TRI_SNAP_PREV_HALF(x)   (TRI_CEIL((x)-TRI_HALF) - TRI_HALF)
+
+// ----------------------------------------------------------------------------
+
+const int GGL_COLOR_BITS = 24;
+
+// To maintain 8-bits color chanels, with a maximum GGLSurface
+// size of 4096 and GGL_SUBPIXEL_BITS=4, we need 8 + 12 + 4 = 24 bits
+// for encoding the color iterators
+
+inline GGLcolor gglFixedToIteratedColor(GGLfixed c) {
+    return (c << 8) - c;
+}
+
+// ----------------------------------------------------------------------------
+
+template<bool> struct CTA;
+template<> struct CTA<true> { };
+
+#define GGL_CONTEXT(con, c)         context_t *con = static_cast<context_t *>(c)
+#define GGL_OFFSETOF(field)         int(&(((context_t*)0)->field))
+#define GGL_INIT_PROC(p, f)         p.f = ggl_ ## f;
+#define GGL_BETWEEN(x, L, H)        (uint32_t((x)-(L)) <= ((H)-(L)))
+
+#define ggl_likely(x)	__builtin_expect(!!(x), 1)
+#define ggl_unlikely(x)	__builtin_expect(!!(x), 0)
+
+const int GGL_TEXTURE_UNIT_COUNT    = 2;
+const int GGL_TMU_STATE             = 0x00000001;
+const int GGL_CB_STATE              = 0x00000002;
+const int GGL_PIXEL_PIPELINE_STATE  = 0x00000004;
+
+// ----------------------------------------------------------------------------
+
+#define GGL_RESERVE_NEEDS(name, l, s)                               \
+    const uint32_t  GGL_NEEDS_##name##_MASK = (((1LU<<(s))-1)<<l);  \
+    const uint32_t  GGL_NEEDS_##name##_SHIFT = (l);
+
+#define GGL_BUILD_NEEDS(val, name)                                  \
+    (((val)<<(GGL_NEEDS_##name##_SHIFT)) & GGL_NEEDS_##name##_MASK)
+
+#define GGL_READ_NEEDS(name, n)                                     \
+    (uint32_t(n & GGL_NEEDS_##name##_MASK) >> GGL_NEEDS_##name##_SHIFT)
+
+#define GGL_NEED_MASK(name)     (uint32_t(GGL_NEEDS_##name##_MASK))
+#define GGL_NEED(name, val)     GGL_BUILD_NEEDS(val, name)
+
+GGL_RESERVE_NEEDS( CB_FORMAT,       0, 6 )
+GGL_RESERVE_NEEDS( SHADE,           6, 1 )
+GGL_RESERVE_NEEDS( W,               7, 1 )
+GGL_RESERVE_NEEDS( BLEND_SRC,       8, 4 )
+GGL_RESERVE_NEEDS( BLEND_DST,      12, 4 )
+GGL_RESERVE_NEEDS( BLEND_SRCA,     16, 4 )
+GGL_RESERVE_NEEDS( BLEND_DSTA,     20, 4 )
+GGL_RESERVE_NEEDS( LOGIC_OP,       24, 4 )
+GGL_RESERVE_NEEDS( MASK_ARGB,      28, 4 )
+
+GGL_RESERVE_NEEDS( P_ALPHA_TEST,    0, 3 )
+GGL_RESERVE_NEEDS( P_AA,            3, 1 )
+GGL_RESERVE_NEEDS( P_DEPTH_TEST,    4, 3 )
+GGL_RESERVE_NEEDS( P_MASK_Z,        7, 1 )
+GGL_RESERVE_NEEDS( P_DITHER,        8, 1 )
+GGL_RESERVE_NEEDS( P_FOG,           9, 1 )
+GGL_RESERVE_NEEDS( P_RESERVED1,    10,22 )
+
+GGL_RESERVE_NEEDS( T_FORMAT,        0, 6 )
+GGL_RESERVE_NEEDS( T_RESERVED0,     6, 1 )
+GGL_RESERVE_NEEDS( T_POT,           7, 1 )
+GGL_RESERVE_NEEDS( T_S_WRAP,        8, 2 )
+GGL_RESERVE_NEEDS( T_T_WRAP,       10, 2 )
+GGL_RESERVE_NEEDS( T_ENV,          12, 3 )
+GGL_RESERVE_NEEDS( T_LINEAR,       15, 1 )
+
+const int GGL_NEEDS_WRAP_CLAMP_TO_EDGE  = 0;
+const int GGL_NEEDS_WRAP_REPEAT         = 1;
+const int GGL_NEEDS_WRAP_11             = 2;
+
+inline uint32_t ggl_wrap_to_needs(uint32_t e) {
+    switch (e) {
+    case GGL_CLAMP:         return GGL_NEEDS_WRAP_CLAMP_TO_EDGE;
+    case GGL_REPEAT:        return GGL_NEEDS_WRAP_REPEAT;
+    }
+    return 0;
+}
+
+inline uint32_t ggl_blendfactor_to_needs(uint32_t b) {
+    if (b <= 1) return b;
+    return (b & 0xF)+2;
+}
+
+inline uint32_t ggl_needs_to_blendfactor(uint32_t n) {
+    if (n <= 1) return n;
+    return (n - 2) + 0x300;
+}
+
+inline uint32_t ggl_env_to_needs(uint32_t e) {
+    switch (e) {
+    case GGL_REPLACE:   return 0;
+    case GGL_MODULATE:  return 1;
+    case GGL_DECAL:     return 2;
+    case GGL_BLEND:     return 3;
+    case GGL_ADD:       return 4;
+    }
+    return 0;
+}
+
+inline uint32_t ggl_needs_to_env(uint32_t n) {
+    const uint32_t envs[] = { GGL_REPLACE, GGL_MODULATE, 
+            GGL_DECAL, GGL_BLEND, GGL_ADD };
+    return envs[n];
+
+}
+
+// ----------------------------------------------------------------------------
+
+enum {
+    GGL_ENABLE_BLENDING     = 0x00000001,
+    GGL_ENABLE_SMOOTH       = 0x00000002,
+    GGL_ENABLE_AA           = 0x00000004,
+    GGL_ENABLE_LOGIC_OP     = 0x00000008,
+    GGL_ENABLE_ALPHA_TEST   = 0x00000010,
+    GGL_ENABLE_SCISSOR_TEST = 0x00000020,
+    GGL_ENABLE_TMUS         = 0x00000040,
+    GGL_ENABLE_DEPTH_TEST   = 0x00000080,
+    GGL_ENABLE_STENCIL_TEST = 0x00000100,
+    GGL_ENABLE_W            = 0x00000200,
+    GGL_ENABLE_DITHER       = 0x00000400,
+    GGL_ENABLE_FOG          = 0x00000800,
+    GGL_ENABLE_POINT_AA_NICE= 0x00001000
+};
+
+// ----------------------------------------------------------------------------
+
+class needs_filter_t;
+struct needs_t {
+    inline int match(const needs_filter_t& filter);
+    inline bool operator == (const needs_t& rhs) const {
+        return  (n==rhs.n) &&
+                (p==rhs.p) &&
+                (t[0]==rhs.t[0]) &&
+                (t[1]==rhs.t[1]);
+    }
+    inline bool operator != (const needs_t& rhs) const {
+        return !operator == (rhs);
+    }
+    uint32_t    n;
+    uint32_t    p;
+    uint32_t    t[GGL_TEXTURE_UNIT_COUNT];
+};
+
+inline int compare_type(const needs_t& lhs, const needs_t& rhs) {
+    return memcmp(&lhs, &rhs, sizeof(needs_t));
+}
+
+struct needs_filter_t {
+    needs_t     value;
+    needs_t     mask;
+};
+
+int needs_t::match(const needs_filter_t& filter) {
+    uint32_t result = 
+        ((filter.value.n ^ n)       & filter.mask.n)    |
+        ((filter.value.p ^ p)       & filter.mask.p)    |
+        ((filter.value.t[0] ^ t[0]) & filter.mask.t[0]) |
+        ((filter.value.t[1] ^ t[1]) & filter.mask.t[1]);
+    return (result == 0);
+}
+
+// ----------------------------------------------------------------------------
+
+struct context_t;
+class Assembly;
+
+struct blend_state_t {
+	uint32_t			src;
+	uint32_t			dst;
+	uint32_t			src_alpha;
+	uint32_t			dst_alpha;
+	uint8_t				reserved;
+	uint8_t				alpha_separate;
+	uint8_t				operation;
+	uint8_t				equation;
+};
+
+struct mask_state_t {
+    uint8_t             color;
+    uint8_t             depth;
+    uint32_t            stencil;
+};
+
+struct clear_state_t {
+    GGLclampx           r;
+    GGLclampx           g;
+    GGLclampx           b;
+    GGLclampx           a;
+    GGLclampx           depth;
+    GGLint              stencil;
+    uint32_t            colorPacked;
+    uint32_t            depthPacked;
+    uint32_t            stencilPacked;
+    uint32_t            dirty;
+};
+
+struct fog_state_t {
+    uint8_t     color[3];
+    uint8_t     reserved;
+};
+
+struct logic_op_state_t {
+    uint16_t            opcode;
+};
+
+struct alpha_test_state_t {
+    uint16_t            func;
+    GGLcolor            ref;
+};
+
+struct depth_test_state_t {
+    uint16_t            func;
+    GGLclampx           clearValue;
+};
+
+struct scissor_t {
+    uint32_t            user_left;
+    uint32_t            user_right;
+    uint32_t            user_top;
+    uint32_t            user_bottom;
+    uint32_t            left;
+    uint32_t            right;
+    uint32_t            top;
+    uint32_t            bottom;
+};
+
+struct pixel_t {
+    uint32_t    c[4];
+    uint8_t     s[4];
+};
+
+struct surface_t {
+    union {
+        GGLSurface      s;
+        struct {
+        uint32_t            reserved;
+        uint32_t			width;
+        uint32_t			height;
+        int32_t             stride;
+        uint8_t*			data;	
+        uint8_t				format;
+        uint8_t				dirty;
+        uint8_t				pad[2];
+        };
+    };
+    void                (*read) (const surface_t* s, context_t* c,
+                                uint32_t x, uint32_t y, pixel_t* pixel);
+    void                (*write)(const surface_t* s, context_t* c,
+                                uint32_t x, uint32_t y, const pixel_t* pixel);
+};
+
+// ----------------------------------------------------------------------------
+
+struct texture_shade_t {
+    union {
+        struct {
+            int32_t             is0;
+            int32_t             idsdx;
+            int32_t             idsdy;
+            int                 sscale;
+            int32_t             it0;
+            int32_t             idtdx;
+            int32_t             idtdy;
+            int                 tscale;
+        };
+        struct {
+            int32_t             v;
+            int32_t             dx;
+            int32_t             dy;
+            int                 scale;
+        } st[2];
+    };
+};
+
+struct texture_iterators_t {
+    // these are not encoded in the same way than in the
+    // texture_shade_t structure
+    union {
+        struct {
+            GGLfixed			ydsdy;
+            GGLfixed            dsdx;
+            GGLfixed            dsdy;
+            int                 sscale;
+            GGLfixed			ydtdy;
+            GGLfixed            dtdx;
+            GGLfixed            dtdy;
+            int                 tscale;
+        };
+        struct {
+            GGLfixed			ydvdy;
+            GGLfixed            dvdx;
+            GGLfixed            dvdy;
+            int                 scale;
+        } st[2];
+    };
+};
+
+struct texture_t {
+	surface_t			surface;
+	texture_iterators_t	iterators;
+    texture_shade_t     shade;
+	uint32_t			s_coord;
+	uint32_t            t_coord;
+	uint16_t			s_wrap;
+	uint16_t            t_wrap;
+	uint16_t            min_filter;
+	uint16_t            mag_filter;
+    uint16_t            env;
+    uint8_t             env_color[4];
+	uint8_t				enable;
+	uint8_t				dirty;
+};
+
+struct raster_t {
+    GGLfixed            x;
+    GGLfixed            y;
+};
+
+struct framebuffer_t {
+    surface_t           color;
+    surface_t           read;
+	surface_t			depth;
+	surface_t			stencil;
+    int16_t             *coverage;
+    size_t              coverageBufferSize;
+};
+
+// ----------------------------------------------------------------------------
+
+struct iterators_t {
+	int32_t             xl;
+	int32_t             xr;
+    int32_t             y;
+	GGLcolor			ydady;
+	GGLcolor			ydrdy;
+	GGLcolor			ydgdy;
+	GGLcolor			ydbdy;
+	GGLfixed			ydzdy;
+	GGLfixed			ydwdy;
+	GGLfixed			ydfdy;
+};
+
+struct shade_t {
+	GGLcolor			a0;
+    GGLcolor            dadx;
+    GGLcolor            dady;
+	GGLcolor			r0;
+    GGLcolor            drdx;
+    GGLcolor            drdy;
+	GGLcolor			g0;
+    GGLcolor            dgdx;
+    GGLcolor            dgdy;
+	GGLcolor			b0;
+    GGLcolor            dbdx;
+    GGLcolor            dbdy;
+	uint32_t            z0;
+    GGLfixed32          dzdx;
+    GGLfixed32          dzdy;
+	GGLfixed            w0;
+    GGLfixed            dwdx;
+    GGLfixed            dwdy;
+	uint32_t			f0;
+    GGLfixed            dfdx;
+    GGLfixed            dfdy;
+};
+
+// these are used in the generated code
+// we use this mirror structure to improve
+// data locality in the pixel pipeline
+struct generated_tex_vars_t {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    stride;
+    int32_t     data;
+    int32_t     dsdx;
+    int32_t     dtdx;
+    int32_t     spill[2];
+};
+
+struct generated_vars_t {
+    struct {
+        int32_t c;
+        int32_t dx;
+    } argb[4];
+    int32_t     aref;
+    int32_t     dzdx;
+    int32_t     zbase;
+    int32_t     f;
+    int32_t     dfdx;
+    int32_t     spill[3];
+    generated_tex_vars_t    texture[GGL_TEXTURE_UNIT_COUNT];
+    int32_t     rt;
+    int32_t     lb;
+};
+
+// ----------------------------------------------------------------------------
+
+struct state_t {
+	framebuffer_t		buffers;
+	texture_t			texture[GGL_TEXTURE_UNIT_COUNT];
+    scissor_t           scissor;
+    raster_t            raster;
+	blend_state_t		blend;
+    alpha_test_state_t  alpha_test;
+    depth_test_state_t  depth_test;
+    mask_state_t        mask;
+    clear_state_t       clear;
+    fog_state_t         fog;
+    logic_op_state_t    logic_op;
+    uint32_t            enables;
+    uint32_t            enabled_tmu;
+    needs_t             needs;
+};
+
+// ----------------------------------------------------------------------------
+
+struct context_t {
+	GGLContext          procs;
+	state_t             state;
+    shade_t             shade;
+	iterators_t         iterators;
+    generated_vars_t    generated_vars                __attribute__((aligned(32)));
+    uint8_t             ditherMatrix[GGL_DITHER_SIZE] __attribute__((aligned(32)));
+    uint32_t            packed;
+    uint32_t            packed8888;
+    const GGLFormat*    formats;
+    uint32_t            dirty;
+    texture_t*          activeTMU;
+    uint32_t            activeTMUIndex;
+
+    void                (*init_y)(context_t* c, int32_t y);
+	void                (*step_y)(context_t* c);
+	void                (*scanline)(context_t* c);
+    void                (*span)(context_t* c);
+    void                (*rect)(context_t* c, size_t yc);
+    
+    void*               base;
+    Assembly*           scanline_as;
+    GGLenum             error;
+};
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_context(context_t* context);
+void ggl_uninit_context(context_t* context);
+void ggl_error(context_t* c, GGLenum error);
+int64_t ggl_system_time();
+
+// ----------------------------------------------------------------------------
+
+};
+
+#endif // ANDROID_GGL_CONTEXT_H
+
diff --git a/include/private/pixelflinger/ggl_fixed.h b/include/private/pixelflinger/ggl_fixed.h
new file mode 100644
index 0000000..96fdb32
--- /dev/null
+++ b/include/private/pixelflinger/ggl_fixed.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GGL_FIXED_H
+#define ANDROID_GGL_FIXED_H
+
+#include <math.h>
+#include <pixelflinger/pixelflinger.h>
+
+// ----------------------------------------------------------------------------
+
+#define CONST           __attribute__((const))
+#define ALWAYS_INLINE   __attribute__((always_inline))
+
+const GGLfixed FIXED_BITS = 16;
+const GGLfixed FIXED_EPSILON  = 1;
+const GGLfixed FIXED_ONE  = 1L<<FIXED_BITS;
+const GGLfixed FIXED_HALF = 1L<<(FIXED_BITS-1);
+const GGLfixed FIXED_MIN  = 0x80000000L;
+const GGLfixed FIXED_MAX  = 0x7FFFFFFFL;
+
+inline GGLfixed gglIntToFixed(GGLfixed i)       ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntRound(GGLfixed f)  ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntFloor(GGLfixed f)  ALWAYS_INLINE ;
+inline GGLfixed gglFixedToIntCeil(GGLfixed f)   ALWAYS_INLINE ;
+inline GGLfixed gglFracx(GGLfixed v)            ALWAYS_INLINE ;
+inline GGLfixed gglFloorx(GGLfixed v)           ALWAYS_INLINE ;
+inline GGLfixed gglCeilx(GGLfixed v)            ALWAYS_INLINE ;
+inline GGLfixed gglCenterx(GGLfixed v)          ALWAYS_INLINE ;
+inline GGLfixed gglRoundx(GGLfixed v)           ALWAYS_INLINE ;
+
+GGLfixed gglIntToFixed(GGLfixed i) {
+    return i<<FIXED_BITS;
+}
+GGLfixed gglFixedToIntRound(GGLfixed f) {
+    return (f + FIXED_HALF)>>FIXED_BITS;
+}
+GGLfixed gglFixedToIntFloor(GGLfixed f) {
+    return f>>FIXED_BITS;
+}
+GGLfixed gglFixedToIntCeil(GGLfixed f) {
+    return (f + ((1<<FIXED_BITS) - 1))>>FIXED_BITS;
+}
+
+GGLfixed gglFracx(GGLfixed v) {
+    return v & ((1<<FIXED_BITS)-1);
+}
+GGLfixed gglFloorx(GGLfixed v) {
+    return gglFixedToIntFloor(v)<<FIXED_BITS;
+}
+GGLfixed gglCeilx(GGLfixed v) {
+    return gglFixedToIntCeil(v)<<FIXED_BITS;
+}
+GGLfixed gglCenterx(GGLfixed v) {
+    return gglFloorx(v + FIXED_HALF) | FIXED_HALF;
+}
+GGLfixed gglRoundx(GGLfixed v) {
+    return gglFixedToIntRound(v)<<FIXED_BITS;
+}
+
+// conversion from (unsigned) int, short, byte to fixed...
+#define GGL_B_TO_X(_x)      GGLfixed( ((int32_t(_x)+1)>>1)<<10 )
+#define GGL_S_TO_X(_x)      GGLfixed( ((int32_t(_x)+1)>>1)<<2 )
+#define GGL_I_TO_X(_x)      GGLfixed( ((int32_t(_x)>>1)+1)>>14 )
+#define GGL_UB_TO_X(_x)     GGLfixed(   uint32_t(_x) +      \
+                                        (uint32_t(_x)<<8) + \
+                                        (uint32_t(_x)>>7) )
+#define GGL_US_TO_X(_x)     GGLfixed( (_x) + ((_x)>>15) )
+#define GGL_UI_TO_X(_x)     GGLfixed( (((_x)>>1)+1)>>15 )
+
+// ----------------------------------------------------------------------------
+
+GGLfixed gglPowx(GGLfixed x, GGLfixed y) CONST;
+GGLfixed gglSqrtx(GGLfixed a) CONST;
+GGLfixed gglSqrtRecipx(GGLfixed x) CONST;
+GGLfixed gglFastDivx(GGLfixed n, GGLfixed d) CONST;
+int32_t gglMulDivi(int32_t a, int32_t b, int32_t c);
+
+int32_t gglRecipQNormalized(int32_t x, int* exponent);
+int32_t gglRecipQ(GGLfixed x, int q) CONST;
+
+inline GGLfixed gglRecip(GGLfixed x) CONST;
+inline GGLfixed gglRecip(GGLfixed x) {
+    return gglRecipQ(x, 16);
+}
+
+inline GGLfixed gglRecip28(GGLfixed x) CONST;
+int32_t gglRecip28(GGLfixed x) {
+    return gglRecipQ(x, 28);
+}
+
+// ----------------------------------------------------------------------------
+
+#if defined(__arm__) && !defined(__thumb__)
+
+// inline ARM implementations
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+    GGLfixed result, t;
+    if (__builtin_constant_p(shift)) {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "movs   %[lo], %[lo], lsr %[rshift]         \n"
+        "adc    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=r"(result), [hi]"=r"(t), [x]"=r"(x)
+        : "%[x]"(x), [y]"r"(y), [lshift] "I"(32-shift), [rshift] "I"(shift)
+        : "cc"
+        );
+    } else {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "movs   %[lo], %[lo], lsr %[rshift]         \n"
+        "adc    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+        : "%[x]"(x), [y]"r"(y), [lshift] "r"(32-shift), [rshift] "r"(shift)
+        : "cc"
+        );
+    }
+    return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+    GGLfixed result, t;
+    if (__builtin_constant_p(shift)) {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "add    %[lo], %[a],  %[lo], lsr %[rshift]  \n"
+        "add    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+        : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift)
+        );
+    } else {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "add    %[lo], %[a],  %[lo], lsr %[rshift]  \n"
+        "add    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+        : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift)
+        );
+    }
+    return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+    GGLfixed result, t;
+    if (__builtin_constant_p(shift)) {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "rsb    %[lo], %[a],  %[lo], lsr %[rshift]  \n"
+        "add    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+        : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "I"(32-shift), [rshift] "I"(shift)
+        );
+    } else {
+    asm("smull  %[lo], %[hi], %[x], %[y]            \n"
+        "rsb    %[lo], %[a],  %[lo], lsr %[rshift]  \n"
+        "add    %[lo], %[lo], %[hi], lsl %[lshift]  \n"
+        : [lo]"=&r"(result), [hi]"=&r"(t), [x]"=&r"(x)
+        : "%[x]"(x), [y]"r"(y), [a]"r"(a), [lshift] "r"(32-shift), [rshift] "r"(shift)
+        );
+    }
+    return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y)
+{
+    // 64-bits result: r0=low, r1=high
+    union {
+        struct {
+            int32_t lo;
+            int32_t hi;
+        } s;
+        int64_t res;
+    };
+    asm("smull %0, %1, %2, %3   \n"
+        : "=r"(s.lo), "=&r"(s.hi)
+        : "%r"(x), "r"(y)
+        :
+        );
+    return res;
+}
+
+#else // ----------------------------------------------------------------------
+
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+    return GGLfixed((int64_t(a)*b + (1<<(shift-1)))>>shift);
+}
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    return GGLfixed((int64_t(a)*b)>>shift) + c;
+}
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    return GGLfixed((int64_t(a)*b)>>shift) - c;
+}
+inline int64_t gglMulii(int32_t a, int32_t b) CONST;
+inline int64_t gglMulii(int32_t a, int32_t b) {
+    return int64_t(a)*b;
+}
+
+#endif
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b) {
+    return gglMulx(a, b, 16);
+}
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c) {
+    return gglMulAddx(a, b, c, 16);
+}
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) {
+    return gglMulSubx(a, b, c, 16);
+}
+
+// ------------------------------------------------------------------------
+
+inline int32_t gglClz(int32_t x) CONST;
+inline int32_t gglClz(int32_t x)
+{
+#if defined(__arm__) && !defined(__thumb__)
+    return __builtin_clz(x);
+#else
+    if (!x) return 32;
+    int32_t exp = 31;
+    if (x & 0xFFFF0000) { exp -=16; x >>= 16; }
+    if (x & 0x0000ff00) { exp -= 8; x >>= 8; }
+    if (x & 0x000000f0) { exp -= 4; x >>= 4; }
+    if (x & 0x0000000c) { exp -= 2; x >>= 2; }
+    if (x & 0x00000002) { exp -= 1; }
+    return exp;
+#endif
+}
+
+// ------------------------------------------------------------------------
+
+int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i) CONST;
+
+inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) CONST;
+inline int32_t gglDivQ16(GGLfixed n, GGLfixed d) {
+    return gglDivQ(n, d, 16);
+}
+
+inline int32_t gglDivx(GGLfixed n, GGLfixed d) CONST;
+inline int32_t gglDivx(GGLfixed n, GGLfixed d) {
+    return gglDivQ(n, d, 16);
+}
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglRecipFast(GGLfixed x) CONST;
+inline GGLfixed gglRecipFast(GGLfixed x)
+{
+    // This is a really bad approximation of 1/x, but it's also
+    // very fast. x must be strictly positive.
+    // if x between [0.5, 1[ , then 1/x = 3-2*x
+    // (we use 2.30 fixed-point)
+    const int32_t lz = gglClz(x);
+    return (0xC0000000 - (x << (lz - 1))) >> (30-lz);
+}
+
+// ------------------------------------------------------------------------
+
+inline GGLfixed gglClampx(GGLfixed c) CONST;
+inline GGLfixed gglClampx(GGLfixed c)
+{
+#if defined(__thumb__)
+    // clamp without branches
+    c &= ~(c>>31);  c = FIXED_ONE - c;
+    c &= ~(c>>31);  c = FIXED_ONE - c;
+#else
+#if defined(__arm__)
+    // I don't know why gcc thinks its smarter than me! The code below
+    // clamps to zero in one instruction, but gcc won't generate it and
+    // replace it by a cmp + movlt (it's quite amazing actually).
+    asm("bic %0, %1, %1, asr #31\n" : "=r"(c) : "r"(c));
+#else
+    c &= ~(c>>31);
+#endif
+    if (c>FIXED_ONE)
+        c = FIXED_ONE;
+#endif
+    return c;
+}
+
+// ------------------------------------------------------------------------
+
+#endif // ANDROID_GGL_FIXED_H
diff --git a/include/zipfile/zipfile.h b/include/zipfile/zipfile.h
new file mode 100644
index 0000000..0ae4ee4
--- /dev/null
+++ b/include/zipfile/zipfile.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ZIPFILE_ZIPFILE_H
+#define _ZIPFILE_ZIPFILE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* zipfile_t;
+typedef void* zipentry_t;
+
+// Provide a buffer.  Returns NULL on failure.
+zipfile_t init_zipfile(const void* data, size_t size);
+
+// Release the zipfile resources.
+void release_zipfile(zipfile_t file);
+
+// Get a named entry object.  Returns NULL if it doesn't exist
+// or if we won't be able to decompress it.  The zipentry_t is
+// freed by release_zipfile()
+zipentry_t lookup_zipentry(zipfile_t file, const char* entryName);
+
+// Return the size of the entry.
+size_t get_zipentry_size(zipentry_t entry);
+
+// return the filename of this entry, you own the memory returned
+char* get_zipentry_name(zipentry_t entry);
+
+// The buffer must be 1.001 times the buffer size returned
+// by get_zipentry_size.  Returns nonzero on failure.
+int decompress_zipentry(zipentry_t entry, void* buf, int bufsize);
+
+// iterate through the entries in the zip file.  pass a pointer to
+// a void* initialized to NULL to start.  Returns NULL when done
+zipentry_t iterate_zipfile(zipfile_t file, void** cookie);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _ZIPFILE_ZIPFILE_H
diff --git a/init/Android.mk b/init/Android.mk
new file mode 100644
index 0000000..d3766d4
--- /dev/null
+++ b/init/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2005 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	builtins.c \
+	init.c \
+	devices.c \
+	property_service.c \
+	util.c \
+	parser.c \
+	logo.c
+
+ifeq ($(strip $(INIT_BOOTCHART)),true)
+LOCAL_SRC_FILES += bootchart.c
+LOCAL_CFLAGS    += -DBOOTCHART=1
+endif
+
+LOCAL_MODULE:= init
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+
+LOCAL_STATIC_LIBRARIES := libcutils libc
+
+#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static
+#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt
+#LOCAL_STATIC_LIBRARIES += libstdc++_static
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/init/MODULE_LICENSE_APACHE2 b/init/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/init/MODULE_LICENSE_APACHE2
diff --git a/init/NOTICE b/init/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/init/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/init/README.BOOTCHART b/init/README.BOOTCHART
new file mode 100644
index 0000000..70cf2c3
--- /dev/null
+++ b/init/README.BOOTCHART
@@ -0,0 +1,52 @@
+This version of init contains code to perform "bootcharting", i.e. generating log
+files that can be later processed by the tools provided by www.bootchart.org.
+
+To activate it, you need to define build 'init' with the INIT_BOOTCHART environment
+variable defined to 'true', for example:
+
+    touch system/init/init.c
+    m INIT_BOOTCHART=true
+
+On the emulator, use the new -bootchart <timeout> option to boot with bootcharting
+activated for <timeout> seconds.
+
+Otherwise, flash your device, and start it. Then create a file on the /data partition
+with a command like the following:
+
+  adb shell 'echo $TIMEOUT > /data/bootchart-start'
+
+Where the value of $TIMEOUT corresponds to the wanted bootcharted period in seconds;
+for example, to bootchart for 2 minutes, do:
+
+  adb shell 'echo 120 > /data/bootchart-start'
+
+Reboot your device, bootcharting will begin and stop after the period you gave.
+You can also stop the bootcharting at any moment by doing the following:
+
+  adb shell 'echo 1 > /data/bootchart-stop'
+
+Note that /data/bootchart-stop is deleted automatically by init at the end of the
+bootcharting. This is not the case of /data/bootchart-start, so don't forget to delete it
+when you're done collecting data:
+
+  adb shell rm /data/bootchart-start
+
+The log files are placed in /data/bootchart/. you must run the script tools/grab-bootchart.sh
+which will use ADB to retrieve them and create a bootchart.tgz file that can be used with
+the bootchart parser/renderer, or even uploaded directly to the form located at:
+
+  http://www.bootchart.org/download.html
+
+NOTE: the bootchart.org webform doesn't seem to work at the moment, you can generate an
+      image on your machine by doing the following:
+
+         1/ download the sources from www.bootchart.org
+         2/ unpack them
+         3/ in the source directory, type 'ant' to build the bootchart program
+         4/ type 'java -jar bootchart.jar /path/to/bootchart.tgz
+
+technical note:
+
+this implementation of bootcharting does use the 'bootchartd' script provided by
+www.bootchart.org, but a C re-implementation that is directly compiled into our init
+program.
diff --git a/init/bootchart.c b/init/bootchart.c
new file mode 100644
index 0000000..f72fcaa
--- /dev/null
+++ b/init/bootchart.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* this code is used to generate a boot sequence profile that can be used
+ * with the 'bootchart' graphics generation tool. see www.bootchart.org
+ * note that unlike the original bootchartd, this is not a Bash script but
+ * some C code that is run right from the init script.
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include "bootchart.h"
+
+#define VERSION         "0.8"
+#define SAMPLE_PERIOD   0.2
+#define LOG_ROOT        "/data/bootchart"
+#define LOG_STAT        LOG_ROOT"/proc_stat.log"
+#define LOG_PROCS       LOG_ROOT"/proc_ps.log"
+#define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
+#define LOG_HEADER      LOG_ROOT"/header"
+#define LOG_ACCT        LOG_ROOT"/kernel_pacct"
+
+#define LOG_STARTFILE   "/data/bootchart-start"
+#define LOG_STOPFILE    "/data/bootchart-stop"
+
+static int
+unix_read(int  fd, void*  buff, int  len)
+{
+    int  ret;
+    do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR);
+    return ret;
+}
+
+static int
+unix_write(int  fd, const void*  buff, int  len)
+{
+    int  ret;
+    do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR);
+    return ret;
+}
+
+static int
+proc_read(const char*  filename, char* buff, size_t  buffsize)
+{
+    int  len = 0;
+    int  fd  = open(filename, O_RDONLY);
+    if (fd >= 0) {
+        len = unix_read(fd, buff, buffsize-1);
+        close(fd);
+    }
+    buff[len > 0 ? len : 0] = 0;
+    return len;
+}
+
+#define FILE_BUFF_SIZE    65536
+
+typedef struct {
+    int   count;
+    int   fd;
+    char  data[FILE_BUFF_SIZE];
+} FileBuffRec, *FileBuff;
+
+static void
+file_buff_open( FileBuff  buff, const char*  path )
+{
+    buff->count = 0;
+    buff->fd    = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0755);
+}
+
+static void
+file_buff_write( FileBuff  buff, const void*  src, int  len )
+{
+    while (len > 0) {
+        int  avail = sizeof(buff->data) - buff->count;
+        if (avail > len)
+            avail = len;
+
+        memcpy( buff->data + buff->count, src, avail );
+        len -= avail;
+        src  = (char*)src + avail;
+
+        buff->count += avail;
+        if (buff->count == FILE_BUFF_SIZE) {
+            unix_write( buff->fd, buff->data, buff->count );
+            buff->count = 0;
+        }
+    }
+}
+
+static void
+file_buff_done( FileBuff  buff )
+{
+    if (buff->count > 0) {
+        unix_write( buff->fd, buff->data, buff->count );
+        buff->count = 0;
+    }
+}
+
+static void
+log_header(void)
+{
+    FILE*      out;
+    char       cmdline[1024];
+    char       uname[128];
+    char       cpuinfo[128];
+    char*      cpu;
+    char       date[32];
+    time_t     now_t = time(NULL);
+    struct tm  now = *localtime(&now_t);
+    strftime(date, sizeof(date), "%x %X", &now);
+
+    out = fopen( LOG_HEADER, "w" );
+    if (out == NULL)
+        return;
+
+    proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
+    proc_read("/proc/version", uname, sizeof(uname));
+    proc_read("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo));
+
+    cpu = strchr( cpuinfo, ':' );
+    if (cpu) {
+        char*  p = strchr(cpu, '\n');
+        cpu += 2;
+        if (p)
+            *p = 0;
+    }
+
+    fprintf(out, "version = %s\n", VERSION);
+    fprintf(out, "title = Boot chart for Android ( %s )\n", date);
+    fprintf(out, "system.uname = %s\n", uname);
+    fprintf(out, "system.release = 0.0\n");
+    fprintf(out, "system.cpu = %s\n", cpu);
+    fprintf(out, "system.kernel.options = %s\n", cmdline);
+    fclose(out);
+}
+
+static void
+close_on_exec(int  fd)
+{
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void
+open_log_file(int*  plogfd, const char*  logfile)
+{
+    int    logfd = *plogfd;
+
+    /* create log file if needed */
+    if (logfd < 0) 
+    {
+        logfd = open(logfile,O_WRONLY|O_CREAT|O_TRUNC,0755);
+        if (logfd < 0) {
+            *plogfd = -2;
+            return;
+        }
+        close_on_exec(logfd);
+        *plogfd = logfd;
+    }
+}
+
+static void
+do_log_uptime(FileBuff  log)
+{
+    char  buff[65];
+    int   fd, ret, len;
+
+    fd = open("/proc/uptime",O_RDONLY);
+    if (fd >= 0) {
+        int  ret;
+        ret = unix_read(fd, buff, 64);
+        close(fd);
+        buff[64] = 0;
+        if (ret >= 0) {
+            long long  jiffies = 100LL*strtod(buff,NULL);
+            int        len;
+            snprintf(buff,sizeof(buff),"%lld\n",jiffies);
+            len = strlen(buff);
+            file_buff_write(log, buff, len);
+        }
+    }
+}
+
+static void
+do_log_ln(FileBuff  log)
+{
+    file_buff_write(log, "\n", 1);
+}
+
+
+static void
+do_log_file(FileBuff  log, const char*  procfile)
+{
+    char   buff[1024];
+    int    fd;
+
+    do_log_uptime(log);
+
+    /* append file content */
+    fd = open(procfile,O_RDONLY);
+    if (fd >= 0) {
+        close_on_exec(fd);
+        for (;;) {
+            int  ret;
+            ret = unix_read(fd, buff, sizeof(buff));
+            if (ret <= 0)
+                break;
+
+            file_buff_write(log, buff, ret);
+            if (ret < (int)sizeof(buff))
+                break;
+        }
+        close(fd);
+    }
+
+    do_log_ln(log);
+}
+
+static void
+do_log_procs(FileBuff  log)
+{
+    DIR*  dir = opendir("/proc");
+    struct dirent*  entry;
+
+    do_log_uptime(log);
+
+    while ((entry = readdir(dir)) != NULL) {
+        /* only match numeric values */
+        char*  end;
+        int    pid = strtol( entry->d_name, &end, 10);
+        if (end != NULL && end > entry->d_name && *end == 0) {
+            char  filename[32];
+            char  buff[1024];
+            char  cmdline[1024];
+            int   len;
+            int   fd;
+
+            /* read command line and extract program name */
+            snprintf(filename,sizeof(filename),"/proc/%d/cmdline",pid);
+            proc_read(filename, cmdline, sizeof(cmdline));
+
+            /* read process stat line */
+            snprintf(filename,sizeof(filename),"/proc/%d/stat",pid);
+            fd = open(filename,O_RDONLY);
+            if (fd >= 0) {
+               len = unix_read(fd, buff, sizeof(buff)-1);
+               close(fd);
+               if (len > 0) {
+                    int  len2 = strlen(cmdline);
+                    if (len2 > 0) {
+                        /* we want to substitute the process name with its real name */
+                        const char*  p1;
+                        const char*  p2;
+                        buff[len] = 0;
+                        p1 = strchr(buff, '(');
+                        p2 = strchr(p1, ')');
+                        file_buff_write(log, buff, p1+1-buff);
+                        file_buff_write(log, cmdline, strlen(cmdline));
+                        file_buff_write(log, p2, strlen(p2));
+                    } else {
+                        /* no substitution */
+                        file_buff_write(log,buff,len);
+                    }
+               }
+            }
+        }
+    }
+    closedir(dir);
+    do_log_ln(log);
+}
+
+static FileBuffRec  log_stat[1];
+static FileBuffRec  log_procs[1];
+static FileBuffRec  log_disks[1];
+
+/* called to setup bootcharting */
+int   bootchart_init( void )
+{
+    int  ret;
+    char buff[4];
+    int  timeout = 0, count = 0;
+
+    buff[0] = 0;
+    proc_read( LOG_STARTFILE, buff, sizeof(buff) );
+    if (buff[0] != 0) {
+        timeout = atoi(buff);
+    }
+    else {
+        /* when running with emulator, androidboot.bootchart=<timeout>
+         * might be passed by as kernel parameters to specify the bootchart
+         * timeout. this is useful when using -wipe-data since the /data
+         * partition is fresh
+         */
+        char  cmdline[1024];
+        char* s;
+#define  KERNEL_OPTION  "androidboot.bootchart="
+        proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
+        s = strstr(cmdline, KERNEL_OPTION);
+        if (s) {
+            s      += sizeof(KERNEL_OPTION)-1;
+            timeout = atoi(s);
+        }
+    }
+    if (timeout == 0)
+        return 0;
+
+    if (timeout > BOOTCHART_MAX_TIME_SEC)
+        timeout = BOOTCHART_MAX_TIME_SEC;
+
+    count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
+
+    do {ret=mkdir(LOG_ROOT,0755);}while (ret < 0 && errno == EINTR);
+
+    file_buff_open(log_stat,  LOG_STAT);
+    file_buff_open(log_procs, LOG_PROCS);
+    file_buff_open(log_disks, LOG_DISK);
+
+    /* create kernel process accounting file */
+    {
+        int  fd = open( LOG_ACCT, O_WRONLY|O_CREAT|O_TRUNC,0644);
+        if (fd >= 0) {
+            close(fd);
+            acct( LOG_ACCT );
+        }
+    }
+
+    log_header();
+    return count;
+}
+
+/* called each time you want to perform a bootchart sampling op */
+int  bootchart_step( void )
+{
+    do_log_file(log_stat,   "/proc/stat");
+    do_log_file(log_disks,  "/proc/diskstats");
+    do_log_procs(log_procs);
+
+    /* we stop when /data/bootchart-stop contains 1 */
+    {
+        char  buff[2];
+        if (proc_read(LOG_STOPFILE,buff,sizeof(buff)) > 0 && buff[0] == '1') {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+void  bootchart_finish( void )
+{
+    unlink( LOG_STOPFILE );
+    file_buff_done(log_stat);
+    file_buff_done(log_disks);
+    file_buff_done(log_procs);
+    acct(NULL);
+}
diff --git a/init/bootchart.h b/init/bootchart.h
new file mode 100644
index 0000000..39d2d4f
--- /dev/null
+++ b/init/bootchart.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BOOTCHART_H
+#define _BOOTCHART_H
+
+#ifndef BOOTCHART
+# define  BOOTCHART  0
+#endif
+
+#if BOOTCHART
+
+extern int   bootchart_init(void);
+extern int   bootchart_step(void);
+extern void  bootchart_finish(void);
+
+# define BOOTCHART_POLLING_MS   200   /* polling period in ms */
+# define BOOTCHART_DEFAULT_TIME_SEC    (2*60)  /* default polling time in seconds */
+# define BOOTCHART_MAX_TIME_SEC        (10*60) /* max polling time in seconds */
+
+#endif /* BOOTCHART */
+
+#endif /* _BOOTCHART_H */
diff --git a/init/builtins.c b/init/builtins.c
new file mode 100644
index 0000000..95fb223
--- /dev/null
+++ b/init/builtins.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <linux/kd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+
+#include "init.h"
+#include "keywords.h"
+#include "property_service.h"
+#include "devices.h"
+
+#include <private/android_filesystem_config.h>
+
+void add_environment(const char *name, const char *value);
+
+extern int init_module(void *, unsigned long, const char *);
+
+static int write_file(const char *path, const char *value)
+{
+    int fd, ret, len;
+
+    fd = open(path, O_WRONLY|O_CREAT, 0622);
+
+    if (fd < 0)
+        return -1;
+
+    len = strlen(value);
+
+    do {
+        ret = write(fd, value, len);
+    } while (ret < 0 && errno == EINTR);
+
+    close(fd);
+    if (ret < 0) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+static int insmod(const char *filename, char *options)
+{
+    void *module;
+    unsigned size;
+    int ret;
+
+    module = read_file(filename, &size);
+    if (!module)
+        return -1;
+
+    ret = init_module(module, size, options);
+
+    free(module);
+
+    return ret;
+}
+
+static int setkey(struct kbentry *kbe)
+{
+    int fd, ret;
+
+    fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    if (fd < 0)
+        return -1;
+
+    ret = ioctl(fd, KDSKBENT, kbe);
+
+    close(fd);
+    return ret;
+}
+
+static int __ifupdown(const char *interface, int up)
+{
+    struct ifreq ifr;
+    int s, ret;
+
+    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0)
+        return -1;
+
+    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
+    if (ret < 0) {
+        goto done;
+    }
+
+    if (up)
+        ifr.ifr_flags |= IFF_UP;
+    else
+        ifr.ifr_flags &= ~IFF_UP;
+
+    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
+    
+done:
+    close(s);
+    return ret;
+}
+
+static void service_start_if_not_disabled(struct service *svc)
+{
+    if (!(svc->flags & SVC_DISABLED)) {
+        service_start(svc);
+    }
+}
+
+int do_class_start(int nargs, char **args)
+{
+        /* Starting a class does not start services
+         * which are explicitly disabled.  They must
+         * be started individually.
+         */
+    service_for_each_class(args[1], service_start_if_not_disabled);
+    return 0;
+}
+
+int do_class_stop(int nargs, char **args)
+{
+    service_for_each_class(args[1], service_stop);
+    return 0;
+}
+
+int do_domainname(int nargs, char **args)
+{
+    return write_file("/proc/sys/kernel/domainname", args[1]);
+}
+
+int do_exec(int nargs, char **args)
+{
+    return -1;
+}
+
+int do_export(int nargs, char **args)
+{
+    add_environment(args[1], args[2]);
+    return 0;
+}
+
+int do_hostname(int nargs, char **args)
+{
+    return write_file("/proc/sys/kernel/hostname", args[1]);
+}
+
+int do_ifup(int nargs, char **args)
+{
+    return __ifupdown(args[1], 1);
+}
+
+
+static int do_insmod_inner(int nargs, char **args, int opt_len)
+{
+    char options[opt_len + 1];
+    int i;
+
+    options[0] = '\0';
+    if (nargs > 2) {
+        strcpy(options, args[2]);
+        for (i = 3; i < nargs; ++i) {
+            strcat(options, " ");
+            strcat(options, args[i]);
+        }
+    }
+
+    return insmod(args[1], options);
+}
+
+int do_insmod(int nargs, char **args)
+{
+    int i;
+    int size = 0;
+
+    if (nargs > 2) {
+        for (i = 2; i < nargs; ++i)
+            size += strlen(args[i]) + 1;
+    }
+
+    return do_insmod_inner(nargs, args, size);
+}
+
+int do_import(int nargs, char **args)
+{
+    return -1;
+}
+
+int do_mkdir(int nargs, char **args)
+{
+    mode_t mode = 0755;
+
+    /* mkdir <path> [mode] [owner] [group] */
+
+    if (nargs >= 3) {
+        mode = strtoul(args[2], 0, 8);
+    }
+
+    if (mkdir(args[1], mode)) {
+        return -errno;
+    }
+
+    if (nargs >= 4) {
+        uid_t uid = decode_uid(args[3]);
+        gid_t gid = -1;
+
+        if (nargs == 5) {
+            gid = decode_uid(args[4]);
+        }
+
+        if (chown(args[1], uid, gid)) {
+            return -errno;
+        }
+    }
+
+    return 0;
+}
+
+static struct {
+    const char *name;
+    unsigned flag;
+} mount_flags[] = {
+    { "noatime",    MS_NOATIME },
+    { "nosuid",     MS_NOSUID },
+    { "nodev",      MS_NODEV },
+    { "nodiratime", MS_NODIRATIME },
+    { "ro",         MS_RDONLY },
+    { "rw",         0 },
+    { "remount",    MS_REMOUNT },
+    { "defaults",   0 },
+    { 0,            0 },
+};
+
+/* mount <type> <device> <path> <flags ...> <options> */
+int do_mount(int nargs, char **args)
+{
+    char tmp[64];
+    char *source;
+    char *options = NULL;
+    unsigned flags = 0;
+    int n, i;
+
+    for (n = 4; n < nargs; n++) {
+        for (i = 0; mount_flags[i].name; i++) {
+            if (!strcmp(args[n], mount_flags[i].name)) {
+                flags |= mount_flags[i].flag;
+                break;
+            }
+        }
+
+        /* if our last argument isn't a flag, wolf it up as an option string */
+        if (n + 1 == nargs && !mount_flags[i].name)
+            options = args[n];
+    }
+
+    source = args[2];
+    if (!strncmp(source, "mtd@", 4)) {
+        n = mtd_name_to_number(source + 4);
+        if (n >= 0) {
+            sprintf(tmp, "/dev/block/mtdblock%d", n);
+            source = tmp;
+        }
+    }
+    return mount(source, args[3], args[1], flags, options);
+}
+
+int do_setkey(int nargs, char **args)
+{
+    struct kbentry kbe;
+    kbe.kb_table = strtoul(args[1], 0, 0);
+    kbe.kb_index = strtoul(args[2], 0, 0);
+    kbe.kb_value = strtoul(args[3], 0, 0);
+    return setkey(&kbe);
+}
+
+int do_setprop(int nargs, char **args)
+{
+    property_set(args[1], args[2]);
+    return 0;
+}
+
+int do_setrlimit(int nargs, char **args)
+{
+    struct rlimit limit;
+    int resource;
+    resource = atoi(args[1]);
+    limit.rlim_cur = atoi(args[2]);
+    limit.rlim_max = atoi(args[3]);
+    return setrlimit(resource, &limit);
+}
+
+int do_start(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_start(svc);
+    }
+    return 0;
+}
+
+int do_stop(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_stop(svc);
+    }
+    return 0;
+}
+
+int do_restart(int nargs, char **args)
+{
+    struct service *svc;
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        service_stop(svc);
+        service_start(svc);
+    }
+    return 0;
+}
+
+int do_trigger(int nargs, char **args)
+{
+    return 0;
+}
+
+int do_symlink(int nargs, char **args)
+{
+    return symlink(args[1], args[2]);
+}
+
+int do_sysclktz(int nargs, char **args)
+{
+    struct timezone tz;
+
+    if (nargs != 2)
+        return -1;
+
+    memset(&tz, 0, sizeof(tz));
+    tz.tz_minuteswest = atoi(args[1]);   
+    if (settimeofday(NULL, &tz))
+        return -1;
+    return 0;
+}
+
+int do_write(int nargs, char **args)
+{
+    return write_file(args[1], args[2]);
+}
+
+int do_chown(int nargs, char **args) {
+    /* GID is optional. */
+    if (nargs == 3) {
+        if (chown(args[2], decode_uid(args[1]), -1) < 0)
+            return -errno;
+    } else if (nargs == 4) {
+        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
+            return -errno;
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+static mode_t get_mode(const char *s) {
+    mode_t mode = 0;
+    while (*s) {
+        if (*s >= '0' && *s <= '7') {
+            mode = (mode<<3) | (*s-'0');
+        } else {
+            return -1;
+        }
+        s++;
+    }
+    return mode;
+}
+
+int do_chmod(int nargs, char **args) {
+    mode_t mode = get_mode(args[1]);
+    if (chmod(args[2], mode) < 0) {
+        return -errno;
+    }
+    return 0;
+}
+
+int do_loglevel(int nargs, char **args) {
+    if (nargs == 2) {
+        log_set_level(atoi(args[1]));
+        return 0;
+    }
+    return -1;
+}
+
+int do_device(int nargs, char **args) {
+    int len;
+    char tmp[64];
+    char *source = args[1];
+    int prefix = 0;
+
+    if (nargs != 5)
+        return -1;
+    /* Check for wildcard '*' at the end which indicates a prefix. */
+    len = strlen(args[1]) - 1;
+    if (args[1][len] == '*') {
+        args[1][len] = '\0';
+        prefix = 1;
+    }
+    /* If path starts with mtd@ lookup the mount number. */
+    if (!strncmp(source, "mtd@", 4)) {
+        int n = mtd_name_to_number(source + 4);
+        if (n >= 0) {
+            snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n);
+            source = tmp;
+        }
+    }
+    add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]),
+                          decode_uid(args[4]), prefix);
+    return 0;
+}
diff --git a/init/devices.c b/init/devices.c
new file mode 100644
index 0000000..b1ef6ab
--- /dev/null
+++ b/init/devices.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+#include <private/android_filesystem_config.h>
+#include <sys/time.h>
+#include <asm/page.h>
+
+#include "init.h"
+#include "devices.h"
+
+#define CMDLINE_PREFIX  "/dev"
+#define SYSFS_PREFIX    "/sys"
+#define FIRMWARE_DIR    "/etc/firmware"
+#define MAX_QEMU_PERM 6
+
+struct uevent {
+    const char *action;
+    const char *path;
+    const char *subsystem;
+    const char *firmware;
+    int major;
+    int minor;
+};
+
+static int open_uevent_socket(void)
+{
+    struct sockaddr_nl addr;
+    int sz = 64*1024; // XXX larger? udev uses 16MB!
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+    if(s < 0)
+        return -1;
+
+    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+struct perms_ {
+    char *name;
+    mode_t perm;
+    unsigned int uid;
+    unsigned int gid;
+    unsigned short prefix;
+};
+static struct perms_ devperms[] = {
+    { "/dev/null",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/zero",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/full",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/ptmx",          0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/tty",           0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/random",        0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/urandom",       0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/ashmem",        0666,   AID_ROOT,       AID_ROOT,       0 },
+    { "/dev/binder",        0666,   AID_ROOT,       AID_ROOT,       0 },
+
+	    /* logger should be world writable (for logging) but not readable */
+    { "/dev/log/",          0662,   AID_ROOT,       AID_LOG,        1 },
+
+        /* these should not be world writable */
+    { "/dev/android_adb",   0660,   AID_ADB,        AID_ADB,        0 },
+    { "/dev/android_adb_enable",   0660,   AID_ADB,        AID_ADB,        0 },
+    { "/dev/ttyMSM0",       0600,   AID_BLUETOOTH,  AID_BLUETOOTH,  0 },
+    { "/dev/ttyHS0",        0600,   AID_BLUETOOTH,  AID_BLUETOOTH,  0 },
+    { "/dev/uinput",        0600,   AID_BLUETOOTH,  AID_BLUETOOTH,  0 },
+    { "/dev/alarm",         0664,   AID_SYSTEM,     AID_RADIO,      0 },
+    { "/dev/tty0",          0660,   AID_ROOT,       AID_SYSTEM,     0 },
+    { "/dev/graphics/",     0660,   AID_ROOT,       AID_GRAPHICS,   1 },
+    { "/dev/hw3d",          0660,   AID_SYSTEM,     AID_GRAPHICS,   0 },
+    { "/dev/input/",        0660,   AID_ROOT,       AID_INPUT,      1 },
+    { "/dev/eac",           0660,   AID_ROOT,       AID_AUDIO,      0 },
+    { "/dev/cam",           0660,   AID_ROOT,       AID_CAMERA,     0 },
+    { "/dev/pmem",          0660,   AID_SYSTEM,     AID_GRAPHICS,   0 },
+    { "/dev/pmem_gpu",      0660,   AID_SYSTEM,     AID_GRAPHICS,   1 },
+    { "/dev/pmem_adsp",     0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/pmem_camera",   0660,   AID_SYSTEM,     AID_CAMERA,     1 },
+    { "/dev/oncrpc/",       0660,   AID_ROOT,       AID_SYSTEM,     1 },
+    { "/dev/adsp/",         0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/mt9t013",       0660,   AID_SYSTEM,     AID_SYSTEM,     0 },
+    { "/dev/akm8976_daemon",0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/akm8976_aot",   0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/akm8976_pffd",  0640,   AID_COMPASS,    AID_SYSTEM,     0 },
+    { "/dev/msm_pcm_out",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_pcm_in",    0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_pcm_ctl",   0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_snd",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_mp3",       0660,   AID_SYSTEM,     AID_AUDIO,      1 },
+    { "/dev/msm_audpre",    0660,   AID_SYSTEM,     AID_AUDIO,      0 },
+    { "/dev/htc-acoustic",  0660,   AID_SYSTEM,     AID_AUDIO,      0 },
+    { "/dev/smd0",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi",           0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi0",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi1",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qmi2",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { NULL, 0, 0, 0, 0 },
+};
+
+/* devperms_partners list and perm_node are for hardware specific /dev entries */
+struct perm_node {
+    struct perms_ dp;
+    struct listnode plist;
+};
+list_declare(devperms_partners);
+
+/*
+ * Permission override when in emulator mode, must be parsed before
+ * system properties is initalized.
+ */
+static int qemu_perm_count;
+static struct perms_ qemu_perms[MAX_QEMU_PERM + 1];
+
+int add_devperms_partners(const char *name, mode_t perm, unsigned int uid,
+                        unsigned int gid, unsigned short prefix) {
+    int size;
+    struct perm_node *node = malloc(sizeof (struct perm_node));
+    if (!node)
+        return -ENOMEM;
+
+    size = strlen(name) + 1;
+    if ((node->dp.name = malloc(size)) == NULL)
+        return -ENOMEM;
+
+    memcpy(node->dp.name, name, size);
+    node->dp.perm = perm;
+    node->dp.uid = uid;
+    node->dp.gid = gid;
+    node->dp.prefix = prefix;
+
+    list_add_tail(&devperms_partners, &node->plist);
+    return 0;
+}
+
+void qemu_init(void) {
+    qemu_perm_count = 0;
+    memset(&qemu_perms, 0, sizeof(qemu_perms));
+}
+
+static int qemu_perm(const char* name, mode_t perm, unsigned int uid,
+                         unsigned int gid, unsigned short prefix)
+{
+    char *buf;
+    if (qemu_perm_count == MAX_QEMU_PERM)
+        return -ENOSPC;
+
+    buf = malloc(strlen(name) + 1);
+    if (!buf)
+        return -errno;
+
+    strlcpy(buf, name, strlen(name) + 1);
+    qemu_perms[qemu_perm_count].name = buf;
+    qemu_perms[qemu_perm_count].perm = perm;
+    qemu_perms[qemu_perm_count].uid = uid;
+    qemu_perms[qemu_perm_count].gid = gid;
+    qemu_perms[qemu_perm_count].prefix = prefix;
+
+    qemu_perm_count++;
+    return 0;
+}
+
+/* Permission overrides for emulator that are parsed from /proc/cmdline. */
+void qemu_cmdline(const char* name, const char *value)
+{
+    char *buf;
+    if (!strcmp(name, "android.ril")) {
+        /* cmd line params currently assume /dev/ prefix */
+        if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) {
+            return;
+        }
+        INFO("nani- buf:: %s\n", buf);
+        qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0);
+    }
+}
+
+static int get_device_perm_inner(struct perms_ *perms, const char *path,
+                                    unsigned *uid, unsigned *gid, mode_t *perm)
+{
+    int i;
+    for(i = 0; perms[i].name; i++) {
+
+        if(perms[i].prefix) {
+            if(strncmp(path, perms[i].name, strlen(perms[i].name)))
+                continue;
+        } else {
+            if(strcmp(path, perms[i].name))
+                continue;
+        }
+        *uid = perms[i].uid;
+        *gid = perms[i].gid;
+        *perm = perms[i].perm;
+        return 0;
+    }
+    return -1;
+}
+
+/* First checks for emulator specific permissions specified in /proc/cmdline. */
+static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
+{
+    mode_t perm;
+
+    if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) {
+        return perm;
+    } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) {
+        return perm;
+    } else {
+        struct listnode *node;
+        struct perm_node *perm_node;
+        struct perms_ *dp;
+
+        /* Check partners list. */
+        list_for_each(node, &devperms_partners) {
+            perm_node = node_to_item(node, struct perm_node, plist);
+            dp = &perm_node->dp;
+
+            if (dp->prefix) {
+                if (strncmp(path, dp->name, strlen(dp->name)))
+                    continue;
+            } else {
+                if (strcmp(path, dp->name))
+                    continue;
+            }
+            /* Found perm in partner list. */
+            *uid = dp->uid;
+            *gid = dp->gid;
+            return dp->perm;
+        }
+        /* Default if nothing found. */
+        *uid = 0;
+        *gid = 0;
+        return 0600;
+    }
+}
+
+static void make_device(const char *path, int block, int major, int minor)
+{
+    unsigned uid;
+    unsigned gid;
+    mode_t mode;
+    dev_t dev;
+
+    if(major > 255 || minor > 255)
+        return;
+
+    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+    dev = (major << 8) | minor;
+    mknod(path, mode, dev);
+    chown(path, uid, gid);
+}
+
+#ifdef LOG_UEVENTS
+
+static inline suseconds_t get_usecs(void)
+{
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
+}
+
+#define log_event_print(x...) INFO(x)
+
+#else
+
+#define log_event_print(fmt, args...)   do { } while (0)
+#define get_usecs()                     0
+
+#endif
+
+static void parse_event(const char *msg, struct uevent *uevent)
+{
+    uevent->action = "";
+    uevent->path = "";
+    uevent->subsystem = "";
+    uevent->firmware = "";
+    uevent->major = -1;
+    uevent->minor = -1;
+
+        /* currently ignoring SEQNUM */
+    while(*msg) {
+        if(!strncmp(msg, "ACTION=", 7)) {
+            msg += 7;
+            uevent->action = msg;
+        } else if(!strncmp(msg, "DEVPATH=", 8)) {
+            msg += 8;
+            uevent->path = msg;
+        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
+            msg += 10;
+            uevent->subsystem = msg;
+        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
+            msg += 9;
+            uevent->firmware = msg;
+        } else if(!strncmp(msg, "MAJOR=", 6)) {
+            msg += 6;
+            uevent->major = atoi(msg);
+        } else if(!strncmp(msg, "MINOR=", 6)) {
+            msg += 6;
+            uevent->minor = atoi(msg);
+        }
+
+            /* advance to after the next \0 */
+        while(*msg++)
+            ;
+    }
+
+    log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
+                    uevent->action, uevent->path, uevent->subsystem,
+                    uevent->firmware, uevent->major, uevent->minor);
+}
+
+static void handle_device_event(struct uevent *uevent)
+{
+    char devpath[96];
+    char *base, *name;
+    int block;
+
+        /* if it's not a /dev device, nothing to do */
+    if((uevent->major < 0) || (uevent->minor < 0))
+        return;
+
+        /* do we have a name? */
+    name = strrchr(uevent->path, '/');
+    if(!name)
+        return;
+    name++;
+
+        /* too-long names would overrun our buffer */
+    if(strlen(name) > 64)
+        return;
+
+        /* are we block or char? where should we live? */
+    if(!strncmp(uevent->subsystem, "block", 5)) {
+        block = 1;
+        base = "/dev/block/";
+        mkdir(base, 0755);
+    } else {
+        block = 0;
+            /* this should probably be configurable somehow */
+        if(!strncmp(uevent->subsystem, "graphics", 8)) {
+            base = "/dev/graphics/";
+            mkdir(base, 0755);
+        } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
+            base = "/dev/oncrpc/";
+            mkdir(base, 0755);
+        } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
+            base = "/dev/adsp/";
+            mkdir(base, 0755);
+      } else if(!strncmp(uevent->subsystem, "input", 5)) {
+            base = "/dev/input/";
+            mkdir(base, 0755);
+        } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
+            base = "/dev/mtd/";
+            mkdir(base, 0755);
+        } else if(!strncmp(uevent->subsystem, "misc", 4) &&
+                    !strncmp(name, "log_", 4)) {
+            base = "/dev/log/";
+            mkdir(base, 0755);
+            name += 4;
+        } else
+            base = "/dev/";
+    }
+
+    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
+
+    if(!strcmp(uevent->action, "add")) {
+        make_device(devpath, block, uevent->major, uevent->minor);
+        return;
+    }
+
+    if(!strcmp(uevent->action, "remove")) {
+        unlink(devpath);
+        return;
+    }
+}
+
+static int load_firmware(int fw_fd, int loading_fd, int data_fd)
+{
+    struct stat st;
+    long len_to_copy;
+    int ret = 0;
+
+    if(fstat(fw_fd, &st) < 0)
+        return -1;
+    len_to_copy = st.st_size;
+
+    write(loading_fd, "1", 1);  /* start transfer */
+
+    while (len_to_copy > 0) {
+        char buf[PAGE_SIZE];
+        ssize_t nr;
+
+        nr = read(fw_fd, buf, sizeof(buf));
+        if(!nr)
+            break;
+        if(nr < 0) {
+            ret = -1;
+            break;
+        }
+
+        len_to_copy -= nr;
+        while (nr > 0) {
+            ssize_t nw = 0;
+
+            nw = write(data_fd, buf + nw, nr);
+            if(nw <= 0) {
+                ret = -1;
+                goto out;
+            }
+            nr -= nw;
+        }
+    }
+
+out:
+    if(!ret)
+        write(loading_fd, "0", 1);  /* successful end of transfer */
+    else
+        write(loading_fd, "-1", 2); /* abort transfer */
+
+    return ret;
+}
+
+static void process_firmware_event(struct uevent *uevent)
+{
+    char *root, *loading, *data, *file;
+    int l, loading_fd, data_fd, fw_fd;
+
+    log_event_print("firmware event { '%s', '%s' }\n",
+                    uevent->path, uevent->firmware);
+
+    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
+    if (l == -1)
+        return;
+
+    l = asprintf(&loading, "%sloading", root);
+    if (l == -1)
+        goto root_free_out;
+
+    l = asprintf(&data, "%sdata", root);
+    if (l == -1)
+        goto loading_free_out;
+
+    l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware);
+    if (l == -1)
+        goto data_free_out;
+
+    loading_fd = open(loading, O_WRONLY);
+    if(loading_fd < 0)
+        goto file_free_out;
+
+    data_fd = open(data, O_WRONLY);
+    if(data_fd < 0)
+        goto loading_close_out;
+
+    fw_fd = open(file, O_RDONLY);
+    if(fw_fd < 0)
+        goto data_close_out;
+
+    if(!load_firmware(fw_fd, loading_fd, data_fd))
+        log_event_print("firmware copy success { '%s', '%s' }\n", root, file);
+    else
+        log_event_print("firmware copy failure { '%s', '%s' }\n", root, file);
+
+    close(fw_fd);
+data_close_out:
+    close(data_fd);
+loading_close_out:
+    close(loading_fd);
+file_free_out:
+    free(file);
+data_free_out:
+    free(data);
+loading_free_out:
+    free(loading);
+root_free_out:
+    free(root);
+}
+
+static void handle_firmware_event(struct uevent *uevent)
+{
+    pid_t pid;
+
+    if(strcmp(uevent->subsystem, "firmware"))
+        return;
+
+    if(strcmp(uevent->action, "add"))
+        return;
+
+    /* we fork, to avoid making large memory allocations in init proper */
+    pid = fork();
+    if (!pid) {
+        process_firmware_event(uevent);
+        exit(EXIT_SUCCESS);
+    }
+}
+
+#define UEVENT_MSG_LEN  1024
+void handle_device_fd(int fd)
+{
+    char msg[UEVENT_MSG_LEN+2];
+    int n;
+
+    while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) {
+        struct uevent uevent;
+
+        if(n == UEVENT_MSG_LEN)   /* overflow -- discard */
+            continue;
+
+        msg[n] = '\0';
+        msg[n+1] = '\0';
+
+        parse_event(msg, &uevent);
+
+        handle_device_event(&uevent);
+        handle_firmware_event(&uevent);
+    }
+}
+
+/* Coldboot walks parts of the /sys tree and pokes the uevent files
+** to cause the kernel to regenerate device add events that happened
+** before init's device manager was started
+**
+** We drain any pending events from the netlink socket every time
+** we poke another uevent file to make sure we don't overrun the
+** socket's buffer.  
+*/
+
+static void do_coldboot(int event_fd, DIR *d)
+{
+    struct dirent *de;
+    int dfd, fd;
+
+    dfd = dirfd(d);
+
+    fd = openat(dfd, "uevent", O_WRONLY);
+    if(fd >= 0) {
+        write(fd, "add\n", 4);
+        close(fd);
+        handle_device_fd(event_fd);
+    }
+
+    while((de = readdir(d))) {
+        DIR *d2;
+
+        if(de->d_type != DT_DIR || de->d_name[0] == '.')
+            continue;
+
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        if(fd < 0)
+            continue;
+
+        d2 = fdopendir(fd);
+        if(d2 == 0)
+            close(fd);
+        else {
+            do_coldboot(event_fd, d2);
+            closedir(d2);
+        }
+    }
+}
+
+static void coldboot(int event_fd, const char *path)
+{
+    DIR *d = opendir(path);
+    if(d) {
+        do_coldboot(event_fd, d);
+        closedir(d);
+    }
+}
+
+int device_init(void)
+{
+    suseconds_t t0, t1;
+    int fd;
+
+    fd = open_uevent_socket();
+    if(fd < 0)
+        return -1;
+
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+
+    t0 = get_usecs();
+    coldboot(fd, "/sys/class");
+    coldboot(fd, "/sys/block");
+    coldboot(fd, "/sys/devices");
+    t1 = get_usecs();
+
+    log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
+
+    return fd;
+}
diff --git a/init/devices.h b/init/devices.h
new file mode 100644
index 0000000..b484da4
--- /dev/null
+++ b/init/devices.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_DEVICES_H
+#define _INIT_DEVICES_H
+
+extern void handle_device_fd(int fd);
+extern int device_init(void);
+extern void qemu_init(void);
+extern void qemu_cmdline(const char* name, const char *value);
+extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid,
+                                 unsigned int gid, unsigned short prefix);
+
+#endif	/* _INIT_DEVICES_H */
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
new file mode 100755
index 0000000..7fe8904
--- /dev/null
+++ b/init/grab-bootchart.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# this script is used to retrieve the bootchart log generated
+# by init when compiled with INIT_BOOTCHART=true.
+#
+# for all details, see //device/system/init/README.BOOTCHART
+#
+TMPDIR=/tmp/android-bootchart
+rm -rf $TMPDIR
+mkdir -p $TMPDIR
+
+LOGROOT=/data/bootchart
+TARBALL=bootchart.tgz
+
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+
+for f in $FILES; do
+    adb pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
+done
+(cd $TMPDIR && tar -czf $TARBALL $FILES)
+cp -f $TMPDIR/$TARBALL ./$TARBALL
+echo "look at $TARBALL"
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 0000000..b8b4f40
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <time.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <mtd/mtd-user.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/reboot.h>
+
+#include <cutils/sockets.h>
+#include <termios.h>
+#include <linux/kd.h>
+#include <linux/keychord.h>
+
+#include <sys/system_properties.h>
+
+#include "devices.h"
+#include "init.h"
+#include "property_service.h"
+#include "bootchart.h"
+
+static int property_triggers_enabled = 0;
+
+#if BOOTCHART
+static int   bootchart_count;
+#endif
+
+static char console[32];
+static char serialno[32];
+static char bootmode[32];
+static char baseband[32];
+static char carrier[32];
+static char bootloader[32];
+static char hardware[32];
+static unsigned revision = 0;
+static char qemu[32];
+static struct input_keychord *keychords = 0;
+static int keychords_count = 0;
+static int keychords_length = 0;
+
+static void drain_action_queue(void);
+
+static void notify_service_state(const char *name, const char *state)
+{
+    char pname[PROP_NAME_MAX];
+    int len = strlen(name);
+    if ((len + 10) > PROP_NAME_MAX)
+        return;
+    snprintf(pname, sizeof(pname), "init.svc.%s", name);
+    property_set(pname, state);
+}
+
+static int have_console;
+static char *console_name = "/dev/console";
+static time_t process_needs_restart;
+
+static const char *ENV[32];
+
+/* add_environment - add "key=value" to the current environment */
+int add_environment(const char *key, const char *val)
+{
+    int n;
+ 
+    for (n = 0; n < 31; n++) {
+        if (!ENV[n]) {
+            size_t len = strlen(key) + strlen(val) + 2;
+            char *entry = malloc(len);
+            snprintf(entry, len, "%s=%s", key, val);
+            ENV[n] = entry;
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static void zap_stdio(void)
+{
+    int fd;
+    fd = open("/dev/null", O_RDWR);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+static void open_console()
+{
+    int fd;
+    if ((fd = open(console_name, O_RDWR)) < 0) {
+        fd = open("/dev/null", O_RDWR);
+    }
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+/*
+ * gettime() - returns the time in seconds of the system's monotonic clock or
+ * zero on error.
+ */
+static time_t gettime(void)
+{
+    struct timespec ts;
+    int ret;
+
+    ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno));
+        return 0;
+    }
+
+    return ts.tv_sec;
+}
+
+static void publish_socket(const char *name, int fd)
+{
+    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
+    char val[64];
+
+    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
+            name,
+            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
+    snprintf(val, sizeof(val), "%d", fd);
+    add_environment(key, val);
+
+    /* make sure we don't close-on-exec */
+    fcntl(fd, F_SETFD, 0);
+}
+
+void service_start(struct service *svc)
+{
+    struct stat s;
+    pid_t pid;
+    int needs_console;
+    int n;
+
+        /* starting a service removes it from the disabled
+         * state and immediately takes it out of the restarting
+         * state if it was in there
+         */
+    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING));
+    svc->time_started = 0;
+    
+        /* running processes require no additional work -- if
+         * they're in the process of exiting, we've ensured
+         * that they will immediately restart on exit, unless
+         * they are ONESHOT
+         */
+    if (svc->flags & SVC_RUNNING) {
+        return;
+    }
+
+    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
+    if (needs_console && (!have_console)) {
+        ERROR("service '%s' requires console\n", svc->name);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
+    if (stat(svc->args[0], &s) != 0) {
+        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
+        svc->flags |= SVC_DISABLED;
+        return;
+    }
+
+    NOTICE("starting '%s'\n", svc->name);
+
+    pid = fork();
+
+    if (pid == 0) {
+        struct socketinfo *si;
+        struct svcenvinfo *ei;
+        char tmp[32];
+        int fd, sz;
+
+        get_property_workspace(&fd, &sz);
+        sprintf(tmp, "%d,%d", dup(fd), sz);
+        add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
+
+        for (ei = svc->envvars; ei; ei = ei->next)
+            add_environment(ei->name, ei->value);
+
+        for (si = svc->sockets; si; si = si->next) {
+            int s = create_socket(si->name,
+                                  !strcmp(si->type, "dgram") ? 
+                                  SOCK_DGRAM : SOCK_STREAM,
+                                  si->perm, si->uid, si->gid);
+            if (s >= 0) {
+                publish_socket(si->name, s);
+            }
+        }
+
+        if (needs_console) {
+            setsid();
+            open_console();
+        } else {
+            zap_stdio();
+        }
+
+#if 0
+        for (n = 0; svc->args[n]; n++) {
+            INFO("args[%d] = '%s'\n", n, svc->args[n]);
+        }
+        for (n = 0; ENV[n]; n++) {
+            INFO("env[%d] = '%s'\n", n, ENV[n]);
+        }
+#endif
+
+        setpgid(0, getpid());
+
+    /* as requested, set our gid, supplemental gids, and uid */
+        if (svc->gid) {
+            setgid(svc->gid);
+        }
+        if (svc->nr_supp_gids) {
+            setgroups(svc->nr_supp_gids, svc->supp_gids);
+        }
+        if (svc->uid) {
+            setuid(svc->uid);
+        }
+
+        execve(svc->args[0], (char**) svc->args, (char**) ENV);
+        _exit(127);
+    }
+
+    if (pid < 0) {
+        ERROR("failed to start '%s'\n", svc->name);
+        svc->pid = 0;
+        return;
+    }
+
+    svc->time_started = gettime();
+    svc->pid = pid;
+    svc->flags |= SVC_RUNNING;
+
+    notify_service_state(svc->name, "running");
+}
+
+void service_stop(struct service *svc)
+{
+        /* we are no longer running, nor should we
+         * attempt to restart
+         */
+    svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING));
+
+        /* if the service has not yet started, prevent
+         * it from auto-starting with its class
+         */
+    svc->flags |= SVC_DISABLED;
+
+    if (svc->pid) {
+        NOTICE("service '%s' is being killed\n", svc->name);
+        kill(-svc->pid, SIGTERM);
+        notify_service_state(svc->name, "stopping");
+    } else {
+        notify_service_state(svc->name, "stopped");
+    }
+}
+
+void property_changed(const char *name, const char *value)
+{
+    if (property_triggers_enabled) {
+        queue_property_triggers(name, value);
+        drain_action_queue();
+    }
+}
+
+#define CRITICAL_CRASH_THRESHOLD    4       /* if we crash >4 times ... */
+#define CRITICAL_CRASH_WINDOW       (4*60)  /* ... in 4 minutes, goto recovery*/
+
+static int wait_for_one_process(int block)
+{
+    pid_t pid;
+    int status;
+    struct service *svc;
+    struct socketinfo *si;
+    time_t now;
+    struct listnode *node;
+    struct command *cmd;
+
+    while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR );
+    if (pid <= 0) return -1;
+    INFO("waitpid returned pid %d, status = %08x\n", pid, status);
+
+    svc = service_find_by_pid(pid);
+    if (!svc) {
+        ERROR("untracked pid %d exited\n", pid);
+        return 0;
+    }
+
+    NOTICE("process '%s', pid %d exited\n", svc->name, pid);
+
+    if (!(svc->flags & SVC_ONESHOT)) {
+        kill(-pid, SIGKILL);
+        NOTICE("process '%s' killing any children in process group\n", svc->name);
+    }
+
+    /* remove any sockets we may have created */
+    for (si = svc->sockets; si; si = si->next) {
+        char tmp[128];
+        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
+        unlink(tmp);
+    }
+
+    svc->pid = 0;
+    svc->flags &= (~SVC_RUNNING);
+
+        /* oneshot processes go into the disabled state on exit */
+    if (svc->flags & SVC_ONESHOT) {
+        svc->flags |= SVC_DISABLED;
+    }
+
+        /* disabled processes do not get restarted automatically */
+    if (svc->flags & SVC_DISABLED) {
+        notify_service_state(svc->name, "stopped");
+        return 0;
+    }
+
+    now = gettime();
+    if (svc->flags & SVC_CRITICAL) {
+        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
+            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
+                ERROR("critical process '%s' exited %d times in %d minutes; "
+                      "rebooting into recovery mode\n", svc->name,
+                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+                sync();
+                __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+                         LINUX_REBOOT_CMD_RESTART2, "recovery");
+                return 0;
+            }
+        } else {
+            svc->time_crashed = now;
+            svc->nr_crashed = 1;
+        }
+    }
+
+    /* Execute all onrestart commands for this service. */
+    list_for_each(node, &svc->onrestart.commands) {
+        cmd = node_to_item(node, struct command, clist);
+        cmd->func(cmd->nargs, cmd->args);
+    }
+    svc->flags |= SVC_RESTARTING;
+    notify_service_state(svc->name, "restarting");
+    return 0;
+}
+
+static void restart_service_if_needed(struct service *svc)
+{
+    time_t next_start_time = svc->time_started + 5;
+
+    if (next_start_time <= gettime()) {
+        svc->flags &= (~SVC_RESTARTING);
+        service_start(svc);
+        return;
+    }
+
+    if ((next_start_time < process_needs_restart) ||
+        (process_needs_restart == 0)) {
+        process_needs_restart = next_start_time;
+    }
+}
+
+static void restart_processes()
+{
+    process_needs_restart = 0;
+    service_for_each_flags(SVC_RESTARTING,
+                           restart_service_if_needed);
+}
+
+static int signal_fd = -1;
+
+static void sigchld_handler(int s)
+{
+    write(signal_fd, &s, 1);
+}
+
+static void msg_start(const char *name)
+{
+    struct service *svc = service_find_by_name(name);
+    
+    if (svc) {
+        service_start(svc);
+    } else {
+        ERROR("no such service '%s'\n", name);
+    }
+}
+
+static void msg_stop(const char *name)
+{
+    struct service *svc = service_find_by_name(name);
+
+    if (svc) {
+        service_stop(svc);
+    } else {
+        ERROR("no such service '%s'\n");
+    }
+}
+
+void handle_control_message(const char *msg, const char *arg)
+{
+    if (!strcmp(msg,"start")) {
+        msg_start(arg);
+    } else if (!strcmp(msg,"stop")) {
+        msg_stop(arg);
+    } else {
+        ERROR("unknown control msg '%s'\n", msg);
+    }
+}
+
+#define MAX_MTD_PARTITIONS 16
+
+static struct {
+    char name[16];
+    int number;
+} mtd_part_map[MAX_MTD_PARTITIONS];
+
+static int mtd_part_count = -1;
+
+static void find_mtd_partitions(void)
+{
+    int fd;
+    char buf[1024];
+    char *pmtdbufp;
+    ssize_t pmtdsize;
+    int r;
+
+    fd = open("/proc/mtd", O_RDONLY);
+    if (fd < 0)
+        return;
+
+    buf[sizeof(buf) - 1] = '\0';
+    pmtdsize = read(fd, buf, sizeof(buf) - 1);
+    pmtdbufp = buf;
+    while (pmtdsize > 0) {
+        int mtdnum, mtdsize, mtderasesize;
+        char mtdname[16];
+        mtdname[0] = '\0';
+        mtdnum = -1;
+        r = sscanf(pmtdbufp, "mtd%d: %x %x %15s",
+                   &mtdnum, &mtdsize, &mtderasesize, mtdname);
+        if ((r == 4) && (mtdname[0] == '"')) {
+            char *x = strchr(mtdname + 1, '"');
+            if (x) {
+                *x = 0;
+            }
+            INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1);
+            if (mtd_part_count < MAX_MTD_PARTITIONS) {
+                strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1);
+                mtd_part_map[mtd_part_count].number = mtdnum;
+                mtd_part_count++;
+            } else {
+                ERROR("too many mtd partitions\n");
+            }
+        }
+        while (pmtdsize > 0 && *pmtdbufp != '\n') {
+            pmtdbufp++;
+            pmtdsize--;
+        }
+        if (pmtdsize > 0) {
+            pmtdbufp++;
+            pmtdsize--;
+        }
+    }
+    close(fd);
+}
+
+int mtd_name_to_number(const char *name) 
+{
+    int n;
+    if (mtd_part_count < 0) {
+        mtd_part_count = 0;
+        find_mtd_partitions();
+    }
+    for (n = 0; n < mtd_part_count; n++) {
+        if (!strcmp(name, mtd_part_map[n].name)) {
+            return mtd_part_map[n].number;
+        }
+    }
+    return -1;
+}
+
+static void import_kernel_nv(char *name, int in_qemu)
+{
+    char *value = strchr(name, '=');
+
+    if (value == 0) return;
+    *value++ = 0;
+    if (*name == 0) return;
+
+    if (!in_qemu)
+    {
+        /* on a real device, white-list the kernel options */
+        if (!strcmp(name,"qemu")) {
+            strlcpy(qemu, value, sizeof(qemu));
+        } else if (!strcmp(name,"androidboot.console")) {
+            strlcpy(console, value, sizeof(console));
+        } else if (!strcmp(name,"androidboot.mode")) {
+            strlcpy(bootmode, value, sizeof(bootmode));
+        } else if (!strcmp(name,"androidboot.serialno")) {
+            strlcpy(serialno, value, sizeof(serialno));
+        } else if (!strcmp(name,"androidboot.baseband")) {
+            strlcpy(baseband, value, sizeof(baseband));
+        } else if (!strcmp(name,"androidboot.carrier")) {
+            strlcpy(carrier, value, sizeof(carrier));
+        } else if (!strcmp(name,"androidboot.bootloader")) {
+            strlcpy(bootloader, value, sizeof(bootloader));
+        } else if (!strcmp(name,"androidboot.hardware")) {
+            strlcpy(hardware, value, sizeof(hardware));
+        } else {
+            qemu_cmdline(name, value);
+        }
+    } else {
+        /* in the emulator, export any kernel option with the
+         * ro.kernel. prefix */
+        char  buff[32];
+        int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
+        if (len < (int)sizeof(buff)) {
+            property_set( buff, value );
+        }
+    }
+}
+
+static void import_kernel_cmdline(int in_qemu)
+{
+    char cmdline[1024];
+    char *ptr;
+    int fd;
+
+    fd = open("/proc/cmdline", O_RDONLY);
+    if (fd >= 0) {
+        int n = read(fd, cmdline, 1023);
+        if (n < 0) n = 0;
+
+        /* get rid of trailing newline, it happens */
+        if (n > 0 && cmdline[n-1] == '\n') n--;
+
+        cmdline[n] = 0;
+        close(fd);
+    } else {
+        cmdline[0] = 0;
+    }
+
+    ptr = cmdline;
+    while (ptr && *ptr) {
+        char *x = strchr(ptr, ' ');
+        if (x != 0) *x++ = 0;
+        import_kernel_nv(ptr, in_qemu);
+        ptr = x;
+    }
+
+        /* don't expose the raw commandline to nonpriv processes */
+    chmod("/proc/cmdline", 0440);
+}
+
+static void get_hardware_name(void)
+{
+    char data[1024];
+    int fd, n;
+    char *x, *hw, *rev;
+
+    /* Hardware string was provided on kernel command line */
+    if (hardware[0])
+        return;
+
+    fd = open("/proc/cpuinfo", O_RDONLY);
+    if (fd < 0) return;
+
+    n = read(fd, data, 1023);
+    close(fd);
+    if (n < 0) return;
+
+    data[n] = 0;
+    hw = strstr(data, "\nHardware");
+    rev = strstr(data, "\nRevision");
+
+    if (hw) {
+        x = strstr(hw, ": ");
+        if (x) {
+            x += 2;
+            n = 0;
+            while (*x && !isspace(*x)) {
+                hardware[n++] = tolower(*x);
+                x++;
+                if (n == 31) break;
+            }
+            hardware[n] = 0;
+        }
+    }
+
+    if (rev) {
+        x = strstr(rev, ": ");
+        if (x) {
+            revision = strtoul(x + 2, 0, 16);
+        }
+    }
+}
+
+static void drain_action_queue(void)
+{
+    struct listnode *node;
+    struct command *cmd;
+    struct action *act;
+    int ret;
+
+    while ((act = action_remove_queue_head())) {
+        INFO("processing action %p (%s)\n", act, act->name);
+        list_for_each(node, &act->commands) {
+            cmd = node_to_item(node, struct command, clist);
+            ret = cmd->func(cmd->nargs, cmd->args);
+            INFO("command '%s' r=%d\n", cmd->args[0], ret);
+        }
+    }
+}
+
+void open_devnull_stdio(void)
+{
+    int fd;
+    static const char *name = "/dev/__null__";
+    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
+        fd = open(name, O_RDWR);
+        unlink(name);
+        if (fd >= 0) {
+            dup2(fd, 0);
+            dup2(fd, 1);
+            dup2(fd, 2);
+            if (fd > 2) {
+                close(fd);
+            }
+            return;
+        }
+    }
+
+    exit(1);
+}
+
+void add_service_keycodes(struct service *svc)
+{
+    struct input_keychord *keychord;
+    int i, size;
+
+    if (svc->keycodes) {
+        /* add a new keychord to the list */
+        size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+        keychords = realloc(keychords, keychords_length + size);
+        if (!keychords) {
+            ERROR("could not allocate keychords\n");
+            keychords_length = 0;
+            keychords_count = 0;
+            return;
+        }
+
+        keychord = (struct input_keychord *)((char *)keychords + keychords_length);
+        keychord->version = KEYCHORD_VERSION;
+        keychord->id = keychords_count + 1;
+        keychord->count = svc->nkeycodes;
+        svc->keychord_id = keychord->id;
+
+        for (i = 0; i < svc->nkeycodes; i++) {
+            keychord->keycodes[i] = svc->keycodes[i];
+        }
+        keychords_count++;
+        keychords_length += size;
+    }
+}
+
+int open_keychord()
+{
+    int fd, ret;
+
+    service_for_each(add_service_keycodes);
+    
+    /* nothing to do if no services require keychords */
+    if (!keychords)
+        return -1;
+
+    fd = open("/dev/keychord", O_RDWR);
+    if (fd < 0) {
+        ERROR("could not open /dev/keychord\n");
+        return fd;
+    }
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+    ret = write(fd, keychords, keychords_length);
+    if (ret != keychords_length) {
+        ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno);
+        close(fd);
+        fd = -1;
+    }
+
+    free(keychords);
+    keychords = 0;
+
+    return fd;
+}
+
+void handle_keychord(int fd)
+{
+    struct service *svc;
+    int ret;
+    __u16 id;
+
+    ret = read(fd, &id, sizeof(id));
+    if (ret != sizeof(id)) {
+        ERROR("could not read keychord id\n");
+        return;
+    }
+
+    svc = service_find_by_keychord(id);
+    if (svc) {
+        INFO("starting service %s from keychord\n", svc->name);
+        service_start(svc);   
+    } else {
+        ERROR("service for keychord %d not found\n", id);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    int device_fd = -1;
+    int property_set_fd = -1;
+    int signal_recv_fd = -1;
+    int keychord_fd = -1;
+    int fd_count;
+    int s[2];
+    int fd;
+    struct sigaction act;
+    char tmp[PROP_VALUE_MAX];
+    struct pollfd ufds[4];
+    char *tmpdev;
+    char* debuggable;
+
+    act.sa_handler = sigchld_handler;
+    act.sa_flags = SA_NOCLDSTOP;
+    act.sa_mask = 0;
+    act.sa_restorer = NULL;
+    sigaction(SIGCHLD, &act, 0);
+
+    /* clear the umask */
+    umask(0);
+
+        /* Get the basic filesystem setup we need put
+         * together in the initramdisk on / and then we'll
+         * let the rc file figure out the rest.
+         */
+    mkdir("/dev", 0755);
+    mkdir("/proc", 0755);
+    mkdir("/sys", 0755);
+
+    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
+    mkdir("/dev/pts", 0755);
+    mkdir("/dev/socket", 0755);
+    mount("devpts", "/dev/pts", "devpts", 0, NULL);
+    mount("proc", "/proc", "proc", 0, NULL);
+    mount("sysfs", "/sys", "sysfs", 0, NULL);
+
+        /* We must have some place other than / to create the
+         * device nodes for kmsg and null, otherwise we won't
+         * be able to remount / read-only later on.
+         * Now that tmpfs is mounted on /dev, we can actually
+         * talk to the outside world.
+         */
+    open_devnull_stdio();
+    log_init();
+    
+    INFO("reading config file\n");
+    parse_config_file("/init.rc");
+
+    /* pull the kernel commandline and ramdisk properties file in */
+    qemu_init();
+    import_kernel_cmdline(0);
+
+    get_hardware_name();
+    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
+    parse_config_file(tmp);
+
+    action_for_each_trigger("early-init", action_add_queue_tail);
+    drain_action_queue();
+
+    INFO("device init\n");
+    device_fd = device_init();
+
+    property_init();
+    
+    // only listen for keychords if ro.debuggable is true
+    debuggable = property_get("ro.debuggable");
+    if (debuggable && !strcmp(debuggable, "1")) {
+        keychord_fd = open_keychord();
+    }
+
+    if (console[0]) {
+        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
+        console_name = strdup(tmp);
+    }
+
+    fd = open(console_name, O_RDWR);
+    if (fd >= 0)
+        have_console = 1;
+    close(fd);
+
+    if( load_565rle_image(INIT_IMAGE_FILE) ) {
+    fd = open("/dev/tty0", O_WRONLY);
+    if (fd >= 0) {
+        const char *msg;
+            msg = "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"  // console is 40 cols x 30 lines
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "\n"
+        "             A N D R O I D ";
+        write(fd, msg, strlen(msg));
+        close(fd);
+    }
+    }
+
+    if (qemu[0])
+        import_kernel_cmdline(1); 
+
+    if (!strcmp(bootmode,"factory"))
+        property_set("ro.factorytest", "1");
+    else if (!strcmp(bootmode,"factory2"))
+        property_set("ro.factorytest", "2");
+    else
+        property_set("ro.factorytest", "0");
+
+    property_set("ro.serialno", serialno[0] ? serialno : "");
+    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
+    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
+    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
+    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
+
+    property_set("ro.hardware", hardware);
+    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
+    property_set("ro.revision", tmp);
+
+        /* execute all the boot actions to get us started */
+    action_for_each_trigger("init", action_add_queue_tail);
+    drain_action_queue();
+
+        /* read any property files on system or data and
+         * fire up the property service.  This must happen
+         * after the ro.foo properties are set above so
+         * that /data/local.prop cannot interfere with them.
+         */
+    property_set_fd = start_property_service();
+
+    /* create a signalling mechanism for the sigchld handler */
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
+        signal_fd = s[0];
+        signal_recv_fd = s[1];
+        fcntl(s[0], F_SETFD, FD_CLOEXEC);
+        fcntl(s[0], F_SETFL, O_NONBLOCK);
+        fcntl(s[1], F_SETFD, FD_CLOEXEC);
+        fcntl(s[1], F_SETFL, O_NONBLOCK);
+    }
+
+    /* make sure we actually have all the pieces we need */
+    if ((device_fd < 0) ||
+        (property_set_fd < 0) ||
+        (signal_recv_fd < 0)) {
+        ERROR("init startup failure\n");
+        return 1;
+    }
+
+    /* execute all the boot actions to get us started */
+    action_for_each_trigger("early-boot", action_add_queue_tail);
+    action_for_each_trigger("boot", action_add_queue_tail);
+    drain_action_queue();
+
+        /* run all property triggers based on current state of the properties */
+    queue_all_property_triggers();
+    drain_action_queue();
+
+        /* enable property triggers */   
+    property_triggers_enabled = 1;     
+
+    ufds[0].fd = device_fd;
+    ufds[0].events = POLLIN;
+    ufds[1].fd = property_set_fd;
+    ufds[1].events = POLLIN;
+    ufds[2].fd = signal_recv_fd;
+    ufds[2].events = POLLIN;
+    fd_count = 3;
+
+    if (keychord_fd > 0) {
+        ufds[3].fd = keychord_fd;
+        ufds[3].events = POLLIN;
+        fd_count++;
+    } else {
+        ufds[3].events = 0;
+        ufds[3].revents = 0;
+    }
+
+#if BOOTCHART
+    bootchart_count = bootchart_init();
+    if (bootchart_count < 0) {
+        ERROR("bootcharting init failure\n");
+    } else if (bootchart_count > 0) {
+        NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS);
+    } else {
+        NOTICE("bootcharting ignored\n");
+    }
+#endif
+
+    for(;;) {
+        int nr, i, timeout = -1;
+
+        for (i = 0; i < fd_count; i++)
+            ufds[i].revents = 0;
+
+        drain_action_queue();
+        restart_processes();
+
+        if (process_needs_restart) {
+            timeout = (process_needs_restart - gettime()) * 1000;
+            if (timeout < 0)
+                timeout = 0;
+        }
+
+#if BOOTCHART
+        if (bootchart_count > 0) {
+            if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)
+                timeout = BOOTCHART_POLLING_MS;
+            if (bootchart_step() < 0 || --bootchart_count == 0) {
+                bootchart_finish();
+                bootchart_count = 0;
+            }
+        }
+#endif
+        nr = poll(ufds, fd_count, timeout);
+        if (nr <= 0)
+            continue;
+
+        if (ufds[2].revents == POLLIN) {
+            /* we got a SIGCHLD - reap and restart as needed */
+            read(signal_recv_fd, tmp, sizeof(tmp));
+            while (!wait_for_one_process(0))
+                ;
+            continue;
+        }
+
+        if (ufds[0].revents == POLLIN)
+            handle_device_fd(device_fd);
+
+        if (ufds[1].revents == POLLIN)
+            handle_property_set_fd(property_set_fd);
+        if (ufds[3].revents == POLLIN)
+            handle_keychord(keychord_fd);
+    }
+
+    return 0;
+}
diff --git a/init/init.h b/init/init.h
new file mode 100644
index 0000000..b686869
--- /dev/null
+++ b/init/init.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_INIT_H
+#define _INIT_INIT_H
+
+int mtd_name_to_number(const char *name);
+
+void handle_control_message(const char *msg, const char *arg);
+
+int create_socket(const char *name, int type, mode_t perm,
+                  uid_t uid, gid_t gid);
+
+void *read_file(const char *fn, unsigned *_sz);
+
+void log_init(void);
+void log_set_level(int level);
+void log_close(void);
+void log_write(int level, const char *fmt, ...);
+
+#define ERROR(x...)   log_write(3, "<3>init: " x)
+#define NOTICE(x...)  log_write(5, "<5>init: " x)
+#define INFO(x...)    log_write(6, "<6>init: " x)
+
+#define LOG_DEFAULT_LEVEL  3  /* messages <= this level are logged */
+#define LOG_UEVENTS        0  /* log uevent messages if 1. verbose */
+
+unsigned int decode_uid(const char *s);
+
+struct listnode
+{
+    struct listnode *next;
+    struct listnode *prev;
+};
+
+#define node_to_item(node, container, member) \
+    (container *) (((char*) (node)) - offsetof(container, member))
+
+#define list_declare(name) \
+    struct listnode name = { \
+        .next = &name, \
+        .prev = &name, \
+    }
+
+#define list_for_each(node, list) \
+    for (node = (list)->next; node != (list); node = node->next)
+
+void list_init(struct listnode *list);
+void list_add_tail(struct listnode *list, struct listnode *item);
+void list_remove(struct listnode *item);
+
+#define list_empty(list) ((list) == (list)->next)
+#define list_head(list) ((list)->next)
+#define list_tail(list) ((list)->prev)
+
+struct command
+{
+        /* list of commands in an action */
+    struct listnode clist;
+
+    int (*func)(int nargs, char **args);
+    int nargs;
+    char *args[1];
+};
+    
+struct action {
+        /* node in list of all actions */
+    struct listnode alist;
+        /* node in the queue of pending actions */
+    struct listnode qlist;
+        /* node in list of actions for a trigger */
+    struct listnode tlist;
+
+    unsigned hash;
+    const char *name;
+    
+    struct listnode commands;
+    struct command *current;
+};
+
+struct socketinfo {
+    struct socketinfo *next;
+    const char *name;
+    const char *type;
+    uid_t uid;
+    gid_t gid;
+    int perm;
+};
+
+struct svcenvinfo {
+    struct svcenvinfo *next;
+    const char *name;
+    const char *value;
+};
+
+#define SVC_DISABLED    0x01  /* do not autostart with class */
+#define SVC_ONESHOT     0x02  /* do not restart on exit */
+#define SVC_RUNNING     0x04  /* currently active */
+#define SVC_RESTARTING  0x08  /* waiting to restart */
+#define SVC_CONSOLE     0x10  /* requires console */
+#define SVC_CRITICAL    0x20  /* will reboot into recovery if keeps crashing */
+
+#define NR_SVC_SUPP_GIDS 6    /* six supplementary groups */
+
+struct service {
+        /* list of all services */
+    struct listnode slist;
+
+    const char *name;
+    const char *classname;
+
+    unsigned flags;
+    pid_t pid;
+    time_t time_started;    /* time of last start */
+    time_t time_crashed;    /* first crash within inspection window */
+    int nr_crashed;         /* number of times crashed within window */
+    
+    uid_t uid;
+    gid_t gid;
+    gid_t supp_gids[NR_SVC_SUPP_GIDS];
+    size_t nr_supp_gids;
+
+    struct socketinfo *sockets;
+    struct svcenvinfo *envvars;
+
+    int nargs;
+    char *args[1];
+    struct action onrestart;  /* Actions to execute on restart. */
+    
+    /* keycodes for triggering this service via /dev/keychord */
+    int *keycodes;
+    int nkeycodes;
+    int keychord_id;
+};
+
+int parse_config_file(const char *fn);
+
+struct service *service_find_by_name(const char *name);
+struct service *service_find_by_pid(pid_t pid);
+struct service *service_find_by_keychord(int keychord_id);
+void service_for_each(void (*func)(struct service *svc));
+void service_for_each_class(const char *classname,
+                            void (*func)(struct service *svc));
+void service_for_each_flags(unsigned matchflags,
+                            void (*func)(struct service *svc));
+void service_stop(struct service *svc);
+void service_start(struct service *svc);
+void property_changed(const char *name, const char *value);
+
+struct action *action_remove_queue_head(void);
+void action_add_queue_tail(struct action *act);
+void action_for_each_trigger(const char *trigger,
+                             void (*func)(struct action *act));
+void queue_property_triggers(const char *name, const char *value);
+void queue_all_property_triggers();
+
+#define INIT_IMAGE_FILE	"/initlogo.rle"
+
+int load_565rle_image( char *file_name );
+
+#endif	/* _INIT_INIT_H */
diff --git a/init/keywords.h b/init/keywords.h
new file mode 100644
index 0000000..6f47379
--- /dev/null
+++ b/init/keywords.h
@@ -0,0 +1,78 @@
+
+#ifndef KEYWORD
+int do_class_start(int nargs, char **args);
+int do_class_stop(int nargs, char **args);
+int do_domainname(int nargs, char **args);
+int do_exec(int nargs, char **args);
+int do_export(int nargs, char **args);
+int do_hostname(int nargs, char **args);
+int do_ifup(int nargs, char **args);
+int do_insmod(int nargs, char **args);
+int do_import(int nargs, char **args);
+int do_mkdir(int nargs, char **args);
+int do_mount(int nargs, char **args);
+int do_restart(int nargs, char **args);
+int do_setkey(int nargs, char **args);
+int do_setprop(int nargs, char **args);
+int do_setrlimit(int nargs, char **args);
+int do_start(int nargs, char **args);
+int do_stop(int nargs, char **args);
+int do_trigger(int nargs, char **args);
+int do_symlink(int nargs, char **args);
+int do_sysclktz(int nargs, char **args);
+int do_write(int nargs, char **args);
+int do_chown(int nargs, char **args);
+int do_chmod(int nargs, char **args);
+int do_loglevel(int nargs, char **args);
+int do_device(int nargs, char **args);
+#define __MAKE_KEYWORD_ENUM__
+#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
+enum {
+    K_UNKNOWN,
+#endif
+    KEYWORD(capability,  OPTION,  0, 0)
+    KEYWORD(class,       OPTION,  0, 0)
+    KEYWORD(class_start, COMMAND, 1, do_class_start)
+    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
+    KEYWORD(console,     OPTION,  0, 0)
+    KEYWORD(critical,    OPTION,  0, 0)
+    KEYWORD(disabled,    OPTION,  0, 0)
+    KEYWORD(domainname,  COMMAND, 1, do_domainname)
+    KEYWORD(exec,        COMMAND, 1, do_exec)
+    KEYWORD(export,      COMMAND, 2, do_export)
+    KEYWORD(group,       OPTION,  0, 0)
+    KEYWORD(hostname,    COMMAND, 1, do_hostname)
+    KEYWORD(ifup,        COMMAND, 1, do_ifup)
+    KEYWORD(insmod,      COMMAND, 1, do_insmod)
+    KEYWORD(import,      COMMAND, 1, do_import)
+    KEYWORD(keycodes,    OPTION,  0, 0)
+    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
+    KEYWORD(mount,       COMMAND, 3, do_mount)
+    KEYWORD(on,          SECTION, 0, 0)
+    KEYWORD(oneshot,     OPTION,  0, 0)
+    KEYWORD(onrestart,   OPTION,  0, 0)
+    KEYWORD(restart,     COMMAND, 1, do_restart)
+    KEYWORD(service,     SECTION, 0, 0)
+    KEYWORD(setenv,      OPTION,  2, 0)
+    KEYWORD(setkey,      COMMAND, 0, do_setkey)
+    KEYWORD(setprop,     COMMAND, 2, do_setprop)
+    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
+    KEYWORD(socket,      OPTION,  0, 0)
+    KEYWORD(start,       COMMAND, 1, do_start)
+    KEYWORD(stop,        COMMAND, 1, do_stop)
+    KEYWORD(trigger,     COMMAND, 1, do_trigger)
+    KEYWORD(symlink,     COMMAND, 1, do_symlink)
+    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
+    KEYWORD(user,        OPTION,  0, 0)
+    KEYWORD(write,       COMMAND, 2, do_write)
+    KEYWORD(chown,       COMMAND, 2, do_chown)
+    KEYWORD(chmod,       COMMAND, 2, do_chmod)
+    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
+    KEYWORD(device,      COMMAND, 4, do_device)
+#ifdef __MAKE_KEYWORD_ENUM__
+    KEYWORD_COUNT,
+};
+#undef __MAKE_KEYWORD_ENUM__
+#undef KEYWORD
+#endif
+
diff --git a/init/logo.c b/init/logo.c
new file mode 100644
index 0000000..6a740bf
--- /dev/null
+++ b/init/logo.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+#include "init.h"
+
+#ifdef ANDROID
+#include <cutils/memory.h>
+#else
+void android_memset16(void *_ptr, unsigned short val, unsigned count)
+{
+    unsigned short *ptr = _ptr;
+    count >>= 1;
+    while(count--)
+        *ptr++ = val;
+}
+#endif
+
+struct FB {
+    unsigned short *bits;
+    unsigned size;
+    int fd;
+    struct fb_fix_screeninfo fi;
+    struct fb_var_screeninfo vi;
+};
+
+#define fb_width(fb) ((fb)->vi.xres)
+#define fb_height(fb) ((fb)->vi.yres)
+#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2)
+
+static int fb_open(struct FB *fb)
+{
+    fb->fd = open("/dev/graphics/fb0", O_RDWR);
+    if (fb->fd < 0)
+        return -1;
+
+    if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0)
+        goto fail;
+    if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0)
+        goto fail;
+
+    fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, 
+                    MAP_SHARED, fb->fd, 0);
+    if (fb->bits == MAP_FAILED)
+        goto fail;
+
+    return 0;
+
+fail:
+    close(fb->fd);
+    return -1;
+}
+
+static void fb_close(struct FB *fb)
+{
+    munmap(fb->bits, fb_size(fb));
+    close(fb->fd);
+}
+
+/* there's got to be a more portable way to do this ... */
+static void fb_update(struct FB *fb)
+{
+    fb->vi.yoffset = 1;
+    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
+    fb->vi.yoffset = 0;
+    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
+}
+
+static int vt_set_mode(int graphics)
+{
+    int fd, r;
+    fd = open("/dev/tty0", O_RDWR | O_SYNC);
+    if (fd < 0)
+        return -1;
+    r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));
+    close(fd);
+    return r;
+}
+
+/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */
+
+int load_565rle_image(char *fn)
+{
+    struct FB fb;
+    struct stat s;
+    unsigned short *data, *bits, *ptr;
+    unsigned count, max;
+    int fd;
+
+    if (vt_set_mode(1)) 
+        return -1;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+        ERROR("cannot open '%s'\n", fn);
+        goto fail_restore_text;
+    }
+
+    if (fstat(fd, &s) < 0) {
+        goto fail_close_file;
+    }
+
+    data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
+    if (data == MAP_FAILED)
+        goto fail_close_file;
+
+    if (fb_open(&fb))
+        goto fail_unmap_data;
+
+    max = fb_width(&fb) * fb_height(&fb);
+    ptr = data;
+    count = s.st_size;
+    bits = fb.bits;
+    while (count > 3) {
+        unsigned n = ptr[0];
+        if (n > max)
+            break;
+        android_memset16(bits, ptr[1], n << 1);
+        bits += n;
+        max -= n;
+        ptr += 2;
+        count -= 4;
+    }
+
+    munmap(data, s.st_size);
+    fb_update(&fb);
+    fb_close(&fb);
+    close(fd);
+    unlink(fn);
+    return 0;
+
+fail_unmap_data:
+    munmap(data, s.st_size);    
+fail_close_file:
+    close(fd);
+fail_restore_text:
+    vt_set_mode(0);
+    return -1;
+}
+
diff --git a/init/parser.c b/init/parser.c
new file mode 100644
index 0000000..6a22d24
--- /dev/null
+++ b/init/parser.c
@@ -0,0 +1,797 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stddef.h>
+#include <ctype.h>
+
+#include "init.h"
+#include "property_service.h"
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static list_declare(service_list);
+static list_declare(action_list);
+static list_declare(action_queue);
+
+#define RAW(x...) log_write(6, x)
+
+void DUMP(void)
+{
+#if 0
+    struct service *svc;
+    struct action *act;
+    struct command *cmd;
+    struct listnode *node;
+    struct listnode *node2;
+    struct socketinfo *si;
+    int n;
+    
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        RAW("service %s\n", svc->name);
+        RAW("  class '%s'\n", svc->classname);
+        RAW("  exec");
+        for (n = 0; n < svc->nargs; n++) {
+            RAW(" '%s'", svc->args[n]);
+        }
+        RAW("\n");
+        for (si = svc->sockets; si; si = si->next) {
+            RAW("  socket %s %s 0%o\n", si->name, si->type, si->perm);
+        }
+    }
+
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        RAW("on %s\n", act->name);
+        list_for_each(node2, &act->commands) {
+            cmd = node_to_item(node2, struct command, clist);
+            RAW("  %p", cmd->func);
+            for (n = 0; n < cmd->nargs; n++) {
+                RAW(" %s", cmd->args[n]);
+            }
+            RAW("\n");
+        }
+        RAW("\n");
+    }
+#endif       
+}
+
+#define MAXARGS 64
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_NEWLINE 2
+
+struct parse_state
+{
+    char *ptr;
+    char *text;
+    int line;
+    int nexttoken;
+    void *context;
+    void (*parse_line)(struct parse_state *state, int nargs, char **args);
+    const char *filename;
+};
+
+static void *parse_service(struct parse_state *state, int nargs, char **args);
+static void parse_line_service(struct parse_state *state, int nargs, char **args);
+
+static void *parse_action(struct parse_state *state, int nargs, char **args);
+static void parse_line_action(struct parse_state *state, int nargs, char **args);
+
+void parse_error(struct parse_state *state, const char *fmt, ...)
+{
+    va_list ap;
+    char buf[128];
+    int off;
+    
+    snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+    buf[127] = 0;
+    off = strlen(buf);
+
+    va_start(ap, fmt);
+    vsnprintf(buf + off, 128 - off, fmt, ap);
+    va_end(ap);
+    buf[127] = 0;
+    ERROR("%s", buf);
+}
+
+#define SECTION 0x01
+#define COMMAND 0x02
+#define OPTION  0x04
+
+#include "keywords.h"
+
+#define KEYWORD(symbol, flags, nargs, func) \
+    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
+
+struct {
+    const char *name;
+    int (*func)(int nargs, char **args);
+    unsigned char nargs;
+    unsigned char flags;
+} keyword_info[KEYWORD_COUNT] = {
+    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
+#include "keywords.h"    
+};
+#undef KEYWORD
+
+#define kw_is(kw, type) (keyword_info[kw].flags & (type))
+#define kw_name(kw) (keyword_info[kw].name)
+#define kw_func(kw) (keyword_info[kw].func)
+#define kw_nargs(kw) (keyword_info[kw].nargs)
+
+int lookup_keyword(const char *s)
+{
+    switch (*s++) {
+    case 'c':
+        if (!strcmp(s, "apability")) return K_capability;
+        if (!strcmp(s, "lass")) return K_class;
+        if (!strcmp(s, "lass_start")) return K_class_start;
+        if (!strcmp(s, "lass_stop")) return K_class_stop;
+        if (!strcmp(s, "onsole")) return K_console;
+        if (!strcmp(s, "hown")) return K_chown;
+        if (!strcmp(s, "hmod")) return K_chmod;
+        if (!strcmp(s, "ritical")) return K_critical;
+        break;
+    case 'd':
+        if (!strcmp(s, "isabled")) return K_disabled;
+        if (!strcmp(s, "omainname")) return K_domainname;
+        if (!strcmp(s, "evice")) return K_device;
+        break;
+    case 'e':
+        if (!strcmp(s, "xec")) return K_exec;
+        if (!strcmp(s, "xport")) return K_export;
+        break;
+    case 'g':
+        if (!strcmp(s, "roup")) return K_group;
+        break;
+    case 'h':
+        if (!strcmp(s, "ostname")) return K_hostname;
+        break;
+    case 'i':
+        if (!strcmp(s, "fup")) return K_ifup;
+        if (!strcmp(s, "nsmod")) return K_insmod;
+        if (!strcmp(s, "mport")) return K_import;
+        break;
+    case 'k':
+        if (!strcmp(s, "eycodes")) return K_keycodes;
+        break;
+    case 'l':
+        if (!strcmp(s, "oglevel")) return K_loglevel;
+        break;
+    case 'm':
+        if (!strcmp(s, "kdir")) return K_mkdir;
+        if (!strcmp(s, "ount")) return K_mount;
+        break;
+    case 'o':
+        if (!strcmp(s, "n")) return K_on;
+        if (!strcmp(s, "neshot")) return K_oneshot;
+        if (!strcmp(s, "nrestart")) return K_onrestart;
+        break;
+    case 'r':
+        if (!strcmp(s, "estart")) return K_restart;
+        break;
+    case 's':
+        if (!strcmp(s, "ervice")) return K_service;
+        if (!strcmp(s, "etenv")) return K_setenv;
+        if (!strcmp(s, "etkey")) return K_setkey;
+        if (!strcmp(s, "etprop")) return K_setprop;
+        if (!strcmp(s, "etrlimit")) return K_setrlimit;
+        if (!strcmp(s, "ocket")) return K_socket;
+        if (!strcmp(s, "tart")) return K_start;
+        if (!strcmp(s, "top")) return K_stop;
+        if (!strcmp(s, "ymlink")) return K_symlink;
+        if (!strcmp(s, "ysclktz")) return K_sysclktz;
+        break;
+    case 't':
+        if (!strcmp(s, "rigger")) return K_trigger;
+        break;
+    case 'u':
+        if (!strcmp(s, "ser")) return K_user;
+        break;
+    case 'w':
+        if (!strcmp(s, "rite")) return K_write;
+        break;
+    }
+    return K_UNKNOWN;
+}
+
+void parse_line_no_op(struct parse_state *state, int nargs, char **args)
+{
+}
+
+int next_token(struct parse_state *state)
+{
+    char *x = state->ptr;
+    char *s;
+
+    if (state->nexttoken) {
+        int t = state->nexttoken;
+        state->nexttoken = 0;
+        return t;
+    }
+
+    for (;;) {
+        switch (*x) {
+        case 0:
+            state->ptr = x;
+            return T_EOF;
+        case '\n':
+            state->line++;
+            x++;
+            state->ptr = x;
+            return T_NEWLINE;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            continue;
+        case '#':
+            while (*x && (*x != '\n')) x++;
+            state->line++;
+            state->ptr = x;
+            return T_NEWLINE;
+        default:
+            goto text;
+        }
+    }
+
+textdone:
+    state->ptr = x;
+    *s = 0;
+    return T_TEXT;
+text:
+    state->text = s = x;
+textresume:
+    for (;;) {
+        switch (*x) {
+        case 0:
+            goto textdone;
+        case ' ':
+        case '\t':
+        case '\r':
+            x++;
+            goto textdone;
+        case '\n':
+            state->nexttoken = T_NEWLINE;
+            x++;
+            goto textdone;
+        case '"':
+            x++;
+            for (;;) {
+                switch (*x) {
+                case 0:
+                        /* unterminated quoted thing */
+                    state->ptr = x;
+                    return T_EOF;
+                case '"':
+                    x++;
+                    goto textresume;
+                default:
+                    *s++ = *x++;
+                }
+            }
+            break;
+        case '\\':
+            x++;
+            switch (*x) {
+            case 0:
+                goto textdone;
+            case 'n':
+                *s++ = '\n';
+                break;
+            case 'r':
+                *s++ = '\r';
+                break;
+            case 't':
+                *s++ = '\t';
+                break;
+            case '\\':
+                *s++ = '\\';
+                break;
+            case '\r':
+                    /* \ <cr> <lf> -> line continuation */
+                if (x[1] != '\n') {
+                    x++;
+                    continue;
+                }
+            case '\n':
+                    /* \ <lf> -> line continuation */
+                state->line++;
+                x++;
+                    /* eat any extra whitespace */
+                while((*x == ' ') || (*x == '\t')) x++;
+                continue;
+            default:
+                    /* unknown escape -- just copy */
+                *s++ = *x++;
+            }
+            continue;
+        default:
+            *s++ = *x++;
+        }
+    }
+    return T_EOF;
+}
+
+void parse_line(int nargs, char **args)
+{
+    int n;
+    int id = lookup_keyword(args[0]);
+    printf("%s(%d)", args[0], id);
+    for (n = 1; n < nargs; n++) {
+        printf(" '%s'", args[n]);
+    }
+    printf("\n");
+}
+
+void parse_new_section(struct parse_state *state, int kw,
+                       int nargs, char **args)
+{
+    printf("[ %s %s ]\n", args[0],
+           nargs > 1 ? args[1] : "");
+    switch(kw) {
+    case K_service:
+        state->context = parse_service(state, nargs, args);
+        if (state->context) {
+            state->parse_line = parse_line_service;
+            return;
+        }
+        break;
+    case K_on:
+        state->context = parse_action(state, nargs, args);
+        if (state->context) {
+            state->parse_line = parse_line_action;
+            return;
+        }
+        break;
+    }
+    state->parse_line = parse_line_no_op;
+}
+
+static void parse_config(const char *fn, char *s)
+{
+    struct parse_state state;
+    char *args[MAXARGS];
+    int nargs;
+
+    nargs = 0;
+    state.filename = fn;
+    state.line = 1;
+    state.ptr = s;
+    state.nexttoken = 0;
+    state.parse_line = parse_line_no_op;
+    for (;;) {
+        switch (next_token(&state)) {
+        case T_EOF:
+            state.parse_line(&state, 0, 0);
+            return;
+        case T_NEWLINE:
+            if (nargs) {
+                int kw = lookup_keyword(args[0]);
+                if (kw_is(kw, SECTION)) {
+                    state.parse_line(&state, 0, 0);
+                    parse_new_section(&state, kw, nargs, args);
+                } else {
+                    state.parse_line(&state, nargs, args);
+                }
+                nargs = 0;
+            }
+            break;
+        case T_TEXT:
+            if (nargs < MAXARGS) {
+                args[nargs++] = state.text;
+            }
+            break;
+        }
+    }
+}
+
+int parse_config_file(const char *fn)
+{
+    char *data;
+    data = read_file(fn, 0);
+    if (!data) return -1;
+
+    parse_config(fn, data);
+    DUMP();
+    return 0;
+}
+
+static int valid_name(const char *name)
+{
+    if (strlen(name) > 16) {
+        return 0;
+    }
+    while (*name) {
+        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
+            return 0;
+        }
+        name++;
+    }
+    return 1;
+}
+
+struct service *service_find_by_name(const char *name)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (!strcmp(svc->name, name)) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+struct service *service_find_by_pid(pid_t pid)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->pid == pid) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+struct service *service_find_by_keychord(int keychord_id)
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->keychord_id == keychord_id) {
+            return svc;
+        }
+    }
+    return 0;
+}
+
+void service_for_each(void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        func(svc);
+    }
+}
+
+void service_for_each_class(const char *classname,
+                            void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (!strcmp(svc->classname, classname)) {
+            func(svc);
+        }
+    }
+}
+
+void service_for_each_flags(unsigned matchflags,
+                            void (*func)(struct service *svc))
+{
+    struct listnode *node;
+    struct service *svc;
+    list_for_each(node, &service_list) {
+        svc = node_to_item(node, struct service, slist);
+        if (svc->flags & matchflags) {
+            func(svc);
+        }
+    }
+}
+
+void action_for_each_trigger(const char *trigger,
+                             void (*func)(struct action *act))
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strcmp(act->name, trigger)) {
+            func(act);
+        }
+    }
+}
+
+void queue_property_triggers(const char *name, const char *value)
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strncmp(act->name, "property:", strlen("property:"))) {
+            const char *test = act->name + strlen("property:");
+            int name_length = strlen(name);
+            
+            if (!strncmp(name, test, name_length) && 
+                    test[name_length] == '=' &&
+                    !strcmp(test + name_length + 1, value)) {
+                action_add_queue_tail(act);
+            }
+        }
+    }
+}
+
+void queue_all_property_triggers()
+{
+    struct listnode *node;
+    struct action *act;
+    list_for_each(node, &action_list) {
+        act = node_to_item(node, struct action, alist);
+        if (!strncmp(act->name, "property:", strlen("property:"))) {
+            /* parse property name and value
+               syntax is property:<name>=<value> */
+            const char* name = act->name + strlen("property:");
+            const char* equals = strchr(name, '=');
+            if (equals) {
+                char* prop_name[PROP_NAME_MAX + 1];
+                const char* value;
+                int length = equals - name;
+                if (length > PROP_NAME_MAX) {
+                    ERROR("property name too long in trigger %s", act->name);
+                } else {
+                    memcpy(prop_name, name, length);
+                    prop_name[length] = 0;
+                    
+                    /* does the property exist, and match the trigger value? */
+                    value = property_get((const char *)&prop_name[0]);
+                    if (value && !strcmp(equals + 1, value)) {
+                        action_add_queue_tail(act);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void action_add_queue_tail(struct action *act)
+{
+    list_add_tail(&action_queue, &act->qlist);
+}
+
+struct action *action_remove_queue_head(void)
+{
+    if (list_empty(&action_queue)) {
+        return 0;
+    } else {
+        struct listnode *node = list_head(&action_queue);
+        struct action *act = node_to_item(node, struct action, qlist);
+        list_remove(node);
+        return act;
+    }
+}
+
+static void *parse_service(struct parse_state *state, int nargs, char **args)
+{
+    struct service *svc;
+    if (nargs < 3) {
+        parse_error(state, "services must have a name and a program\n");
+        return 0;
+    }
+    if (!valid_name(args[1])) {
+        parse_error(state, "invalid service name '%s'\n", args[1]);
+        return 0;
+    }
+
+    svc = service_find_by_name(args[1]);
+    if (svc) {
+        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
+        return 0;
+    }
+    
+    nargs -= 2;
+    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
+    if (!svc) {
+        parse_error(state, "out of memory\n");
+        return 0;
+    }
+    svc->name = args[1];
+    svc->classname = "default";
+    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
+    svc->args[nargs] = 0;
+    svc->nargs = nargs;
+    svc->onrestart.name = "onrestart";
+    list_init(&svc->onrestart.commands);
+    list_add_tail(&service_list, &svc->slist);
+    return svc;
+}
+
+static void parse_line_service(struct parse_state *state, int nargs, char **args)
+{
+    struct service *svc = state->context;
+    struct command *cmd;
+    int i, kw, kw_nargs;
+
+    if (nargs == 0) {
+        return;
+    }
+    
+    kw = lookup_keyword(args[0]);
+    switch (kw) {
+    case K_capability:
+        break;
+    case K_class:
+        if (nargs != 2) {
+            parse_error(state, "class option requires a classname\n");
+        } else {
+            svc->classname = args[1];
+        }
+        break;
+    case K_console:
+        svc->flags |= SVC_CONSOLE;
+        break;
+    case K_disabled:
+        svc->flags |= SVC_DISABLED;
+        break;
+    case K_group:
+        if (nargs < 2) {
+            parse_error(state, "group option requires a group id\n");
+        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
+            parse_error(state, "group option accepts at most %d supp. groups\n",
+                        NR_SVC_SUPP_GIDS);
+        } else {
+            int n;
+            svc->gid = decode_uid(args[1]);
+            for (n = 2; n < nargs; n++) {
+                svc->supp_gids[n-2] = decode_uid(args[n]);
+            }
+            svc->nr_supp_gids = n - 2;
+        }
+        break;
+    case K_keycodes:
+        if (nargs < 2) {
+            parse_error(state, "keycodes option requires atleast one keycode\n");
+        } else {
+            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
+            if (!svc->keycodes) {
+                parse_error(state, "could not allocate keycodes\n");
+            } else {
+                svc->nkeycodes = nargs - 1;
+                for (i = 1; i < nargs; i++) {
+                    svc->keycodes[i - 1] = atoi(args[i]);
+                }
+            }
+        }
+        break;
+    case K_oneshot:
+        svc->flags |= SVC_ONESHOT;
+        break;
+    case K_onrestart:
+        nargs--;
+        args++;
+        kw = lookup_keyword(args[0]);
+        if (!kw_is(kw, COMMAND)) {
+            parse_error(state, "invalid command '%s'\n", args[0]);
+            break;
+        }
+        kw_nargs = kw_nargs(kw);
+        if (nargs < kw_nargs) {
+            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
+                kw_nargs > 2 ? "arguments" : "argument");
+            break;
+        }
+
+        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+        cmd->func = kw_func(kw);
+        cmd->nargs = nargs;
+        memcpy(cmd->args, args, sizeof(char*) * nargs);
+        list_add_tail(&svc->onrestart.commands, &cmd->clist);
+        break;
+    case K_critical:
+        svc->flags |= SVC_CRITICAL;
+        break;
+    case K_setenv: { /* name value */
+        struct svcenvinfo *ei;
+        if (nargs < 2) {
+            parse_error(state, "setenv option requires name and value arguments\n");
+            break;
+        }
+        ei = calloc(1, sizeof(*ei));
+        if (!ei) {
+            parse_error(state, "out of memory\n");
+            break;
+        }
+        ei->name = args[1];
+        ei->value = args[2];
+        ei->next = svc->envvars;
+        svc->envvars = ei;
+        break;
+    }
+    case K_socket: {/* name type perm [ uid gid ] */
+        struct socketinfo *si;
+        if (nargs < 4) {
+            parse_error(state, "socket option requires name, type, perm arguments\n");
+            break;
+        }
+        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
+            parse_error(state, "socket type must be 'dgram' or 'stream'\n");
+            break;
+        }
+        si = calloc(1, sizeof(*si));
+        if (!si) {
+            parse_error(state, "out of memory\n");
+            break;
+        }
+        si->name = args[1];
+        si->type = args[2];
+        si->perm = strtoul(args[3], 0, 8);
+        if (nargs > 4)
+            si->uid = decode_uid(args[4]);
+        if (nargs > 5)
+            si->gid = decode_uid(args[5]);
+        si->next = svc->sockets;
+        svc->sockets = si;
+        break;
+    }
+    case K_user:
+        if (nargs != 2) {
+            parse_error(state, "user option requires a user id\n");
+        } else {
+            svc->uid = decode_uid(args[1]);
+        }
+        break;
+    default:
+        parse_error(state, "invalid option '%s'\n", args[0]);
+    }
+}
+
+static void *parse_action(struct parse_state *state, int nargs, char **args)
+{
+    struct action *act;
+    if (nargs < 2) {
+        parse_error(state, "actions must have a trigger\n");
+        return 0;
+    }
+    if (nargs > 2) {
+        parse_error(state, "actions may not have extra parameters\n");
+        return 0;
+    }
+    act = calloc(1, sizeof(*act));
+    act->name = args[1];
+    list_init(&act->commands);
+    list_add_tail(&action_list, &act->alist);
+        /* XXX add to hash */
+    return act;
+}
+
+static void parse_line_action(struct parse_state* state, int nargs, char **args)
+{
+    struct command *cmd;
+    struct action *act = state->context;
+    int (*func)(int nargs, char **args);
+    int kw, n;
+
+    if (nargs == 0) {
+        return;
+    }
+
+    kw = lookup_keyword(args[0]);
+    if (!kw_is(kw, COMMAND)) {
+        parse_error(state, "invalid command '%s'\n", args[0]);
+        return;
+    }
+
+    n = kw_nargs(kw);
+    if (nargs < n) {
+        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
+            n > 2 ? "arguments" : "argument");
+        return;
+    }
+    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
+    cmd->func = kw_func(kw);
+    cmd->nargs = nargs;
+    memcpy(cmd->args, args, sizeof(char*) * nargs);
+    list_add_tail(&act->commands, &cmd->clist);
+}
diff --git a/init/property_service.c b/init/property_service.c
new file mode 100644
index 0000000..7a6416b
--- /dev/null
+++ b/init/property_service.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <cutils/misc.h>
+#include <cutils/sockets.h>
+#include <cutils/ashmem.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/mman.h>
+#include <sys/atomics.h>
+#include <private/android_filesystem_config.h>
+
+#include "property_service.h"
+#include "init.h"
+
+#define PERSISTENT_PROPERTY_DIR  "/data/property"
+
+static int persistent_properties_loaded = 0;
+
+/* White list of permissions for setting property services. */
+struct {
+    const char *prefix;
+    unsigned int uid;
+} property_perms[] = {
+    { "net.rmnet0.",    AID_RADIO },
+    { "net.gprs.",      AID_RADIO },
+    { "ril.",           AID_RADIO },
+    { "gsm.",           AID_RADIO },
+    { "net.dns",        AID_RADIO },
+    { "net.",           AID_SYSTEM },
+    { "dev.",           AID_SYSTEM },
+    { "runtime.",       AID_SYSTEM },
+    { "hw.",            AID_SYSTEM },
+    { "sys.",		AID_SYSTEM },
+    { "service.",	AID_SYSTEM },
+    { "wlan.",		AID_SYSTEM },
+    { "dhcp.",		AID_SYSTEM },
+    { "dhcp.",		AID_DHCP },
+    { "debug.",		AID_SHELL },
+    { "log.",		AID_SHELL },
+    { "persist.sys.",	AID_SYSTEM },
+    { "persist.service.",   AID_SYSTEM },
+    { NULL, 0 }
+};
+
+/*
+ * White list of UID that are allowed to start/stop services.
+ * Currently there are no user apps that require.
+ */
+struct {
+    const char *service;
+    unsigned int uid;
+} control_perms[] = {
+     {NULL, 0 }
+};
+
+typedef struct {
+    void *data;
+    size_t size;
+    int fd;
+} workspace;
+
+static int init_workspace(workspace *w, size_t size)
+{
+    void *data;
+    int fd;
+
+    fd = ashmem_create_region("system_properties", size);
+    if(fd < 0)
+        return -1;
+
+    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if(data == MAP_FAILED)
+        goto out;
+
+    /* allow the wolves we share with to do nothing but read */
+    ashmem_set_prot_region(fd, PROT_READ);
+
+    w->data = data;
+    w->size = size;
+    w->fd = fd;
+
+    return 0;
+
+out:
+    close(fd);
+    return -1;
+}
+
+/* (8 header words + 247 toc words) = 1020 bytes */
+/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */
+
+#define PA_COUNT_MAX  247
+#define PA_INFO_START 1024
+#define PA_SIZE       32768
+
+static workspace pa_workspace;
+static prop_info *pa_info_array;
+
+extern prop_area *__system_property_area__;
+
+static int init_property_area(void)
+{
+    prop_area *pa;
+
+    if(pa_info_array)
+        return -1;
+
+    if(init_workspace(&pa_workspace, PA_SIZE))
+        return -1;
+
+    fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
+
+    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
+
+    pa = pa_workspace.data;
+    memset(pa, 0, PA_SIZE);
+    pa->magic = PROP_AREA_MAGIC;
+    pa->version = PROP_AREA_VERSION;
+
+        /* plug into the lib property services */
+    __system_property_area__ = pa;
+
+    return 0;
+}
+
+static void update_prop_info(prop_info *pi, const char *value, unsigned len)
+{
+    pi->serial = pi->serial | 1;
+    memcpy(pi->value, value, len + 1);
+    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
+    __futex_wake(&pi->serial, INT32_MAX);
+}
+
+static int property_write(prop_info *pi, const char *value)
+{
+    int valuelen = strlen(value);
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    update_prop_info(pi, value, valuelen);
+    return 0;
+}
+
+
+/*
+ * Checks permissions for starting/stoping system services.
+ * AID_SYSTEM and AID_ROOT are always allowed.
+ *
+ * Returns 1 if uid allowed, 0 otherwise.
+ */
+static int check_control_perms(const char *name, int uid) {
+    int i;
+    if (uid == AID_SYSTEM || uid == AID_ROOT)
+        return 1;
+
+    /* Search the ACL */
+    for (i = 0; control_perms[i].service; i++) {
+        if (strcmp(control_perms[i].service, name) == 0) {
+            if (control_perms[i].uid == uid)
+                return 1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Checks permissions for setting system properties.
+ * Returns 1 if uid allowed, 0 otherwise.
+ */
+static int check_perms(const char *name, unsigned int uid)
+{
+    int i;
+    if (uid == 0)
+        return 1;
+
+    if(!strncmp(name, "ro.", 3))
+        name +=3;
+
+    for (i = 0; property_perms[i].prefix; i++) {
+        int tmp;
+        if (strncmp(property_perms[i].prefix, name,
+                    strlen(property_perms[i].prefix)) == 0) {
+            if (property_perms[i].uid == uid) {
+                return 1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+const char* property_get(const char *name)
+{
+    prop_info *pi;
+
+    if(strlen(name) >= PROP_NAME_MAX) return 0;
+
+    pi = (prop_info*) __system_property_find(name);
+
+    if(pi != 0) {
+        return pi->value;
+    } else {
+        return 0;
+    }
+}
+
+static void write_peristent_property(const char *name, const char *value)
+{
+    const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp";
+    char path[PATH_MAX];
+    int fd, length;
+
+    snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
+
+    fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+    if (fd < 0) {
+        ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno);
+        return;   
+    }
+    write(fd, value, strlen(value));
+    close(fd);
+
+    if (rename(tempPath, path)) {
+        unlink(tempPath);
+        ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
+    }
+}
+
+int property_set(const char *name, const char *value)
+{
+    prop_area *pa;
+    prop_info *pi;
+
+    int namelen = strlen(name);
+    int valuelen = strlen(value);
+
+    if(namelen >= PROP_NAME_MAX) return -1;
+    if(valuelen >= PROP_VALUE_MAX) return -1;
+    if(namelen < 1) return -1;
+
+    pi = (prop_info*) __system_property_find(name);
+
+    if(pi != 0) {
+        /* ro.* properties may NEVER be modified once set */
+        if(!strncmp(name, "ro.", 3)) return -1;
+
+        pa = __system_property_area__;
+        update_prop_info(pi, value, valuelen);
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    } else {
+        pa = __system_property_area__;
+        if(pa->count == PA_COUNT_MAX) return -1;
+
+        pi = pa_info_array + pa->count;
+        pi->serial = (valuelen << 24);
+        memcpy(pi->name, name, namelen + 1);
+        memcpy(pi->value, value, valuelen + 1);
+
+        pa->toc[pa->count] =
+            (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
+
+        pa->count++;
+        pa->serial++;
+        __futex_wake(&pa->serial, INT32_MAX);
+    }
+    /* If name starts with "net." treat as a DNS property. */
+    if (strncmp("net.", name, sizeof("net.") - 1) == 0)  {
+        if (strcmp("net.change", name) == 0) {
+            return 0;
+        }
+       /* 
+        * The 'net.change' property is a special property used track when any
+        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
+        * contains the last updated 'net.*' property.
+        */
+        property_set("net.change", name);
+    } else if (persistent_properties_loaded &&
+            strncmp("persist.", name, sizeof("persist.") - 1) == 0) {
+        /* 
+         * Don't write properties to disk until after we have read all default properties
+         * to prevent them from being overwritten by default values.
+         */
+        write_peristent_property(name, value);
+    }
+    property_changed(name, value);
+    return 0;
+}
+
+static int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+                  void *cookie)
+{
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    const prop_info *pi;
+    unsigned n;
+
+    for(n = 0; (pi = __system_property_find_nth(n)); n++) {
+        __system_property_read(pi, name, value);
+        propfn(name, value, cookie);
+    }
+    return 0;
+}
+
+void handle_property_set_fd(int fd)
+{
+    prop_msg msg;
+    int s;
+    int r;
+    int res;
+    struct ucred cr;
+    struct sockaddr_un addr;
+    socklen_t addr_size = sizeof(addr);
+    socklen_t cr_size = sizeof(cr);
+
+    if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
+        return;
+    }
+
+    /* Check socket options here */
+    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
+        close(s);
+        ERROR("Unable to recieve socket options\n");
+        return;
+    }
+
+    r = recv(s, &msg, sizeof(msg), 0);
+    close(s);
+    if(r != sizeof(prop_msg)) {
+        ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n",
+              r, sizeof(prop_msg));
+        return;
+    }
+
+    switch(msg.cmd) {
+    case PROP_MSG_SETPROP:
+        msg.name[PROP_NAME_MAX-1] = 0;
+        msg.value[PROP_VALUE_MAX-1] = 0;
+
+        if(memcmp(msg.name,"ctl.",4) == 0) {
+            if (check_control_perms(msg.value, cr.uid)) {
+                handle_control_message((char*) msg.name + 4, (char*) msg.value);
+            } else {
+                ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",
+                        msg.name + 4, msg.value, cr.uid, cr.pid);
+            }
+        } else {
+            if (check_perms(msg.name, cr.uid)) {
+                property_set((char*) msg.name, (char*) msg.value);
+            } else {
+                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
+                      cr.uid, msg.name);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+void get_property_workspace(int *fd, int *sz)
+{
+    *fd = pa_workspace.fd;
+    *sz = pa_workspace.size;
+}
+
+static void load_properties(char *data)
+{
+    char *key, *value, *eol, *sol, *tmp;
+
+    sol = data;
+    while((eol = strchr(sol, '\n'))) {
+        key = sol;
+        *eol++ = 0;
+        sol = eol;
+
+        value = strchr(key, '=');
+        if(value == 0) continue;
+        *value++ = 0;
+
+        while(isspace(*key)) key++;
+        if(*key == '#') continue;
+        tmp = value - 2;
+        while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
+
+        while(isspace(*value)) value++;
+        tmp = eol - 2;
+        while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
+
+        property_set(key, value);
+    }
+}
+
+static void load_properties_from_file(const char *fn)
+{
+    char *data;
+    unsigned sz;
+
+    data = read_file(fn, &sz);
+
+    if(data != 0) {
+        load_properties(data);
+        free(data);
+    }
+}
+
+static void load_persistent_properties()
+{
+    DIR* dir = opendir(PERSISTENT_PROPERTY_DIR);
+    struct dirent*  entry;
+    char path[PATH_MAX];
+    char value[PROP_VALUE_MAX];
+    int fd, length;
+
+    if (dir) {
+        while ((entry = readdir(dir)) != NULL) {
+            if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..") ||
+                    strncmp("persist.", entry->d_name, sizeof("persist.") - 1))
+                continue;
+#if HAVE_DIRENT_D_TYPE
+            if (entry->d_type != DT_REG)
+                continue;
+#endif
+            /* open the file and read the property value */
+            snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name);
+            fd = open(path, O_RDONLY);
+            if (fd >= 0) {
+                length = read(fd, value, sizeof(value) - 1);
+                if (length >= 0) {
+                    value[length] = 0;
+                    property_set(entry->d_name, value);
+                } else {
+                    ERROR("Unable to read persistent property file %s errno: %d\n", path, errno);
+                }
+                close(fd);
+            } else {
+                ERROR("Unable to open persistent property file %s errno: %d\n", path, errno);
+            }
+        }
+        closedir(dir);
+    } else {
+        ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno);
+    }
+    
+    persistent_properties_loaded = 1;
+}
+
+void property_init(void)
+{
+    init_property_area();
+    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
+}
+
+int start_property_service(void)
+{
+    int fd;
+
+    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
+    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
+    load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
+    /* Read persistent properties after all default values have been loaded. */
+    load_persistent_properties();
+
+    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
+    if(fd < 0) return -1;
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+
+    listen(fd, 8);
+    return fd;
+}
diff --git a/init/property_service.h b/init/property_service.h
new file mode 100644
index 0000000..d12f1f3
--- /dev/null
+++ b/init/property_service.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_PROPERTY_H
+#define _INIT_PROPERTY_H
+
+extern void handle_property_fd(int fd);
+extern void handle_property_set_fd(int fd);
+extern void property_init(void);
+extern int start_property_service(void);
+void get_property_workspace(int *fd, int *sz);
+extern const char* property_get(const char *name);
+extern int property_set(const char *name, const char *value);
+
+#endif	/* _INIT_PROPERTY_H */
diff --git a/init/readme.txt b/init/readme.txt
new file mode 100644
index 0000000..665090b
--- /dev/null
+++ b/init/readme.txt
@@ -0,0 +1,293 @@
+
+Android Init Language
+---------------------
+
+The Android Init Language consists of four broad classes of statements,
+which are Actions, Commands, Services, and Options.
+
+All of these are line-oriented, consisting of tokens separated by
+whitespace.  The c-style backslash escapes may be used to insert
+whitespace into a token.  Double quotes may also be used to prevent
+whitespace from breaking text into multiple tokens.  The backslash,
+when it is the last character on a line, may be used for line-folding.
+
+Lines which start with a # (leading whitespace allowed) are comments.
+
+Actions and Services implicitly declare a new section.  All commands
+or options belong to the section most recently declared.  Commands
+or options before the first section are ignored.
+
+Actions and Services have unique names.  If a second Action or Service
+is declared with the same name as an existing one, it is ignored as
+an error.  (??? should we override instead)
+
+
+Actions
+-------
+Actions are named sequences of commands.  Actions have a trigger which
+is used to determine when the action should occur.  When an event
+occurs which matches an action's trigger, that action is added to
+the tail of a to-be-executed queue (unless it is already on the
+queue).
+
+Each action in the queue is dequeued in sequence and each command in
+that action is executed in sequence.  Init handles other activities
+(device creation/destruction, property setting, process restarting)
+"between" the execution of the commands in activities.
+
+Actions take the form of:
+
+on <trigger>
+   <command>
+   <command>
+   <command>
+
+
+Services
+--------
+Services are programs which init launches and (optionally) restarts
+when they exit.  Services take the form of:
+
+service <name> <pathname> [ <argument> ]*
+   <option>
+   <option>
+   ...
+
+
+Options
+-------
+Options are modifiers to services.  They affect how and when init
+runs the service.
+
+critical
+   This is a device-critical service. If it exits more than four times in
+   four minutes, the device will reboot into recovery mode.
+
+disabled
+   This service will not automatically start with its class.
+   It must be explicitly started by name.
+
+setenv <name> <value>
+   Set the environment variable <name> to <value> in the launched process.
+
+socket <name> <type> <perm> [ <user> [ <group> ] ]
+   Create a unix domain socket named /dev/socket/<name> and pass
+   its fd to the launched process.  <type> must be "dgram" or "stream".
+   User and group default to 0.
+
+user <username>
+   Change to username before exec'ing this service.
+   Currently defaults to root.  (??? probably should default to nobody)
+   Currently, if your process requires linux capabilities then you cannot use
+   this command. You must instead request the capabilities in-process while
+   still root, and then drop to your desired uid.
+
+group <groupname> [ <groupname> ]*
+   Change to groupname before exec'ing this service.  Additional
+   groupnames beyond the (required) first one are used to set the
+   supplemental groups of the process (via setgroups()).
+   Currently defaults to root.  (??? probably should default to nobody)
+
+oneshot
+   Do not restart the service when it exits.
+
+class <name>
+   Specify a class name for the service.  All services in a
+   named class may be started or stopped together.  A service
+   is in the class "default" if one is not specified via the
+   class option.
+
+onrestart
+    Execute a Command (see below) when service restarts.
+
+Triggers
+--------
+   Triggers are strings which can be used to match certain kinds
+   of events and used to cause an action to occur.
+
+boot
+   This is the first trigger that will occur when init starts
+   (after /init.conf is loaded)
+
+<name>=<value>
+   Triggers of this form occur when the property <name> is set
+   to the specific value <value>.
+
+device-added-<path>
+device-removed-<path>
+   Triggers of these forms occur when a device node is added
+   or removed.
+
+service-exited-<name>
+   Triggers of this form occur when the specified service exits.
+
+
+Commands
+--------
+
+exec <path> [ <argument> ]*
+   Fork and execute a program (<path>).  This will block until
+   the program completes execution.  It is best to avoid exec
+   as unlike the builtin commands, it runs the risk of getting
+   init "stuck". (??? maybe there should be a timeout?)
+
+export <name> <value>
+   Set the environment variable <name> equal to <value> in the
+   global environment (which will be inherited by all processes
+   started after this command is executed)
+
+ifup <interface>
+   Bring the network interface <interface> online.
+
+import <filename>
+   Parse an init config file, extending the current configuration.
+
+hostname <name>
+   Set the host name.
+
+chmod <octal-mode> <path>
+   Change file access permissions.
+
+chown <owner> <group> <path>
+   Change file owner and group.
+
+class_start <serviceclass>
+   Start all services of the specified class if they are
+   not already running.
+
+class_stop <serviceclass>
+   Stop all services of the specified class if they are
+   currently running.
+
+domainname <name>
+   Set the domain name.
+
+insmod <path>
+   Install the module at <path>
+
+mkdir <path> [mode] [owner] [group]
+   Create a directory at <path>, optionally with the given mode, owner, and
+   group. If not provided, the directory is created with permissions 755 and
+   owned by the root user and root group.
+
+mount <type> <device> <dir> [ <mountoption> ]*
+   Attempt to mount the named device at the directory <dir>
+   <device> may be of the form mtd@name to specify a mtd block
+   device by name.
+   <mountoption>s include "ro", "rw", "remount", "noatime", ...
+
+setkey
+   TBD
+
+setprop <name> <value>
+   Set system property <name> to <value>.
+
+setrlimit <resource> <cur> <max>
+   Set the rlimit for a resource.
+
+start <service>
+   Start a service running if it is not already running.
+
+stop <service>
+   Stop a service from running if it is currently running.
+
+symlink <target> <path>
+   Create a symbolic link at <path> with the value <target>
+
+sysclktz <mins_west_of_gmt>
+   Set the system clock base (0 if system clock ticks in GMT)
+
+trigger <event>
+   Trigger an event.  Used to queue an action from another
+   action.
+
+write <path> <string> [ <string> ]*
+   Open the file at <path> and write one or more strings
+   to it with write(2)
+
+
+Properties
+----------
+Init updates some system properties to provide some insight into
+what it's doing:
+
+init.action 
+   Equal to the name of the action currently being executed or "" if none
+
+init.command
+   Equal to the command being executed or "" if none.
+
+init.svc.<name>
+   State of a named service ("stopped", "running", "restarting")
+
+
+Example init.conf
+-----------------
+
+# not complete -- just providing some examples of usage
+#
+on boot
+   export PATH /sbin:/system/sbin:/system/bin
+   export LD_LIBRARY_PATH /system/lib
+
+   mkdir /dev
+   mkdir /proc
+   mkdir /sys
+
+   mount tmpfs tmpfs /dev
+   mkdir /dev/pts
+   mkdir /dev/socket
+   mount devpts devpts /dev/pts
+   mount proc proc /proc
+   mount sysfs sysfs /sys
+
+   write /proc/cpu/alignment 4
+
+   ifup lo
+
+   hostname localhost
+   domainname localhost
+
+   mount yaffs2 mtd@system /system
+   mount yaffs2 mtd@userdata /data
+
+   import /system/etc/init.conf
+
+   class_start default
+
+service adbd /sbin/adbd
+   user adb
+   group adb
+
+service usbd /system/bin/usbd -r
+   user usbd
+   group usbd
+   socket usbd 666
+
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote
+   socket zygote 666
+
+service runtime /system/bin/runtime
+   user system
+   group system
+
+on device-added-/dev/compass
+   start akmd
+
+on device-removed-/dev/compass
+   stop akmd
+
+service akmd /sbin/akmd
+   disabled
+   user akmd
+   group akmd
+
+Debugging notes
+---------------
+By default, programs executed by init will drop stdout and stderr into
+/dev/null. To help with debugging, you can execute your program via the
+Andoird program logwrapper. This will redirect stdout/stderr into the
+Android logging system (accessed via logcat).
+
+For example
+service akmd /system/bin/logwrapper /sbin/akmd
diff --git a/init/util.c b/init/util.c
new file mode 100644
index 0000000..0b7667d
--- /dev/null
+++ b/init/util.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* for ANDROID_SOCKET_* */
+#include <cutils/sockets.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "init.h"
+
+static int log_fd = -1;
+/* Inital log level before init.rc is parsed and this this is reset. */
+static int log_level = LOG_DEFAULT_LEVEL;
+
+
+void log_set_level(int level) {
+    log_level = level;
+}
+
+void log_init(void)
+{
+    static const char *name = "/dev/__kmsg__";
+    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
+        log_fd = open(name, O_WRONLY);
+        fcntl(log_fd, F_SETFD, FD_CLOEXEC);
+        unlink(name);
+    }
+}
+
+#define LOG_BUF_MAX 512
+
+void log_write(int level, const char *fmt, ...)
+{
+    char buf[LOG_BUF_MAX];
+    va_list ap;
+    
+    if (level > log_level) return;
+    if (log_fd < 0) return;
+    
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_MAX, fmt, ap);
+    buf[LOG_BUF_MAX - 1] = 0;
+    va_end(ap);
+    write(log_fd, buf, strlen(buf));
+}
+
+/*
+ * android_name_to_id - returns the integer uid/gid associated with the given
+ * name, or -1U on error.
+ */
+static unsigned int android_name_to_id(const char *name)
+{
+    struct android_id_info *info = android_ids;
+    unsigned int n;
+
+    for (n = 0; n < android_id_count; n++) {
+        if (!strcmp(info[n].name, name))
+            return info[n].aid;
+    }
+
+    return -1U;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns -1U on
+ * error.
+ */
+unsigned int decode_uid(const char *s)
+{
+    unsigned int v;
+
+    if (!s || *s == '\0')
+        return -1U;
+    if (isalpha(s[0]))
+        return android_name_to_id(s);
+
+    errno = 0;
+    v = (unsigned int) strtoul(s, 0, 0);
+    if (errno)
+        return -1U;
+    return v;
+}
+
+/*
+ * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
+ * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
+ * daemon. We communicate the file descriptor's value via the environment
+ * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
+ */
+int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
+{
+    struct sockaddr_un addr;
+    int fd, ret;
+
+    fd = socket(PF_UNIX, type, 0);
+    if (fd < 0) {
+        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
+        return -1;
+    }
+
+    memset(&addr, 0 , sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
+             name);
+
+    ret = unlink(addr.sun_path);
+    if (ret != 0 && errno != ENOENT) {
+        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
+        goto out_close;
+    }
+
+    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+    if (ret) {
+        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
+        goto out_unlink;
+    }
+
+    chown(addr.sun_path, uid, gid);
+    chmod(addr.sun_path, perm);
+
+    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
+         addr.sun_path, perm, uid, gid);
+
+    return fd;
+
+out_unlink:
+    unlink(addr.sun_path);
+out_close:
+    close(fd);
+    return -1;
+}
+
+/* reads a file, making sure it is terminated with \n \0 */
+void *read_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz + 2);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+    data[sz] = '\n';
+    data[sz+1] = 0;
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
+
+void list_init(struct listnode *node)
+{
+    node->next = node;
+    node->prev = node;
+}
+
+void list_add_tail(struct listnode *head, struct listnode *item)
+{
+    item->next = head;
+    item->prev = head->prev;
+    head->prev->next = item;
+    head->prev = item;
+}
+
+void list_remove(struct listnode *item)
+{
+    item->next->prev = item->prev;
+    item->prev->next = item->next;
+}
+
diff --git a/libctest/Android.mk b/libctest/Android.mk
new file mode 100644
index 0000000..815fabb
--- /dev/null
+++ b/libctest/Android.mk
@@ -0,0 +1,7 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libctest
+LOCAL_SRC_FILES := ctest.c
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libctest/ctest.c b/libctest/ctest.c
new file mode 100644
index 0000000..ee6331f
--- /dev/null
+++ b/libctest/ctest.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <ctest/ctest.h>
+
+#define MAX_TESTS 255
+
+/** Semi-random number used to identify assertion errors. */
+#define ASSERTION_ERROR 42
+    
+typedef void TestCase();
+
+/** A suite of tests. */
+typedef struct {
+    int size;
+    const char* testNames[MAX_TESTS];
+    TestCase* tests[MAX_TESTS];
+    int currentTest;
+    FILE* out;
+} TestSuite;
+
+/** Gets the test suite. Creates it if necessary. */
+static TestSuite* getTestSuite() {
+    static TestSuite* suite = NULL;
+    
+    if (suite != NULL) {
+        return suite;
+    }
+    
+    suite = calloc(1, sizeof(TestSuite));
+    assert(suite != NULL);
+    
+    suite->out = tmpfile();
+    assert(suite->out != NULL);
+    
+    return suite;
+}
+
+void addNamedTest(const char* name, TestCase* test) {
+    TestSuite* testSuite = getTestSuite();
+    assert(testSuite->size <= MAX_TESTS);
+    
+    int index = testSuite->size;
+    testSuite->testNames[index] = name;
+    testSuite->tests[index] = test;
+    
+    testSuite->size++;
+}
+
+/** Prints failures to stderr. */
+static void printFailures(int failures) {
+    TestSuite* suite = getTestSuite();
+
+    fprintf(stderr, "FAILURE! %d of %d tests failed. Failures:\n", 
+            failures, suite->size);
+
+    // Copy test output to stdout.
+    rewind(suite->out);
+    char buffer[512];
+    size_t read;
+    while ((read = fread(buffer, sizeof(char), 512, suite->out)) > 0) {
+        // TODO: Make sure we actually wrote 'read' bytes.
+        fwrite(buffer, sizeof(char), read, stderr);
+    }
+}
+
+/** Runs a single test case. */
+static int runCurrentTest() {
+    TestSuite* suite = getTestSuite();
+    
+    pid_t pid = fork();
+    if (pid == 0) {
+        // Child process. Runs test case.
+        suite->tests[suite->currentTest]();
+        
+        // Exit successfully.
+        exit(0);
+    } else if (pid < 0) {
+        fprintf(stderr, "Fork failed.");
+        exit(1); 
+    } else {
+        // Parent process. Wait for child.
+        int status;
+        waitpid(pid, &status, 0);
+        
+        if (!WIFEXITED(status)) {
+            return -1;
+        }
+        
+        return WEXITSTATUS(status);
+    }
+}
+
+void runTests() {
+    TestSuite* suite = getTestSuite();
+   
+    int failures = 0;
+    for (suite->currentTest = 0; suite->currentTest < suite->size; 
+            suite->currentTest++) {
+        // Flush stdout before forking.
+        fflush(stdout);
+        
+        int result = runCurrentTest();
+       
+        if (result != 0) {
+            printf("X");
+            
+            failures++;
+
+            // Handle errors other than assertions.
+            if (result != ASSERTION_ERROR) {
+                // TODO: Report file name.
+                fprintf(suite->out, "Process failed: [%s] status: %d\n",
+                        suite->testNames[suite->currentTest], result);
+                fflush(suite->out);
+            }
+        } else {
+            printf(".");
+        }
+    }
+
+    printf("\n");
+    
+    if (failures > 0) {
+        printFailures(failures);
+    } else {
+        printf("SUCCESS! %d tests ran successfully.\n", suite->size);
+    }
+}
+
+void assertTrueWithSource(int value, const char* file, int line, char* message) {
+    if (!value) {
+        TestSuite* suite = getTestSuite();
+
+        fprintf(suite->out, "Assertion failed: [%s:%d] %s: %s\n", file, line, 
+                suite->testNames[suite->currentTest], message);
+        fflush(suite->out);
+        
+        // Exit the process for this test case.
+        exit(ASSERTION_ERROR);
+    }
+}
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
new file mode 100644
index 0000000..a43f7e3
--- /dev/null
+++ b/libcutils/Android.mk
@@ -0,0 +1,113 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+commonSources := \
+	array.c \
+	hashmap.c \
+	atomic.c \
+	buffer.c \
+	socket_inaddr_any_server.c \
+	socket_local_client.c \
+	socket_local_server.c \
+	socket_loopback_client.c \
+	socket_loopback_server.c \
+	socket_network_client.c \
+	config_utils.c \
+	cpu_info.c \
+	load_file.c \
+	strdup16to8.c \
+	strdup8to16.c \
+	record_stream.c \
+	process_name.c \
+	properties.c \
+	threads.c
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+WITH_MINGW :=
+ifeq ($(HOST_OS),windows)
+    ifeq ($(strip $(USE_CYGWIN)),)
+        WITH_MINGW := 1
+    endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+    WITH_MINGW := 1
+endif
+
+ifeq ($(WITH_MINGW),1)
+    commonSources += \
+        uio.c
+else
+    commonSources += \
+        mspace.c \
+        selector.c \
+        fdevent.c \
+        tztime.c \
+        tzstrftime.c \
+        adb_networking.c \
+	zygote.c
+endif
+
+
+# Static library for host
+# ========================================================
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+ifeq ($(TARGET_SIMULATOR),true)
+
+# Shared library for simulator
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) memory.c dlmalloc_stubs.c ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+else #!sim
+
+# Shared and static library for target
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += memset32.S atomic-android-arm.S
+else  # !arm
+LOCAL_SRC_FILES += memory.c
+endif # !arm
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_WHOLE_STATIC_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+endif #!sim
diff --git a/libcutils/MODULE_LICENSE_APACHE2 b/libcutils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libcutils/MODULE_LICENSE_APACHE2
diff --git a/libcutils/NOTICE b/libcutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libcutils/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libcutils/adb_networking.c b/libcutils/adb_networking.c
new file mode 100644
index 0000000..d819d44
--- /dev/null
+++ b/libcutils/adb_networking.c
@@ -0,0 +1,172 @@
+/* libs/utils/adb_networking.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#define ADB_PORT 5037
+
+#define _GNU_SOURCE     /* for asprintf */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <cutils/adb_networking.h>
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+
+#define ADB_RESPONSE_SIZE 4
+
+/**
+ * Unfortunately, java.net.Socket wants to create it's filedescriptor early
+ * So, this function takes an fd that must be an unconnected
+ * PF_LOCAL SOCK_STREAM
+ */
+int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address)
+{
+    struct sockaddr_in local_addr;
+    socklen_t alen;
+    char *cmd;
+    char buf[ADB_RESPONSE_SIZE + 1];
+    ssize_t count_read;
+    int ret;
+    int err;
+    /* for impl of inet_ntoa below*/
+    union {
+        uint8_t  b[4];
+        uint32_t l;
+    } a;
+
+    /* First, connect to adb */
+   
+    memset(&local_addr, 0, sizeof(local_addr));
+    local_addr.sin_family = AF_INET;
+    local_addr.sin_port = htons(ADB_PORT);
+    local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    do {
+        err = connect(fd, (struct sockaddr *) &local_addr, sizeof(local_addr));
+    } while (err < 0 && errno == EINTR);
+
+    if (err < 0) {
+        return -1;
+    }
+
+    a.l = p_address->sin_addr.s_addr;
+
+    // compose the command
+    asprintf(&cmd, "tcp:%u:%u.%u.%u.%u", 
+                (unsigned int)ntohs(p_address->sin_port), 
+                a.b[0],a.b[1],a.b[2],a.b[3]);
+
+    // buf is now the ascii hex length of cmd
+    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+    // write the 4-byte length
+    do {
+        err = write(fd, buf, 4);        
+    } while (err < 0 && errno == EINTR);
+
+    // write the command
+    do {
+        err = write(fd, cmd, strlen(cmd));        
+    } while (err < 0 && errno == EINTR);
+
+    // read the result
+    do {
+        count_read = read(fd, buf, sizeof(buf) - 1);
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read == ADB_RESPONSE_SIZE 
+            && 0 == strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+        ret = 0;
+    } else {
+        /* what errno here? <shrug? */
+        errno = ENETUNREACH;
+        ret = -1;
+    }
+
+    free(cmd);
+    
+    return ret;
+}
+
+/**
+ * Fills in *p_out_addr and returns 0 on success
+ * Memset's *p_out_addr and returns -1 on fail
+ */
+
+int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr)
+{
+    int fd;
+    char *cmd = NULL;
+    char buf[ADB_RESPONSE_SIZE + 1];
+    int err;
+    ssize_t count_read;
+    
+    fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
+
+    if (fd < 0) {
+        return -1;
+    }
+
+    // compose the command
+    asprintf(&cmd, "dns:%s", name);
+
+    // buf is now the ascii hex length of cmd
+    snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+    // write the 4-byte length
+    do {
+        err = write(fd, buf, 4);        
+    } while (err < 0 && errno == EINTR);
+
+    // write the command
+    do {
+        err = write(fd, cmd, strlen(cmd));        
+    } while (err < 0 && errno == EINTR);
+
+    // read the result
+    do {
+        count_read = read(fd, buf, ADB_RESPONSE_SIZE);
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read != ADB_RESPONSE_SIZE 
+            || 0 != strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+        goto error;
+    }
+
+    // read the actual IP address
+    do {
+        count_read = read(fd, &(p_out_addr->s_addr), sizeof(p_out_addr->s_addr));
+    } while (count_read < 0 && errno != EINTR);
+
+    if (count_read != 4) {
+        goto error;
+    }
+
+    free(cmd);
+    close(fd);
+    return 0;
+error:
+    free(cmd);
+    close(fd);
+    memset(p_out_addr, 0, sizeof(struct in_addr));
+    return -1;
+}
+
diff --git a/libcutils/array.c b/libcutils/array.c
new file mode 100644
index 0000000..ff2c8ff
--- /dev/null
+++ b/libcutils/array.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/array.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define INITIAL_CAPACITY (4)
+
+struct Array {
+    void** contents;
+    int size;
+    int capacity;
+};
+
+Array* arrayCreate() {
+    return calloc(1, sizeof(struct Array));
+}
+
+void arrayFree(Array* array) {
+    assert(array != NULL);
+
+    // Free internal array.
+    free(array->contents);
+
+    // Free the Array itself.
+    free(array);
+}
+
+/** Returns 0 if successful, < 0 otherwise.. */
+static int ensureCapacity(Array* array, int capacity) {
+    int oldCapacity = array->capacity;
+    if (capacity > oldCapacity) {
+        int newCapacity = (oldCapacity == 0) ? INITIAL_CAPACITY : oldCapacity * 2;
+    
+        // Keep doubling capacity until we surpass necessary capacity. 
+        while (newCapacity < capacity) {
+            newCapacity *= 2;
+        }
+    
+        void** newContents;
+        if (array->contents == NULL) {
+            // Allocate new array.
+            newContents = malloc(newCapacity * sizeof(void*));
+            if (newContents == NULL) {
+                return -1;
+            }
+        } else {
+            // Expand existing array.
+            newContents = realloc(array->contents, sizeof(void*) * newCapacity);
+            if (newContents == NULL) {
+                return -1;
+            }
+        }
+
+        array->capacity = newCapacity;
+        array->contents = newContents;
+    }
+
+    return 0;
+}
+
+int arrayAdd(Array* array, void* pointer) {
+    assert(array != NULL);
+    int size = array->size;
+    int result = ensureCapacity(array, size + 1);
+    if (result < 0) {
+        return result;
+    }
+    array->contents[size] = pointer;
+    array->size++;
+    return 0;
+}
+
+static inline void checkBounds(Array* array, int index) {
+    assert(array != NULL);
+    assert(index < array->size);
+    assert(index >= 0);
+}
+
+void* arrayGet(Array* array, int index) {
+    checkBounds(array, index);
+    return array->contents[index];
+}
+
+void* arrayRemove(Array* array, int index) {
+    checkBounds(array, index);
+
+    void* pointer = array->contents[index];
+    
+    int newSize = array->size - 1;
+    
+    // Shift entries left.
+    if (index != newSize) {
+        memmove(array->contents + index, array->contents + index + 1, 
+                (sizeof(void*)) * (newSize - index));
+    }
+
+    array->size = newSize;
+
+    return pointer;
+}
+
+void* arraySet(Array* array, int index, void* pointer) {
+    checkBounds(array, index);
+    void* old = array->contents[index];
+    array->contents[index] = pointer;
+    return old;
+}
+
+int arraySetSize(Array* array, int newSize) {
+    assert(array != NULL);
+    assert(newSize >= 0);
+   
+    int oldSize = array->size;
+    
+    if (newSize > oldSize) {
+        // Expand.
+        int result = ensureCapacity(array, newSize);
+        if (result < 0) {
+            return result;
+        }
+
+        // Zero out new entries.
+        memset(array->contents + sizeof(void*) * oldSize, 0,
+                sizeof(void*) * (newSize - oldSize));
+    }
+
+    array->size = newSize;
+
+    return 0;
+}
+
+int arraySize(Array* array) {
+    assert(array != NULL);
+    return array->size;
+}
+
+const void** arrayUnwrap(Array* array) {
+    return array->contents;
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
new file mode 100644
index 0000000..5e158af
--- /dev/null
+++ b/libcutils/ashmem-dev.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <linux/ashmem.h>
+#include <cutils/ashmem.h>
+
+#define ASHMEM_DEVICE	"/dev/ashmem"
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+	int fd, ret;
+
+	fd = open(ASHMEM_DEVICE, O_RDWR);
+	if (fd < 0)
+		return fd;
+
+	if (name) {
+		char buf[ASHMEM_NAME_LEN];
+
+		strlcpy(buf, name, sizeof(buf));
+		ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+		if (ret < 0)
+			goto error;
+	}
+
+	ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+	if (ret < 0)
+		goto error;
+
+	return fd;
+
+error:
+	close(fd);
+	return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+	return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+	struct ashmem_pin pin = { offset, len };
+	return ioctl(fd, ASHMEM_PIN, &pin);
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+	struct ashmem_pin pin = { offset, len };
+	return ioctl(fd, ASHMEM_UNPIN, &pin);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
new file mode 100644
index 0000000..dbb52bc
--- /dev/null
+++ b/libcutils/ashmem-host.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for the simulator, which lacks
+ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#include <cutils/ashmem.h>
+
+int ashmem_create_region(const char *ignored, size_t size)
+{
+	static const char txt[] = "abcdefghijklmnopqrstuvwxyz"
+				  "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	char name[64];
+	unsigned int retries = 0;
+	pid_t pid = getpid();
+	int fd;
+
+	srand(time(NULL) + pid);
+
+retry:
+	/* not beautiful, its just wolf-like loop unrolling */
+	snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c",
+		pid,
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+		txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]);
+
+	/* open O_EXCL & O_CREAT: we are either the sole owner or we fail */
+	fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+	if (fd == -1) {
+		/* unlikely, but if we failed because `name' exists, retry */
+		if (errno == EEXIST && ++retries < 6)
+			goto retry;
+		return -1;
+	}
+
+	/* truncate the file to `len' bytes */
+	if (ftruncate(fd, size) == -1)
+		goto error;
+
+	if (unlink(name) == -1)
+		goto error;
+
+	return fd;
+error:
+	close(fd);
+	return -1;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+	return 0;
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+	return ASHMEM_NOT_PURGED;
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+	return ASHMEM_IS_UNPINNED;
+}
diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S
new file mode 100644
index 0000000..c56ec5d
--- /dev/null
+++ b/libcutils/atomic-android-arm.S
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <machine/cpu-features.h>
+
+/*
+ * NOTE: these atomic operations are SMP safe on all architectures, 
+ * except swap(), see below.
+ */
+
+	.text
+	.align
+	
+    .global android_atomic_write
+
+	.global android_atomic_inc
+	.global android_atomic_dec
+    
+	.global android_atomic_add
+	.global android_atomic_and
+	.global android_atomic_or
+    
+    .global android_atomic_swap
+	
+	.global android_atomic_cmpxchg
+
+/*
+ * ----------------------------------------------------------------------------
+ * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
+ * clobbered: r3, ip, flags
+ * return 0 if a swap was made, non-zero otherwise.
+ */ 
+
+   .equ     kernel_cmpxchg, 0xFFFF0FC0
+   .equ     kernel_atomic_base, 0xFFFF0FFF
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+ 
+android_atomic_write:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r1
+    mov     r1, r0
+1: @ android_atomic_write
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    ldmia   sp!, {r4, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+ 
+android_atomic_inc:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r0
+1: @ android_atomic_inc
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     r1, r0, #1
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r1, r0, #1
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    sub     r0, r1, #1
+    ldmia   sp!, {r4, lr}
+    bx      lr
+  
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+ 
+android_atomic_dec:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r0
+1: @ android_atomic_dec
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    sub     r1, r0, #1
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    sub     r1, r0, #1
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    add     r0, r1, #1
+    ldmia   sp!, {r4, lr}
+    bx      lr
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+    stmdb   sp!, {r4, lr}
+    mov     r2, r1
+    mov     r4, r0
+1: @ android_atomic_add
+    ldr     r0, [r2]
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    add     r1, r0, r4
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    add     r1, r0, r4
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    sub     r0, r1, r4
+    ldmia   sp!, {r4, lr}
+    bx      lr
+    
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+    stmdb   sp!, {r4, r5, lr}   
+    mov     r2, r1              /* r2 = address */
+    mov     r4, r0              /* r4 = the value */
+1: @ android_atomic_and
+    ldr     r0, [r2]            /* r0 = address[0] */
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #8
+    mov     r5, r0              /* r5 = save address[0] */
+    and     r1, r0, r4          /* r1 = new value */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+#else
+    mov     r5, r0              /* r5 = save address[0] */
+    and     r1, r0, r4          /* r1 = new value */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    mov     r0, r5
+    ldmia   sp!, {r4, r5, lr}
+    bx      lr
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+    stmdb   sp!, {r4, r5, lr}   
+    mov     r2, r1              /* r2 = address */
+    mov     r4, r0              /* r4 = the value */
+1: @ android_atomic_or
+    ldr     r0, [r2]            /* r0 = address[0] */
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #8
+    mov     r5, r0              /* r5 = save address[0] */
+    orr     r1, r0, r4          /* r1 = new value */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+#else
+    mov     r5, r0              /* r5 = save address[0] */
+    orr     r1, r0, r4          /* r1 = new value */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)  /* call cmpxchg() */
+    mov     lr, pc
+    bx      r3
+#endif
+    bcc     1b
+    mov     r0, r5
+    ldmia   sp!, {r4, r5, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+/* FIXME: this is not safe on SMP systems 
+ * a general way to do it is to use kernel_cmpxchg */
+
+android_atomic_swap:
+    swp     r0, r0, [r1]
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+    stmdb   sp!, {r4, lr}
+    mov     r4, r0          /* r4 = save oldvalue */
+1: @ android_atomic_cmpxchg
+    mov     r3, #kernel_atomic_base
+#ifdef __ARM_HAVE_PC_INTERWORK
+    add     lr, pc, #4
+    mov     r0, r4          /* r0 = oldvalue */
+    add     pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+#else
+    mov     r0, r4          /* r0 = oldvalue */
+    add     r3, r3, #(kernel_cmpxchg - kernel_atomic_base)
+    mov     lr, pc
+    bx      r3
+#endif
+    bcs     2f              /* swap was made. we're good, return. */
+    ldr     r3, [r2]        /* swap not made, see if it's because *ptr!=oldvalue */
+    cmp     r3, r4
+    beq     1b
+2: @ android_atomic_cmpxchg
+    ldmia   sp!, {r4, lr}
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic-android-armv6.S b/libcutils/atomic-android-armv6.S
new file mode 100644
index 0000000..64146c1
--- /dev/null
+++ b/libcutils/atomic-android-armv6.S
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+    .text
+    .align
+    
+    .global android_atomic_write
+    
+    .global android_atomic_inc
+    .global android_atomic_dec
+    
+    .global android_atomic_add
+    .global android_atomic_and
+    .global android_atomic_or
+    
+    .global android_atomic_swap
+    
+    .global android_atomic_cmpxchg
+    
+
+
+/* FIXME: On SMP systems memory barriers may be needed */
+#warning  "this file is not safe with SMP systems"
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+
+android_atomic_write:
+1:  ldrex   r12, [r1]
+    strex   r12, r0, [r1]
+    cmp     r12, #0
+    bne     1b
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+ 
+android_atomic_inc:
+    mov     r12, r0
+1:  ldrex   r0, [r12]
+    add     r2, r0, #1
+    strex   r1, r2, [r12]
+    cmp     r1, #0
+    bxeq    lr
+    b       1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+ 
+android_atomic_dec:
+    mov     r12, r0
+1:  ldrex   r0, [r12]
+    sub     r2, r0, #1
+    strex   r1, r2, [r12]
+    cmp     r1, #0
+    bxeq    lr
+    b       1b
+
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    add     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    and     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+
+    
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+    mov     r12, r0
+1:  ldrex   r0, [r1]
+    orr     r2, r0, r12
+    strex   r3, r2, [r1]
+    cmp     r3, #0
+    bxeq    lr
+    b       1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_swap:
+    swp     r0, r0, [r1]
+    bx      lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+    mov     r12, r1
+    ldrex   r3, [r2]
+    eors    r0, r0, r3
+    strexeq r0, r12, [r2]
+    bx      lr
+
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
new file mode 100644
index 0000000..65d7af0
--- /dev/null
+++ b/libcutils/atomic.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/atomic.h>
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#else
+#include <sched.h>
+#endif
+
+/*****************************************************************************/
+#if defined(HAVE_MACOSX_IPC)
+
+#include <libkern/OSAtomic.h>
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, value, (int32_t*)addr) == 0);
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+    return OSAtomicIncrement32Barrier((int32_t*)addr)-1;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+    return OSAtomicDecrement32Barrier((int32_t*)addr)+1;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+    return OSAtomicAdd32Barrier(value, (int32_t*)addr)-value;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue&value, (int32_t*)addr) == 0);
+    return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0);
+    return oldValue;
+}
+
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, value, addr));
+    return oldValue;
+}
+
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+    return OSAtomicCompareAndSwap32Barrier(oldvalue, newvalue, (int32_t*)addr) == 0;
+}
+
+#if defined(__ppc__)        \
+    || defined(__PPC__)     \
+    || defined(__powerpc__) \
+    || defined(__powerpc)   \
+    || defined(__POWERPC__) \
+    || defined(_M_PPC)      \
+    || defined(__PPC)
+#define NEED_QUASIATOMICS 1
+#else
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr) {
+    return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+            (int64_t*)addr) == 0;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_quasiatomic_cmpxchg_64(oldValue, value, addr));
+    return oldValue;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    return OSAtomicAdd64Barrier(0, addr);
+}    
+
+#endif
+
+
+/*****************************************************************************/
+#elif defined(__i386__) || defined(__x86_64__)
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, value, addr));
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr));
+    return oldValue;
+}
+
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) {
+    int32_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (android_atomic_cmpxchg(oldValue, value, addr));
+    return oldValue;
+}
+
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+    int xchg;
+    asm volatile
+    (
+    "   lock; cmpxchg %%ecx, (%%edx);"
+    "   setne %%al;"
+    "   andl $1, %%eax"
+    : "=a" (xchg)
+    : "a" (oldvalue), "c" (newvalue), "d" (addr)
+    );
+    return xchg;
+}
+
+#define NEED_QUASIATOMICS 1
+
+/*****************************************************************************/
+#elif __arm__
+// Most of the implementation is in atomic-android-arm.s.
+
+// on the device, we implement the 64-bit atomic operations through
+// mutex locking. normally, this is bad because we must initialize
+// a pthread_mutex_t before being able to use it, and this means
+// having to do an initialization check on each function call, and
+// that's where really ugly things begin...
+//
+// BUT, as a special twist, we take advantage of the fact that in our
+// pthread library, a mutex is simply a volatile word whose value is always
+// initialized to 0. In other words, simply declaring a static mutex
+// object initializes it !
+//
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+//
+
+#include <pthread.h>
+
+#define  SWAP_LOCK_COUNT  32U
+static pthread_mutex_t  _swap_locks[SWAP_LOCK_COUNT];
+
+#define  SWAP_LOCK(addr)   \
+   &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
+
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t oldValue;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+
+    oldValue = *addr;
+    *addr    = value;
+
+    pthread_mutex_unlock(lock);
+    return oldValue;
+}
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr) {
+    int result;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+
+    if (*addr == oldvalue) {
+        *addr  = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    int64_t result;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+    result = *addr;
+    pthread_mutex_unlock(lock);
+    return result;
+}    
+
+#else
+
+#error "Unsupported atomic operations for this platform"
+
+#endif
+
+
+
+#if NEED_QUASIATOMICS
+
+/* Note that a spinlock is *not* a good idea in general
+ * since they can introduce subtle issues. For example,
+ * a real-time thread trying to acquire a spinlock already
+ * acquired by another thread will never yeld, making the
+ * CPU loop endlessly!
+ *
+ * However, this code is only used on the Linux simulator
+ * so it's probably ok for us.
+ *
+ * The alternative is to use a pthread mutex, but
+ * these must be initialized before being used, and
+ * then you have the problem of lazily initializing
+ * a mutex without any other synchronization primitive.
+ */
+
+/* global spinlock for all 64-bit quasiatomic operations */
+static int32_t quasiatomic_spinlock = 0;
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+        volatile int64_t* addr) {
+    int result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else        
+        sched_yield();
+#endif        
+    }
+
+    if (*addr == oldvalue) {
+        *addr = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+    int64_t result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+    int64_t result;
+    
+    while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    *addr = value;
+    android_atomic_swap(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+#endif
diff --git a/libcutils/buffer.c b/libcutils/buffer.c
new file mode 100644
index 0000000..f34b8f8
--- /dev/null
+++ b/libcutils/buffer.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "buffer"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "buffer.h"
+#include "loghack.h"
+
+Buffer* bufferCreate(size_t capacity) {
+    Buffer* buffer = malloc(sizeof(Buffer));
+    if (buffer == NULL) {
+        return NULL;
+    }
+    buffer->capacity = capacity;
+    buffer->expected = 0;
+    buffer->data = malloc(capacity);
+    if (buffer->data == NULL) {
+        free(buffer);
+        return NULL;
+    }
+    return buffer;
+}
+
+void bufferFree(Buffer* buffer) {
+    free(buffer->data);
+    free(buffer);
+}
+
+Buffer* bufferWrap(char* data, size_t capacity, size_t size) {
+    Buffer* buffer = malloc(sizeof(Buffer));
+    if (buffer == NULL) {
+        return NULL;
+    }
+
+    buffer->data = data;
+    buffer->capacity = capacity;
+    buffer->size = size;
+    buffer->expected = 0;
+    return buffer;
+}
+
+int bufferPrepareForRead(Buffer* buffer, size_t expected) {
+    if (expected > buffer->capacity) {
+        // Expand buffer.
+        char* expanded = realloc(buffer->data, expected);
+        if (expanded == NULL) {
+            errno = ENOMEM;
+            return -1;
+        }
+        buffer->capacity = expected;
+        buffer->data = expanded;
+    }
+
+    buffer->size = 0;
+    buffer->expected = expected;
+    return 0;
+}
+
+ssize_t bufferRead(Buffer* buffer, int fd) {
+    assert(buffer->size < buffer->expected);
+    
+    ssize_t bytesRead = read(fd, 
+            buffer->data + buffer->size, 
+            buffer->expected - buffer->size);
+
+    if (bytesRead > 0) {
+        buffer->size += bytesRead;
+        return buffer->size;        
+    }
+
+    return bytesRead;
+}
+
+void bufferPrepareForWrite(Buffer* buffer) {
+    buffer->remaining = buffer->size;
+}
+
+ssize_t bufferWrite(Buffer* buffer, int fd) {
+    assert(buffer->remaining > 0);
+    assert(buffer->remaining <= buffer->size);
+
+    ssize_t bytesWritten = write(fd, 
+            buffer->data + buffer->size - buffer->remaining,
+            buffer->remaining);
+
+    if (bytesWritten >= 0) {
+        buffer->remaining -= bytesWritten;
+
+        LOGD("Buffer bytes written: %d", (int) bytesWritten);
+        LOGD("Buffer size: %d", (int) buffer->size);
+        LOGD("Buffer remaining: %d", (int) buffer->remaining);        
+
+        return buffer->remaining;
+    }
+
+    return bytesWritten;
+}
+
diff --git a/libcutils/buffer.h b/libcutils/buffer.h
new file mode 100644
index 0000000..d8bc108
--- /dev/null
+++ b/libcutils/buffer.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Byte buffer utilities.
+ */
+
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/** 
+ * Byte buffer of known size. Keeps track of how much data has been read
+ * into or written out of the buffer.
+ */
+typedef struct {
+    /** Buffered data. */
+    char* data;
+
+    union {
+        /** For reading. # of bytes we expect. */
+        size_t expected;
+
+        /** For writing. # of bytes to write. */
+        size_t remaining;
+    };
+
+    /** Actual # of bytes in the buffer. */
+    size_t size;
+
+    /** Amount of memory allocated for this buffer. */
+    size_t capacity;
+} Buffer;
+
+/**
+ * Returns true if all data has been read into the buffer.
+ */
+#define bufferReadComplete(buffer) (buffer->expected == buffer->size)
+
+/**
+ * Returns true if the buffer has been completely written.
+ */
+#define bufferWriteComplete(buffer) (buffer->remaining == 0)
+
+/**
+ * Creates a new buffer with the given initial capacity.
+ */
+Buffer* bufferCreate(size_t initialCapacity);
+
+/**
+ * Wraps an existing byte array.
+ */
+Buffer* bufferWrap(char* data, size_t capacity, size_t size);
+
+/**
+ * Frees and its data.
+ */
+void bufferFree(Buffer* buffer);
+
+/**
+ * Prepares buffer to read 'expected' number of bytes. Expands capacity if
+ * necessary. Returns 0 if successful or -1 if an error occurs allocating
+ * memory.
+ */
+int bufferPrepareForRead(Buffer* buffer, size_t expected);
+
+/**
+ * Reads some data into a buffer. Returns -1 in case of an error and sets 
+ * errno (see read()). Returns 0 for EOF. Updates buffer->size and returns
+ * the new size after a succesful read. 
+ *
+ * Precondition: buffer->size < buffer->expected
+ */
+ssize_t bufferRead(Buffer* buffer, int fd);
+
+/**
+ * Prepares a buffer to be written out.
+ */
+void bufferPrepareForWrite(Buffer* buffer);
+
+/**
+ * Writes data from buffer to the given fd. Returns -1 and sets errno in case
+ * of an error. Updates buffer->remaining and returns the number of remaining
+ * bytes to be written after a successful write. 
+ *
+ * Precondition: buffer->remaining > 0
+ */
+ssize_t bufferWrite(Buffer* buffer, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BUFFER_H */ 
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c
new file mode 100644
index 0000000..75fa6c6
--- /dev/null
+++ b/libcutils/config_utils.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+    cnode *node;
+
+    node = calloc(sizeof(cnode), 1);
+    if(node) {
+        node->name = name ? name : "";
+        node->value = value ? value : "";
+    }
+
+    return node;
+}
+
+cnode* config_find(cnode *root, const char *name)
+{
+    cnode *node, *match = NULL;
+
+    /* we walk the whole list, as we need to return the last (newest) entry */
+    for(node = root->first_child; node; node = node->next)
+        if(!strcmp(node->name, name))
+            match = node;
+
+    return match;
+}
+
+static cnode* _config_create(cnode *root, const char *name)
+{
+    cnode *node;
+
+    node = config_node(name, NULL);
+
+    if(root->last_child)
+        root->last_child->next = node;
+    else
+        root->first_child = node;
+
+    root->last_child = node;
+
+    return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+    cnode *node;
+        
+    node = config_find(root, name);
+    if(!node)
+        return _default;
+
+    switch(node->value[0]) {
+    case 'y':
+    case 'Y':
+    case '1':
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+const char* config_str(cnode *root, const char *name, const char *_default)
+{
+    cnode *node;
+
+    node = config_find(root, name);
+    if(!node)
+        return _default;
+    return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+    cnode *node;
+
+    node = config_find(root, name);
+    if(node)
+        node->value = value;
+    else {
+        node = _config_create(root, name);
+        node->value = value;
+    }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+    char *data;
+    char *text;
+    int len;
+    char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+    char c;
+    char *s;
+    char *data;
+
+    data = cs->data;
+
+    if(cs->next != 0) {
+        c = cs->next;
+        cs->next = 0;
+        goto got_c;
+    }
+
+restart:
+    for(;;) {
+        c = *data++;
+    got_c:
+        if(isspace(c))
+            continue;
+
+        switch(c) {
+        case 0:
+            return T_EOF;
+
+        case '#':
+            for(;;) {
+                switch(*data) {
+                case 0:
+                    cs->data = data;
+                    return T_EOF;
+                case '\n':
+                    cs->data = data + 1;
+                    goto restart;
+                default:
+                    data++;
+                }
+            }
+            break;
+            
+        case '.':
+            cs->data = data;
+            return T_DOT;
+
+        case '{':
+            cs->data = data;
+            return T_OBRACE;
+
+        case '}':
+            cs->data = data;
+            return T_CBRACE;
+
+        default:
+            s = data - 1;
+
+            if(value) {
+                for(;;) {
+                    if(*data == 0) {
+                        cs->data = data;
+                        break;
+                    }
+                    if(*data == '\n') {
+                        cs->data = data + 1;
+                        *data-- = 0;
+                        break;
+                    }
+                    data++;
+                }
+
+                    /* strip trailing whitespace */
+                while(data > s){
+                    if(!isspace(*data)) break;
+                    *data-- = 0;
+                }
+
+                goto got_text;                
+            } else {
+                for(;;) {
+                    if(isspace(*data)) {
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    }
+                    switch(*data) {
+                    case 0:
+                        cs->data = data;
+                        goto got_text;
+                    case '.':
+                    case '{':
+                    case '}':
+                        cs->next = *data;
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    default:
+                        data++;
+                    }
+                }
+            }
+        }
+    }
+
+got_text:
+    cs->text = s;
+    return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+    int tok = _lex(cs, value);
+    printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+           tok == T_TEXT ? cs->text : "");
+    return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+    for(;;){
+        switch(lex(cs, 0)){
+        case T_TEXT:
+            if(parse_expr(cs, node)) return -1;
+            continue;
+
+        case T_CBRACE:
+            return 0;
+
+        default:
+            return -1;
+        }
+    }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+    cnode *node;
+
+        /* last token was T_TEXT */
+    node = config_find(root, cs->text);
+    if(!node || *node->value)
+        node = _config_create(root, cs->text);
+
+    for(;;) {
+        switch(lex(cs, 1)) {
+        case T_DOT:
+            if(lex(cs, 0) != T_TEXT)
+                return -1;
+            node = _config_create(node, cs->text);
+            continue;
+
+        case T_TEXT:
+            node->value = cs->text;
+            return 0;
+
+        case T_OBRACE:
+            return parse_block(cs, node);
+
+        default:
+            return -1;
+        }
+    }
+}
+
+void config_load(cnode *root, char *data)
+{
+    if(data != 0) {
+        cstate cs;
+        cs.data = data;
+        cs.next = 0;
+
+        for(;;) {
+            switch(lex(&cs, 0)) {
+            case T_TEXT:
+                if(parse_expr(&cs, root))
+                    return;
+                break;
+            default:
+                return;
+            }
+        }
+    }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+    char *data;
+    data = load_file(fn, 0);
+    config_load(root, data);
+}
diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c
new file mode 100644
index 0000000..23dda8a
--- /dev/null
+++ b/libcutils/cpu_info.c
@@ -0,0 +1,83 @@
+/* libs/cutils/cpu_info.c
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/cpu_info.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// we cache the serial number here.
+// this is also used as a fgets() line buffer when we are reading /proc/cpuinfo
+static char serial_number[100] = { 0 };
+
+extern const char* get_cpu_serial_number(void)
+{
+    if (serial_number[0] == 0)
+    {
+        FILE* file;
+        char* chp, *end;
+        char* whitespace;
+        int length;
+        
+        // read serial number from /proc/cpuinfo
+        file = fopen("proc/cpuinfo", "r");
+        if (! file)
+            return NULL;
+        
+        while ((chp = fgets(serial_number, sizeof(serial_number), file)) != NULL)
+        {
+            // look for something like "Serial          : 999206122a03591c"
+
+            if (strncmp(chp, "Serial", 6) != 0)
+                continue;
+            
+            chp = strchr(chp, ':');
+            if (!chp)
+                continue;
+                
+             // skip colon and whitespace
+            while ( *(++chp) == ' ') {}
+            
+            // truncate trailing whitespace
+            end = chp;
+            while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r')
+                ++end;
+            *end = 0; 
+            
+            whitespace = strchr(chp, ' ');
+            if (whitespace)
+                *whitespace = 0;
+            whitespace = strchr(chp, '\t');
+            if (whitespace)
+                *whitespace = 0;
+            whitespace = strchr(chp, '\r');
+            if (whitespace)
+                *whitespace = 0;
+            whitespace = strchr(chp, '\n');
+            if (whitespace)
+                *whitespace = 0;
+
+            // shift serial number to beginning of the buffer
+            memmove(serial_number, chp, strlen(chp) + 1);
+            break;
+        }
+        
+        fclose(file);
+    }
+
+    return (serial_number[0] ? serial_number : NULL);
+}
diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c
new file mode 100644
index 0000000..be14af6
--- /dev/null
+++ b/libcutils/dir_hash.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sha1.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <resolv.h>
+
+#include <cutils/dir_hash.h>
+
+/**
+ * Copies, if it fits within max_output_string bytes, into output_string
+ * a hash of the contents, size, permissions, uid, and gid of the file
+ * specified by path, using the specified algorithm.  Returns the length
+ * of the output string, or a negative number if the buffer is too short.
+ */
+int get_file_hash(HashAlgorithm algorithm, const char *path,
+                  char *output_string, size_t max_output_string) {
+    SHA1_CTX context;
+    struct stat sb;
+    unsigned char md[SHA1_DIGEST_LENGTH];
+    int used;
+    size_t n;
+
+    if (algorithm != SHA_1) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (stat(path, &sb) != 0) {
+        return -1;
+    }
+
+    if (S_ISLNK(sb.st_mode)) {
+        char buf[PATH_MAX];
+        int len;
+
+        len = readlink(path, buf, sizeof(buf));
+        if (len < 0) {
+            return -1;
+        }
+
+        SHA1Init(&context);
+        SHA1Update(&context, (unsigned char *) buf, len);
+        SHA1Final(md, &context);
+    } else if (S_ISREG(sb.st_mode)) {
+        char buf[10000];
+        FILE *f = fopen(path, "rb");
+        int len;
+
+        if (f == NULL) {
+            return -1;
+        }
+
+        SHA1Init(&context);
+
+        while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
+            SHA1Update(&context, (unsigned char *) buf, len);
+        }
+
+        if (ferror(f)) {
+            fclose(f);
+            return -1;
+        }
+
+        fclose(f);
+        SHA1Final(md, &context);
+    }
+
+    if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) {
+        used = b64_ntop(md, SHA1_DIGEST_LENGTH,
+                        output_string, max_output_string);
+        if (used < 0) {
+            errno = ENOSPC;
+            return -1;
+        }
+
+        n = snprintf(output_string + used, max_output_string - used,
+                     " %d 0%o %d %d", (int) sb.st_size, sb.st_mode,
+                     (int) sb.st_uid, (int) sb.st_gid);
+    } else {
+        n = snprintf(output_string, max_output_string,
+                     "- - 0%o %d %d", sb.st_mode,
+                     (int) sb.st_uid, (int) sb.st_gid);
+    }
+
+    if (n >= max_output_string - used) {
+        errno = ENOSPC;
+        return -(used + n);
+    }
+
+    return used + n;
+}
+
+struct list {
+    char *name;
+    struct list *next;
+};
+
+static int cmp(const void *a, const void *b) {
+    struct list *const *ra = a;
+    struct list *const *rb = b;
+
+    return strcmp((*ra)->name, (*rb)->name);
+}
+
+static int recurse(HashAlgorithm algorithm, const char *directory_path,
+                    struct list **out) {
+    struct list *list = NULL;
+    struct list *f;
+
+    struct dirent *de;
+    DIR *d = opendir(directory_path);
+
+    if (d == NULL) {
+        return -1;
+    }
+
+    while ((de = readdir(d)) != NULL) {
+        if (strcmp(de->d_name, ".") == 0) {
+            continue;
+        }
+        if (strcmp(de->d_name, "..") == 0) {
+            continue;
+        }
+
+        char *name = malloc(strlen(de->d_name) + 1);
+        struct list *node = malloc(sizeof(struct list));
+
+        if (name == NULL || node == NULL) {
+            struct list *next;
+            for (f = list; f != NULL; f = next) {
+                next = f->next;
+                free(f->name);
+                free(f);
+            }
+
+            free(name);
+            free(node);
+            return -1;
+        }
+
+        strcpy(name, de->d_name);
+
+        node->name = name;
+        node->next = list;
+        list = node;
+    }
+
+    closedir(d);
+
+    for (f = list; f != NULL; f = f->next) {
+        struct stat sb;
+        char *name;
+        char outstr[NAME_MAX + 100];
+        char *keep;
+        struct list *res;
+
+        name = malloc(strlen(f->name) + strlen(directory_path) + 2);
+        if (name == NULL) {
+            struct list *next;
+            for (f = list; f != NULL; f = f->next) {
+                next = f->next;
+                free(f->name);
+                free(f);
+            }
+            for (f = *out; f != NULL; f = f->next) {
+                next = f->next;
+                free(f->name);
+                free(f);
+            }
+            *out = NULL;
+            return -1;
+        }
+
+        sprintf(name, "%s/%s", directory_path, f->name);
+
+        int len = get_file_hash(algorithm, name,
+                                outstr, sizeof(outstr));
+        if (len < 0) {
+            // should not happen
+            return -1;
+        }
+
+        keep = malloc(len + strlen(name) + 3);
+        res = malloc(sizeof(struct list));
+
+        if (keep == NULL || res == NULL) {
+            struct list *next;
+            for (f = list; f != NULL; f = f->next) {
+                next = f->next;
+                free(f->name);
+                free(f);
+            }
+            for (f = *out; f != NULL; f = f->next) {
+                next = f->next;
+                free(f->name);
+                free(f);
+            }
+            *out = NULL;
+
+            free(keep);
+            free(res);
+            return -1;
+        }
+
+        sprintf(keep, "%s %s\n", name, outstr);
+
+        res->name = keep;
+        res->next = *out;
+        *out = res;
+
+        if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
+            if (recurse(algorithm, name, out) < 0) {
+                struct list *next;
+                for (f = list; f != NULL; f = next) {
+                    next = f->next;
+                    free(f->name);
+                    free(f);
+                }
+
+                return -1;
+            }
+        }
+    }
+
+    struct list *next;
+    for (f = list; f != NULL; f = next) {
+        next = f->next;
+
+        free(f->name);
+        free(f);
+    }
+}
+
+/**
+ * Allocates a string containing the names and hashes of all files recursively
+ * reached under the specified directory_path, using the specified algorithm.
+ * The string is returned as *output_string; the return value is the length
+ * of the string, or a negative number if there was a failure.
+ */
+int get_recursive_hash_manifest(HashAlgorithm algorithm,
+                                const char *directory_path,
+                                char **output_string) {
+    struct list *out = NULL;
+    struct list *r;
+    struct list **list;
+    int count = 0;
+    int len = 0;
+    int retlen = 0;
+    int i;
+    char *buf;
+    
+    if (recurse(algorithm, directory_path, &out) < 0) {
+        return -1;
+    }
+
+    for (r = out; r != NULL; r = r->next) {
+        count++;
+        len += strlen(r->name);
+    }
+
+    list = malloc(count * sizeof(struct list *));
+    if (list == NULL) {
+        struct list *next;
+        for (r = out; r != NULL; r = next) {
+            next = r->next;
+            free(r->name);
+            free(r);
+        }
+        return -1;
+    }
+
+    count = 0;
+    for (r = out; r != NULL; r = r->next) {
+        list[count++] = r;
+    }
+
+    qsort(list, count, sizeof(struct list *), cmp);
+
+    buf = malloc(len + 1);
+    if (buf == NULL) {
+        struct list *next;
+        for (r = out; r != NULL; r = next) {
+            next = r->next;
+            free(r->name);
+            free(r);
+        }
+        free(list);
+        return -1;
+    }
+
+    for (i = 0; i < count; i++) {
+        int n = strlen(list[i]->name);
+
+        strcpy(buf + retlen, list[i]->name);
+        retlen += n;
+    }
+
+    free(list);
+
+    struct list *next;
+    for (r = out; r != NULL; r = next) {
+        next = r->next;
+
+        free(r->name);
+        free(r);
+    }
+
+    *output_string = buf;
+    return retlen;
+}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
new file mode 100644
index 0000000..1ced147
--- /dev/null
+++ b/libcutils/dlmalloc_stubs.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* No-op stubs for functions defined in system/bionic/bionic/dlmalloc.c.
+ */
+void dlmalloc_walk_free_pages()
+{
+}
+
+void dlmalloc_walk_heap()
+{
+}
+
+void dlmalloc_trim()
+{
+}
diff --git a/libcutils/fdevent.c b/libcutils/fdevent.c
new file mode 100644
index 0000000..4cf46fa
--- /dev/null
+++ b/libcutils/fdevent.c
@@ -0,0 +1,506 @@
+/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
+**
+** Copyright 2006, Brian Swetland <swetland@frotz.net>
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <fcntl.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <cutils/fdevent.h>
+
+#define TRACE(x...) fprintf(stderr,x)
+
+#define DEBUG 0
+
+static void fatal(const char *fn, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr, "%s:", fn);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    abort();
+}
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+            fde->state & FDE_READ ? 'R' : ' ',
+            fde->state & FDE_WRITE ? 'W' : ' ',
+            fde->state & FDE_ERROR ? 'E' : ' ',
+            info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK  0x00ff
+#define FDE_STATEMASK  0xff00
+
+#define FDE_ACTIVE     0x0100
+#define FDE_PENDING    0x0200
+#define FDE_CREATED    0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+    .next = &list_pending,
+    .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+#ifdef CRAPTASTIC
+//HAVE_EPOLL
+
+#include <sys/epoll.h>
+
+static int epoll_fd = -1;
+
+static void fdevent_init()
+{
+        /* XXX: what's a good size for the passed in hint? */
+    epoll_fd = epoll_create(256);
+    
+    if(epoll_fd < 0) {
+        perror("epoll_create() failed");
+        exit(1);
+    }
+
+        /* mark for close-on-exec */
+    fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+    struct epoll_event ev;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+#if 0    
+    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+        perror("epoll_ctl() failed\n");
+        exit(1);
+    }
+#endif
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+    struct epoll_event ev;
+    
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+        /* technically we only need to delete if we
+        ** were actively monitoring events, but let's
+        ** be aggressive and do it anyway, just in case
+        ** something's out of sync
+        */
+    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+    struct epoll_event ev;
+    int active;
+    
+    active = (fde->state & FDE_EVENTMASK) != 0;
+    
+    memset(&ev, 0, sizeof(ev));
+    ev.events = 0;
+    ev.data.ptr = fde;
+
+    if(events & FDE_READ) ev.events |= EPOLLIN;
+    if(events & FDE_WRITE) ev.events |= EPOLLOUT;
+    if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+
+    if(active) {
+            /* we're already active. if we're changing to *no*
+            ** events being monitored, we need to delete, otherwise
+            ** we need to just modify
+            */
+        if(ev.events) {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        } else {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        }
+    } else {
+            /* we're not active.  if we're watching events, we need
+            ** to add, otherwise we can just do nothing
+            */
+        if(ev.events) {
+            if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+                perror("epoll_ctl() failed\n");
+                exit(1);
+            }
+        }
+    }
+}
+
+static void fdevent_process()
+{
+    struct epoll_event events[256];
+    fdevent *fde;
+    int i, n;
+
+    n = epoll_wait(epoll_fd, events, 256, -1);
+
+    if(n < 0) {
+        if(errno == EINTR) return;
+        perror("epoll_wait");
+        exit(1);
+    }
+
+    for(i = 0; i < n; i++) {
+        struct epoll_event *ev = events + i;
+        fde = ev->data.ptr;
+
+        if(ev->events & EPOLLIN) {
+            fde->events |= FDE_READ;
+        }
+        if(ev->events & EPOLLOUT) {
+            fde->events |= FDE_WRITE;
+        }
+        if(ev->events & (EPOLLERR | EPOLLHUP)) {
+            fde->events |= FDE_ERROR;
+        }
+        if(fde->events) {
+            if(fde->state & FDE_PENDING) continue;
+            fde->state |= FDE_PENDING;
+            fdevent_plist_enqueue(fde);
+        }
+    }
+}
+
+#else /* USE_SELECT */
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+static fd_set read_fds;
+static fd_set write_fds;
+static fd_set error_fds;
+
+static int select_n = 0;
+
+static void fdevent_init(void)
+{
+    FD_ZERO(&read_fds);
+    FD_ZERO(&write_fds);
+    FD_ZERO(&error_fds);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+    if(fde->fd >= select_n) {
+        select_n = fde->fd + 1;
+    }
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+    int i, n;
+    
+    FD_CLR(fde->fd, &read_fds);
+    FD_CLR(fde->fd, &write_fds);
+    FD_CLR(fde->fd, &error_fds);
+
+    for(n = 0, i = 0; i < select_n; i++) {
+        if(fd_table[i] != 0) n = i;
+    }
+    select_n = n + 1;
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+    if(events & FDE_READ) {
+        FD_SET(fde->fd, &read_fds);
+    } else {
+        FD_CLR(fde->fd, &read_fds);
+    }
+    if(events & FDE_WRITE) {
+        FD_SET(fde->fd, &write_fds);
+    } else {
+        FD_CLR(fde->fd, &write_fds);
+    }
+    if(events & FDE_ERROR) {
+        FD_SET(fde->fd, &error_fds);
+    } else {
+        FD_CLR(fde->fd, &error_fds);
+    }
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;    
+}
+
+static void fdevent_process()
+{
+    int i, n;
+    fdevent *fde;
+    unsigned events;
+    fd_set rfd, wfd, efd;
+
+    memcpy(&rfd, &read_fds, sizeof(fd_set));
+    memcpy(&wfd, &write_fds, sizeof(fd_set));
+    memcpy(&efd, &error_fds, sizeof(fd_set));
+    
+    n = select(select_n, &rfd, &wfd, &efd, 0);
+    
+    if(n < 0) {
+        if(errno == EINTR) return;
+        perror("select");
+        return;
+    }
+
+    for(i = 0; (i < select_n) && (n > 0); i++) {
+        events = 0;
+        if(FD_ISSET(i, &rfd)) events |= FDE_READ;
+        if(FD_ISSET(i, &wfd)) events |= FDE_WRITE;
+        if(FD_ISSET(i, &efd)) events |= FDE_ERROR;
+
+        if(events) {
+            n--;
+            
+            fde = fd_table[i];
+            if(fde == 0) FATAL("missing fde for fd %d\n", i);
+
+            fde->events |= events;
+            
+            if(fde->state & FDE_PENDING) continue;
+            fde->state |= FDE_PENDING;
+            fdevent_plist_enqueue(fde);
+        }
+    }
+}
+
+#endif
+
+static void fdevent_register(fdevent *fde)
+{
+    if(fde->fd < 0) {
+        FATAL("bogus negative fd (%d)\n", fde->fd);
+    }
+    
+    if(fde->fd >= fd_table_max) {
+        int oldmax = fd_table_max;
+        if(fde->fd > 32000) {
+            FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+        }
+        if(fd_table_max == 0) {
+            fdevent_init();
+            fd_table_max = 256;
+        }
+        while(fd_table_max <= fde->fd) {
+            fd_table_max *= 2;
+        }
+        fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+        if(fd_table == 0) {
+            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+        }
+        memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+    }
+
+    fd_table[fde->fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+    if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
+        FATAL("fd out of range (%d)\n", fde->fd);
+    }
+
+    if(fd_table[fde->fd] != fde) {
+        FATAL("fd_table out of sync");
+    }
+
+    fd_table[fde->fd] = 0;
+
+    if(!(fde->state & FDE_DONT_CLOSE)) {
+        dump_fde(fde, "close");
+        close(fde->fd);
+    }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+    fdevent *list = &list_pending;
+
+    node->next = list;
+    node->prev = list->prev;
+    node->prev->next = node;
+    list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+    node->prev->next = node->next;
+    node->next->prev = node->prev;
+    node->next = 0;
+    node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+    fdevent *list = &list_pending;
+    fdevent *node = list->next;
+    
+    if(node == list) return 0;
+    
+    list->next = node->next;
+    list->next->prev = list;
+    node->next = 0;
+    node->prev = 0;
+
+    return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+    if(fde == 0) return 0;
+    fdevent_install(fde, fd, func, arg);
+    fde->state |= FDE_CREATED;
+    return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+    if(fde == 0) return;
+    if(!(fde->state & FDE_CREATED)) {
+        FATAL("fde %p not created by fdevent_create()\n", fde);
+    }
+    fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) 
+{
+    memset(fde, 0, sizeof(fdevent));
+    fde->state = FDE_ACTIVE;
+    fde->fd = fd;
+    fde->func = func;
+    fde->arg = arg;
+
+#ifndef HAVE_WINSOCK
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+    fdevent_register(fde);
+    dump_fde(fde, "connect");
+    fdevent_connect(fde);
+    fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+    if(fde->state & FDE_PENDING) {
+        fdevent_plist_remove(fde);
+    }
+
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_disconnect(fde);
+        dump_fde(fde, "disconnect");    
+        fdevent_unregister(fde);
+    }
+
+    fde->state = 0;
+    fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+    events &= FDE_EVENTMASK;
+    
+    if((fde->state & FDE_EVENTMASK) == events) return;
+    
+    if(fde->state & FDE_ACTIVE) {
+        fdevent_update(fde, events);
+        dump_fde(fde, "update");
+    }
+
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+
+    if(fde->state & FDE_PENDING) {
+            /* if we're pending, make sure
+            ** we don't signal an event that
+            ** is no longer wanted.
+            */
+        fde->events &= (~events);
+        if(fde->events == 0) {
+            fdevent_plist_remove(fde);
+            fde->state &= (~FDE_PENDING);
+        }
+    }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+    fdevent_set(
+        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+    fdevent *fde;
+    
+    for(;;) {
+#if DEBUG
+        fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+        fdevent_process();
+        
+        while((fde = fdevent_plist_dequeue())) {
+            unsigned events = fde->events;
+            fde->events = 0;
+            fde->state &= (~FDE_PENDING);
+            dump_fde(fde, "callback");
+            fde->func(fde->fd, events, fde->arg);
+        }
+    }
+}
+
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
new file mode 100644
index 0000000..e29bc24
--- /dev/null
+++ b/libcutils/hashmap.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/hashmap.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/threads.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct Entry Entry;
+struct Entry {
+    void* key;
+    int hash;
+    void* value;
+    Entry* next;
+};
+
+struct Hashmap {
+    Entry** buckets;
+    size_t bucketCount;
+    int (*hash)(void* key);
+    bool (*equals)(void* keyA, void* keyB);
+    mutex_t lock; 
+    size_t size;
+};
+
+Hashmap* hashmapCreate(size_t initialCapacity,
+        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
+    assert(hash != NULL);
+    assert(equals != NULL);
+    
+    Hashmap* map = malloc(sizeof(Hashmap));
+    if (map == NULL) {
+        return NULL;
+    }
+    
+    // 0.75 load factor.
+    size_t minimumBucketCount = initialCapacity * 4 / 3;
+    map->bucketCount = 1;
+    while (map->bucketCount <= minimumBucketCount) {
+        // Bucket count must be power of 2.
+        map->bucketCount <<= 1; 
+    }
+
+    map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+    if (map->buckets == NULL) {
+        free(map);
+        return NULL;
+    }
+    
+    map->size = 0;
+
+    map->hash = hash;
+    map->equals = equals;
+    
+    mutex_init(&map->lock);
+    
+    return map;
+}
+
+/**
+ * Hashes the given key.
+ */
+static inline int hashKey(Hashmap* map, void* key) {
+    int h = map->hash(key);
+
+    // We apply this secondary hashing discovered by Doug Lea to defend
+    // against bad hashes.
+    h += ~(h << 9);
+    h ^= (((unsigned int) h) >> 14);
+    h += (h << 4);
+    h ^= (((unsigned int) h) >> 10);
+       
+    return h;
+}
+
+size_t hashmapSize(Hashmap* map) {
+    return map->size;
+}
+
+static inline size_t calculateIndex(size_t bucketCount, int hash) {
+    return ((size_t) hash) & (bucketCount - 1);
+}
+
+static void expandIfNecessary(Hashmap* map) {
+    // If the load factor exceeds 0.75...
+    if (map->size > (map->bucketCount * 3 / 4)) {
+        // Start off with a 0.33 load factor.
+        size_t newBucketCount = map->bucketCount << 1;
+        Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+        if (newBuckets == NULL) {
+            // Abort expansion.
+            return;
+        }
+        
+        // Move over existing entries.
+        size_t i;
+        for (i = 0; i < map->bucketCount; i++) {
+            Entry* entry = map->buckets[i];
+            while (entry != NULL) {
+                Entry* next = entry->next;
+                size_t index = calculateIndex(newBucketCount, entry->hash);
+                entry->next = newBuckets[index];
+                newBuckets[index] = entry;
+                entry = next;
+            }
+        }
+
+        // Copy over internals.
+        free(map->buckets);
+        map->buckets = newBuckets;
+        map->bucketCount = newBucketCount;
+    }
+}
+
+void hashmapLock(Hashmap* map) {
+    mutex_lock(&map->lock);
+}
+
+void hashmapUnlock(Hashmap* map) {
+    mutex_unlock(&map->lock);
+}
+
+void hashmapFree(Hashmap* map) {
+    size_t i;
+    for (i = 0; i < map->bucketCount; i++) {
+        Entry* entry = map->buckets[i];
+        while (entry != NULL) {
+            Entry* next = entry->next;
+            free(entry);
+            entry = next;
+        }
+    }
+    free(map->buckets);
+    mutex_destroy(&map->lock);
+    free(map);
+}
+
+int hashmapHash(void* key, size_t keySize) {
+    int h = keySize;
+    char* data = (char*) key;
+    size_t i;
+    for (i = 0; i < keySize; i++) {
+        h = h * 31 + *data;
+        data++;
+    }
+    return h;
+}
+
+static Entry* createEntry(void* key, int hash, void* value) {
+    Entry* entry = malloc(sizeof(Entry));
+    if (entry == NULL) {
+        return NULL;
+    }
+    entry->key = key;
+    entry->hash = hash;
+    entry->value = value;
+    entry->next = NULL;
+    return entry;
+}
+
+static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
+        bool (*equals)(void*, void*)) {
+    if (keyA == keyB) {
+        return true;
+    }
+    if (hashA != hashB) {
+        return false;
+    }
+    return equals(keyA, keyB);
+}
+
+void* hashmapPut(Hashmap* map, void* key, void* value) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry** p = &(map->buckets[index]);
+    while (true) {
+        Entry* current = *p;
+
+        // Add a new entry.
+        if (current == NULL) {
+            *p = createEntry(key, hash, value);
+            if (*p == NULL) {
+                errno = ENOMEM;
+                return NULL;
+            }
+            map->size++;
+            expandIfNecessary(map);
+            return NULL;
+        }
+
+        // Replace existing entry.
+        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+            void* oldValue = current->value;
+            current->value = value;
+            return oldValue;
+        }
+
+        // Move to next entry.
+        p = &current->next;
+    }
+}
+
+void* hashmapGet(Hashmap* map, void* key) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry* entry = map->buckets[index];
+    while (entry != NULL) {
+        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+            return entry->value;
+        }
+        entry = entry->next;
+    }
+
+    return NULL;
+}
+
+bool hashmapContainsKey(Hashmap* map, void* key) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry* entry = map->buckets[index];
+    while (entry != NULL) {
+        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+            return true;
+        }
+        entry = entry->next;
+    }
+
+    return false;
+}
+
+void* hashmapMemoize(Hashmap* map, void* key, 
+        void* (*initialValue)(void* key, void* context), void* context) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    Entry** p = &(map->buckets[index]);
+    while (true) {
+        Entry* current = *p;
+
+        // Add a new entry.
+        if (current == NULL) {
+            *p = createEntry(key, hash, NULL);
+            if (*p == NULL) {
+                errno = ENOMEM;
+                return NULL;
+            }
+            void* value = initialValue(key, context);
+            (*p)->value = value;
+            map->size++;
+            expandIfNecessary(map);
+            return value;
+        }
+
+        // Return existing value.
+        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+            return current->value;
+        }
+
+        // Move to next entry.
+        p = &current->next;
+    }
+}
+
+void* hashmapRemove(Hashmap* map, void* key) {
+    int hash = hashKey(map, key);
+    size_t index = calculateIndex(map->bucketCount, hash);
+
+    // Pointer to the current entry.
+    Entry** p = &(map->buckets[index]);
+    Entry* current;
+    while ((current = *p) != NULL) {
+        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+            void* value = current->value;
+            *p = current->next;
+            free(current);
+            map->size--;
+            return value;
+        }
+
+        p = &current->next;
+    }
+
+    return NULL;
+}
+
+void hashmapForEach(Hashmap* map, 
+        bool (*callback)(void* key, void* value, void* context),
+        void* context) {
+    size_t i;
+    for (i = 0; i < map->bucketCount; i++) {
+        Entry* entry = map->buckets[i];
+        while (entry != NULL) {
+            if (!callback(entry->key, entry->value, context)) {
+                return;
+            }
+            entry = entry->next;
+        }
+    }
+}
+
+size_t hashmapCurrentCapacity(Hashmap* map) {
+    size_t bucketCount = map->bucketCount;
+    return bucketCount * 3 / 4;
+}
+
+size_t hashmapCountCollisions(Hashmap* map) {
+    size_t collisions = 0;
+    size_t i;
+    for (i = 0; i < map->bucketCount; i++) {
+        Entry* entry = map->buckets[i];
+        while (entry != NULL) {
+            if (entry->next != NULL) {
+                collisions++;
+            }
+            entry = entry->next;
+        }
+    }
+    return collisions;
+}
+
+int hashmapIntHash(void* key) {
+    // Return the key value itself.
+    return *((int*) key);
+}
+
+bool hashmapIntEquals(void* keyA, void* keyB) {
+    int a = *((int*) keyA);
+    int b = *((int*) keyB);
+    return a == b;
+}
diff --git a/libcutils/load_file.c b/libcutils/load_file.c
new file mode 100644
index 0000000..99f2965
--- /dev/null
+++ b/libcutils/load_file.c
@@ -0,0 +1,51 @@
+/* libs/cutils/load_file.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz + 1);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+    data[sz] = 0;
+
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
diff --git a/libcutils/loghack.h b/libcutils/loghack.h
new file mode 100644
index 0000000..2bfffe4
--- /dev/null
+++ b/libcutils/loghack.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is a temporary hack to enable logging from cutils.
+ */
+
+#ifndef _CUTILS_LOGHACK_H
+#define _CUTILS_LOGHACK_H
+
+#ifdef HAVE_ANDROID_OS
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG(level, ...) \
+        ((void)printf("cutils:" level "/" LOG_TAG ": " __VA_ARGS__))
+#define LOGV(...)   LOG("V", __VA_ARGS__)
+#define LOGD(...)   LOG("D", __VA_ARGS__)
+#define LOGI(...)   LOG("I", __VA_ARGS__)
+#define LOGW(...)   LOG("W", __VA_ARGS__)
+#define LOGE(...)   LOG("E", __VA_ARGS__)
+#define LOG_ALWAYS_FATAL(...)   do { LOGE(__VA_ARGS__); exit(1); } while (0)
+#endif
+
+#endif // _CUTILS_LOGHACK_H
diff --git a/libcutils/memory.c b/libcutils/memory.c
new file mode 100644
index 0000000..ef6c7e6
--- /dev/null
+++ b/libcutils/memory.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/memory.h>
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+    size >>= 1;
+    while (size--) {
+        *dst++ = value;
+    }
+}
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+    size >>= 2;
+    while (size--) {
+        *dst++ = value;
+    }
+}
+
+#if !HAVE_STRLCPY
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/* Implementation of strlcpy() for platforms that don't already have it. */
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = *s++) == '\0')
+				break;
+		}
+  }
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
+#endif
diff --git a/libcutils/memset32.S b/libcutils/memset32.S
new file mode 100644
index 0000000..4697265
--- /dev/null
+++ b/libcutils/memset32.S
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ *  memset32.S
+ *
+ */
+
+    .text
+    .align
+
+    .global android_memset32
+    .type   android_memset32, %function
+    .global android_memset16
+    .type   android_memset16, %function
+
+        /*
+         * Optimized memset32 and memset16 for ARM.
+         *
+         * void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+         * void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+         *
+         */
+
+android_memset16:
+        .fnstart
+        cmp         r2, #1
+        bxle        lr
+
+        /* expand the data to 32 bits */
+        mov         r1, r1, lsl #16
+        orr         r1, r1, r1, lsr #16
+
+        /* align to 32 bits */
+        tst         r0, #2
+        strneh      r1, [r0], #2
+        subne       r2, r2, #2
+        .fnend
+
+android_memset32:
+        .fnstart
+        .save       {lr}
+        str         lr, [sp, #-4]!
+
+        /* align the destination to a cache-line */
+        mov         r12, r1
+        mov         lr, r1
+        rsb         r3, r0, #0
+        ands        r3, r3, #0x1C
+        beq         .Laligned32
+        cmp         r3, r2
+        andhi       r3, r2, #0x1C
+        sub         r2, r2, r3
+
+        /* conditionally writes 0 to 7 words (length in r3) */
+        movs        r3, r3, lsl #28
+        stmcsia     r0!, {r1, lr}
+        stmcsia     r0!, {r1, lr}
+        stmmiia     r0!, {r1, lr}
+        movs        r3, r3, lsl #2
+        strcs       r1, [r0], #4
+
+.Laligned32:
+        mov         r3, r1
+1:      subs        r2, r2, #32
+        stmhsia     r0!, {r1,r3,r12,lr}
+        stmhsia     r0!, {r1,r3,r12,lr}
+        bhs         1b
+        add         r2, r2, #32
+
+        /* conditionally stores 0 to 30 bytes */
+        movs        r2, r2, lsl #28
+        stmcsia     r0!, {r1,r3,r12,lr}
+        stmmiia     r0!, {r1,lr}
+        movs        r2, r2, lsl #2
+        strcs       r1, [r0], #4
+        strmih      lr, [r0], #2
+
+        ldr         lr, [sp], #4
+        bx          lr
+        .fnend
diff --git a/libcutils/mq.c b/libcutils/mq.c
new file mode 100644
index 0000000..3b65f1f
--- /dev/null
+++ b/libcutils/mq.c
@@ -0,0 +1,1357 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "mq"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <cutils/array.h>
+#include <cutils/hashmap.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+#include "buffer.h"
+
+/** Number of dead peers to remember. */
+#define PEER_HISTORY (16)
+
+typedef struct sockaddr SocketAddress;
+typedef struct sockaddr_un UnixAddress;
+
+/** 
+ * Process/user/group ID. We don't use ucred directly because it's only 
+ * available on Linux.
+ */
+typedef struct {
+    pid_t pid;
+    uid_t uid;
+    gid_t gid;
+} Credentials;
+
+/** Listens for bytes coming from remote peers. */
+typedef void BytesListener(Credentials credentials, char* bytes, size_t size);
+
+/** Listens for the deaths of remote peers. */
+typedef void DeathListener(pid_t pid);
+
+/** Types of packets. */
+typedef enum {
+    /** Request for a connection to another peer. */
+    CONNECTION_REQUEST, 
+
+    /** A connection to another peer. */
+    CONNECTION, 
+
+    /** Reports a failed connection attempt. */
+    CONNECTION_ERROR, 
+
+    /** A generic packet of bytes. */
+    BYTES,
+} PacketType;
+
+typedef enum {
+    /** Reading a packet header. */
+    READING_HEADER,
+
+    /** Waiting for a connection from the master. */
+    ACCEPTING_CONNECTION,
+
+    /** Reading bytes. */
+    READING_BYTES,
+} InputState;
+
+/** A packet header. */
+// TODO: Use custom headers for master->peer, peer->master, peer->peer.
+typedef struct {
+    PacketType type;
+    union {
+        /** Packet size. Used for BYTES. */
+        size_t size;
+
+        /** Credentials. Used for CONNECTION and CONNECTION_REQUEST. */
+        Credentials credentials; 
+    };
+} Header;
+
+/** A packet which will be sent to a peer. */
+typedef struct OutgoingPacket OutgoingPacket;
+struct OutgoingPacket {
+    /** Packet header. */
+    Header header; 
+    
+    union {
+        /** Connection to peer. Used with CONNECTION. */
+        int socket;
+        
+        /** Buffer of bytes. Used with BYTES. */
+        Buffer* bytes;
+    };
+
+    /** Frees all resources associated with this packet. */
+    void (*free)(OutgoingPacket* packet);
+   
+    /** Optional context. */
+    void* context;
+    
+    /** Next packet in the queue. */
+    OutgoingPacket* nextPacket;
+};
+
+/** Represents a remote peer. */
+typedef struct PeerProxy PeerProxy;
+
+/** Local peer state. You typically have one peer per process. */
+typedef struct {
+    /** This peer's PID. */
+    pid_t pid;
+    
+    /** 
+     * Map from pid to peer proxy. The peer has a peer proxy for each remote
+     * peer it's connected to. 
+     *
+     * Acquire mutex before use.
+     */
+    Hashmap* peerProxies;
+
+    /** Manages I/O. */
+    Selector* selector;
+   
+    /** Used to synchronize operations with the selector thread. */
+    pthread_mutex_t mutex; 
+
+    /** Is this peer the master? */
+    bool master;
+
+    /** Peer proxy for the master. */
+    PeerProxy* masterProxy;
+    
+    /** Listens for packets from remote peers. */
+    BytesListener* onBytes;
+    
+    /** Listens for deaths of remote peers. */
+    DeathListener* onDeath;
+    
+    /** Keeps track of recently dead peers. Requires mutex. */
+    pid_t deadPeers[PEER_HISTORY];
+    size_t deadPeerCursor;
+} Peer;
+
+struct PeerProxy {
+    /** Credentials of the remote process. */
+    Credentials credentials;
+
+    /** Keeps track of data coming in from the remote peer. */
+    InputState inputState;
+    Buffer* inputBuffer;
+    PeerProxy* connecting;
+
+    /** File descriptor for this peer. */
+    SelectableFd* fd;
+
+    /** 
+     * Queue of packets to be written out to the remote peer.
+     *
+     * Requires mutex.
+     */
+    // TODO: Limit queue length.
+    OutgoingPacket* currentPacket;
+    OutgoingPacket* lastPacket;
+    
+    /** Used to write outgoing header. */
+    Buffer outgoingHeader;
+    
+    /** True if this is the master's proxy. */
+    bool master;
+
+    /** Reference back to the local peer. */
+    Peer* peer;
+
+    /**
+     * Used in master only. Maps this peer proxy to other peer proxies to
+     * which the peer has been connected to. Maps pid to PeerProxy. Helps
+     * keep track of which connections we've sent to whom.
+     */
+    Hashmap* connections;
+};
+
+/** Server socket path. */
+static const char* MASTER_PATH = "/master.peer";
+
+/** Credentials of the master peer. */
+static const Credentials MASTER_CREDENTIALS = {0, 0, 0};
+
+/** Creates a peer proxy and adds it to the peer proxy map. */
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials);
+
+/** Sets the non-blocking flag on a descriptor. */
+static void setNonBlocking(int fd) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { 
+        LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+    } 
+    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 
+        LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+    } 
+}
+
+/** Closes a fd and logs a warning if the close fails. */
+static void closeWithWarning(int fd) {
+    int result = close(fd);
+    if (result == -1) {
+        LOGW("close() error: %s", strerror(errno));
+    }
+}
+
+/** Hashes pid_t keys. */
+static int pidHash(void* key) {
+    pid_t* pid = (pid_t*) key;
+    return (int) (*pid);
+}
+
+/** Compares pid_t keys. */
+static bool pidEquals(void* keyA, void* keyB) {
+    pid_t* a = (pid_t*) keyA;
+    pid_t* b = (pid_t*) keyB;
+    return *a == *b;
+}
+
+/** Gets the master address. Not thread safe. */
+static UnixAddress* getMasterAddress() {
+    static UnixAddress masterAddress;
+    static bool initialized = false;
+    if (initialized == false) {
+        masterAddress.sun_family = AF_LOCAL;
+        strcpy(masterAddress.sun_path, MASTER_PATH); 
+        initialized = true;
+    }
+    return &masterAddress;
+}
+
+/** Gets exclusive access to the peer for this thread. */
+static void peerLock(Peer* peer) {
+    pthread_mutex_lock(&peer->mutex);
+}
+
+/** Releases exclusive access to the peer. */
+static void peerUnlock(Peer* peer) {
+    pthread_mutex_unlock(&peer->mutex);
+}
+
+/** Frees a simple, i.e. header-only, outgoing packet. */
+static void outgoingPacketFree(OutgoingPacket* packet) {
+    LOGD("Freeing outgoing packet.");
+	free(packet);
+}
+
+/**
+ * Prepare to read a new packet from the peer.
+ */
+static void peerProxyExpectHeader(PeerProxy* peerProxy) {
+    peerProxy->inputState = READING_HEADER;
+    bufferPrepareForRead(peerProxy->inputBuffer, sizeof(Header));
+}
+
+/** Sets up the buffer for the outgoing header. */
+static void peerProxyPrepareOutgoingHeader(PeerProxy* peerProxy) {
+    peerProxy->outgoingHeader.data 
+        = (char*) &(peerProxy->currentPacket->header);
+    peerProxy->outgoingHeader.size = sizeof(Header);
+    bufferPrepareForWrite(&peerProxy->outgoingHeader);
+}
+
+/** Adds a packet to the end of the queue. Callers must have the mutex. */
+static void peerProxyEnqueueOutgoingPacket(PeerProxy* peerProxy,
+        OutgoingPacket* newPacket) {
+    newPacket->nextPacket = NULL; // Just in case.
+    if (peerProxy->currentPacket == NULL) {
+        // The queue is empty.
+        peerProxy->currentPacket = newPacket;
+        peerProxy->lastPacket = newPacket;
+        
+        peerProxyPrepareOutgoingHeader(peerProxy); 
+    } else {
+        peerProxy->lastPacket->nextPacket = newPacket;
+    }
+}
+
+/** Takes the peer lock and enqueues the given packet. */
+static void peerProxyLockAndEnqueueOutgoingPacket(PeerProxy* peerProxy,
+        OutgoingPacket* newPacket) {
+    Peer* peer = peerProxy->peer;
+    peerLock(peer);
+    peerProxyEnqueueOutgoingPacket(peerProxy, newPacket);
+    peerUnlock(peer);
+}
+
+/** 
+ * Frees current packet and moves to the next one. Returns true if there is
+ * a next packet or false if the queue is empty.
+ */
+static bool peerProxyNextPacket(PeerProxy* peerProxy) {
+    Peer* peer = peerProxy->peer;
+    peerLock(peer);
+    
+    OutgoingPacket* current = peerProxy->currentPacket;
+    
+    if (current == NULL) {
+    	// The queue is already empty.
+        peerUnlock(peer);
+        return false;
+    }
+    
+    OutgoingPacket* next = current->nextPacket;
+    peerProxy->currentPacket = next;
+    current->nextPacket = NULL;
+    current->free(current);
+    if (next == NULL) {
+        // The queue is empty.
+        peerProxy->lastPacket = NULL;
+        peerUnlock(peer);
+        return false;
+    } else {
+        peerUnlock(peer);
+        peerProxyPrepareOutgoingHeader(peerProxy); 
+
+        // TODO: Start writing next packet? It would reduce the number of
+        // system calls, but we could also starve other peers.
+        return true;
+    }
+}
+
+/**
+ * Checks whether a peer died recently.
+ */
+static bool peerIsDead(Peer* peer, pid_t pid) {
+    size_t i;
+    for (i = 0; i < PEER_HISTORY; i++) {
+        pid_t deadPeer = peer->deadPeers[i];
+        if (deadPeer == 0) {
+            return false;
+        }
+        if (deadPeer == pid) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Cleans up connection information.
+ */
+static bool peerProxyRemoveConnection(void* key, void* value, void* context) {
+    PeerProxy* deadPeer = (PeerProxy*) context;
+    PeerProxy* otherPeer = (PeerProxy*) value;
+    hashmapRemove(otherPeer->connections, &(deadPeer->credentials.pid));
+    return true;
+}
+
+/**
+ * Called when the peer dies.
+ */
+static void peerProxyKill(PeerProxy* peerProxy, bool errnoIsSet) {
+    if (errnoIsSet) {
+        LOGI("Peer %d died. errno: %s", peerProxy->credentials.pid, 
+                strerror(errno));
+    } else {
+        LOGI("Peer %d died.", peerProxy->credentials.pid);
+    }
+    
+    // If we lost the master, we're up a creek. We can't let this happen.
+    if (peerProxy->master) {    
+        LOG_ALWAYS_FATAL("Lost connection to master.");
+    }
+
+    Peer* localPeer = peerProxy->peer;
+    pid_t pid = peerProxy->credentials.pid;
+    
+    peerLock(localPeer);
+    
+    // Remember for awhile that the peer died.
+    localPeer->deadPeers[localPeer->deadPeerCursor] 
+        = peerProxy->credentials.pid;
+    localPeer->deadPeerCursor++;
+    if (localPeer->deadPeerCursor == PEER_HISTORY) {
+        localPeer->deadPeerCursor = 0;
+    }
+  
+    // Remove from peer map.
+    hashmapRemove(localPeer->peerProxies, &pid);
+    
+    // External threads can no longer get to this peer proxy, so we don't 
+    // need the lock anymore.
+    peerUnlock(localPeer);
+    
+    // Remove the fd from the selector.
+    if (peerProxy->fd != NULL) {
+        peerProxy->fd->remove = true;
+    }
+
+    // Clear outgoing packet queue.
+    while (peerProxyNextPacket(peerProxy)) {}
+
+    bufferFree(peerProxy->inputBuffer);
+
+    // This only applies to the master.
+    if (peerProxy->connections != NULL) {
+        // We can't leave these other maps pointing to freed memory.
+        hashmapForEach(peerProxy->connections, &peerProxyRemoveConnection, 
+                peerProxy);
+        hashmapFree(peerProxy->connections);
+    }
+
+    // Invoke death listener.
+    localPeer->onDeath(pid);
+
+    // Free the peer proxy itself.
+    free(peerProxy);
+}
+
+static void peerProxyHandleError(PeerProxy* peerProxy, char* functionName) {
+    if (errno == EINTR) {
+        // Log interruptions but otherwise ignore them.
+        LOGW("%s() interrupted.", functionName);
+    } else if (errno == EAGAIN) {
+    	LOGD("EWOULDBLOCK");
+        // Ignore.
+    } else {
+        LOGW("Error returned by %s().", functionName);
+        peerProxyKill(peerProxy, true);
+    }
+}
+
+/**
+ * Buffers output sent to a peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is empty.
+ */
+static bool peerProxyWriteFromBuffer(PeerProxy* peerProxy, Buffer* outgoing) {
+    ssize_t size = bufferWrite(outgoing, peerProxy->fd->fd);
+    if (size < 0) {
+        peerProxyHandleError(peerProxy, "write");
+        return false;
+    } else {
+        return bufferWriteComplete(outgoing);
+    }
+}
+
+/** Writes packet bytes to peer. */
+static void peerProxyWriteBytes(PeerProxy* peerProxy) {	
+	Buffer* buffer = peerProxy->currentPacket->bytes;
+	if (peerProxyWriteFromBuffer(peerProxy, buffer)) {
+        LOGD("Bytes written.");
+        peerProxyNextPacket(peerProxy);
+    }    
+}
+
+/** Sends a socket to the peer. */
+static void peerProxyWriteConnection(PeerProxy* peerProxy) {
+    int socket = peerProxy->currentPacket->socket;
+
+    // Why does sending and receiving fds have to be such a PITA?
+    struct msghdr msg;
+    struct iovec iov[1];
+    
+    union {
+        struct cmsghdr cm;
+        char control[CMSG_SPACE(sizeof(int))];
+    } control_un;
+   
+    struct cmsghdr *cmptr;
+    
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+    cmptr = CMSG_FIRSTHDR(&msg);
+    cmptr->cmsg_len = CMSG_LEN(sizeof(int));
+    cmptr->cmsg_level = SOL_SOCKET;
+    cmptr->cmsg_type = SCM_RIGHTS;
+   
+    // Store the socket in the message.
+    *((int *) CMSG_DATA(cmptr)) = peerProxy->currentPacket->socket;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    iov[0].iov_base = "";
+    iov[0].iov_len = 1;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    ssize_t result = sendmsg(peerProxy->fd->fd, &msg, 0);
+    
+    if (result < 0) {
+        peerProxyHandleError(peerProxy, "sendmsg");
+    } else {
+        // Success. Queue up the next packet.
+        peerProxyNextPacket(peerProxy);
+        
+    }
+}
+
+/**
+ * Writes some outgoing data.
+ */
+static void peerProxyWrite(SelectableFd* fd) {
+    // TODO: Try to write header and body with one system call.
+
+    PeerProxy* peerProxy = (PeerProxy*) fd->data;
+    OutgoingPacket* current = peerProxy->currentPacket;
+    
+    if (current == NULL) {
+        // We have nothing left to write.
+        return;
+    }
+
+    // Write the header.
+    Buffer* outgoingHeader = &peerProxy->outgoingHeader;
+    bool headerWritten = bufferWriteComplete(outgoingHeader);
+    if (!headerWritten) {
+        LOGD("Writing header...");
+        headerWritten = peerProxyWriteFromBuffer(peerProxy, outgoingHeader);
+        if (headerWritten) {
+            LOGD("Header written.");
+        }
+    }    
+
+    // Write body.
+    if (headerWritten) {
+        PacketType type = current->header.type;
+        switch (type) {
+            case CONNECTION:
+                peerProxyWriteConnection(peerProxy);
+                break;
+            case BYTES:
+                peerProxyWriteBytes(peerProxy);
+                break;
+            case CONNECTION_REQUEST:
+            case CONNECTION_ERROR:
+                // These packets consist solely of a header.
+                peerProxyNextPacket(peerProxy);
+                break;
+            default:
+                LOG_ALWAYS_FATAL("Unknown packet type: %d", type); 
+        }
+    }
+}
+
+/**
+ * Sets up a peer proxy's fd before we try to select() it.
+ */
+static void peerProxyBeforeSelect(SelectableFd* fd) {
+    LOGD("Before select...");
+
+    PeerProxy* peerProxy = (PeerProxy*) fd->data;
+  
+    peerLock(peerProxy->peer);
+    bool hasPackets = peerProxy->currentPacket != NULL;
+    peerUnlock(peerProxy->peer);
+    
+    if (hasPackets) {
+        LOGD("Packets found. Setting onWritable().");
+            
+        fd->onWritable = &peerProxyWrite;
+    } else {
+        // We have nothing to write.
+        fd->onWritable = NULL;
+    }
+}
+
+/** Prepare to read bytes from the peer. */
+static void peerProxyExpectBytes(PeerProxy* peerProxy, Header* header) {
+	LOGD("Expecting %d bytes.", header->size);
+	
+	peerProxy->inputState = READING_BYTES;
+    if (bufferPrepareForRead(peerProxy->inputBuffer, header->size) == -1) {
+        LOGW("Couldn't allocate memory for incoming data. Size: %u",
+                (unsigned int) header->size);    
+        
+        // TODO: Ignore the packet and log a warning?
+        peerProxyKill(peerProxy, false);
+    }
+}
+
+/**
+ * Gets a peer proxy for the given ID. Creates a peer proxy if necessary.
+ * Sends a connection request to the master if desired.
+ *
+ * Returns NULL if an error occurs. Sets errno to EHOSTDOWN if the peer died
+ * or ENOMEM if memory couldn't be allocated.
+ */
+static PeerProxy* peerProxyGetOrCreate(Peer* peer, pid_t pid, 
+        bool requestConnection) {
+    if (pid == peer->pid) {
+        errno = EINVAL;
+        return NULL;
+    }
+    
+    if (peerIsDead(peer, pid)) {
+        errno = EHOSTDOWN;
+        return NULL;
+    }
+    
+    PeerProxy* peerProxy = hashmapGet(peer->peerProxies, &pid);
+    if (peerProxy != NULL) {
+        return peerProxy;
+    }
+
+    // If this is the master peer, we already know about all peers.
+    if (peer->master) {
+        errno = EHOSTDOWN;
+        return NULL;
+    }
+
+    // Try to create a peer proxy.
+    Credentials credentials;
+    credentials.pid = pid;
+
+    // Fake gid and uid until we have the real thing. The real creds are
+    // filled in by masterProxyExpectConnection(). These fake creds will
+    // never be exposed to the user.
+    credentials.uid = 0;
+    credentials.gid = 0;
+
+    // Make sure we can allocate the connection request packet.
+    OutgoingPacket* packet = NULL;
+    if (requestConnection) {
+        packet = calloc(1, sizeof(OutgoingPacket));
+        if (packet == NULL) {
+            errno = ENOMEM;
+            return NULL;
+        }
+
+        packet->header.type = CONNECTION_REQUEST;
+        packet->header.credentials = credentials;
+        packet->free = &outgoingPacketFree;
+    }
+    
+    peerProxy = peerProxyCreate(peer, credentials);
+    if (peerProxy == NULL) {
+        free(packet);
+        errno = ENOMEM;
+        return NULL;
+    } else {
+        // Send a connection request to the master.
+        if (requestConnection) {
+            PeerProxy* masterProxy = peer->masterProxy;
+            peerProxyEnqueueOutgoingPacket(masterProxy, packet);
+        }
+        
+        return peerProxy;
+    }
+}
+
+/**
+ * Switches the master peer proxy into a state where it's waiting for a
+ * connection from the master.
+ */
+static void masterProxyExpectConnection(PeerProxy* masterProxy,
+        Header* header) {
+    // TODO: Restructure things so we don't need this check.
+    // Verify that this really is the master.
+    if (!masterProxy->master) {
+        LOGW("Non-master process %d tried to send us a connection.", 
+            masterProxy->credentials.pid);
+        // Kill off the evil peer.
+        peerProxyKill(masterProxy, false);
+        return;
+    }
+    
+    masterProxy->inputState = ACCEPTING_CONNECTION;
+    Peer* localPeer = masterProxy->peer;
+   
+    // Create a peer proxy so we have somewhere to stash the creds.
+    // See if we already have a proxy set up.
+    pid_t pid = header->credentials.pid;
+    peerLock(localPeer);
+    PeerProxy* peerProxy = peerProxyGetOrCreate(localPeer, pid, false);
+    if (peerProxy == NULL) {
+        LOGW("Peer proxy creation failed: %s", strerror(errno));
+    } else {
+        // Fill in full credentials.
+        peerProxy->credentials = header->credentials;
+    }
+    peerUnlock(localPeer);
+    
+    // Keep track of which peer proxy we're accepting a connection for.
+    masterProxy->connecting = peerProxy;
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd);
+
+/** Sets up fd callbacks. */
+static void peerProxySetFd(PeerProxy* peerProxy, SelectableFd* fd) {
+    peerProxy->fd = fd;
+    fd->data = peerProxy;
+    fd->onReadable = &peerProxyRead;
+    fd->beforeSelect = &peerProxyBeforeSelect;
+    
+    // Make the socket non-blocking.
+    setNonBlocking(fd->fd);
+}
+
+/**
+ * Accepts a connection sent by the master proxy.
+ */
+static void masterProxyAcceptConnection(PeerProxy* masterProxy) {
+    struct msghdr msg;
+    struct iovec iov[1];
+    ssize_t size;
+    char ignored;
+    int incomingFd;
+    
+    // TODO: Reuse code which writes the connection. Who the heck designed
+    // this API anyway?
+    union {
+        struct cmsghdr cm;
+        char control[CMSG_SPACE(sizeof(int))];
+    } control_un;
+    struct cmsghdr *cmptr;
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+
+    // We sent 1 byte of data so we can detect EOF.
+    iov[0].iov_base = &ignored;
+    iov[0].iov_len = 1;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    size = recvmsg(masterProxy->fd->fd, &msg, 0);
+    if (size < 0) {
+        if (errno == EINTR) {
+            // Log interruptions but otherwise ignore them.
+            LOGW("recvmsg() interrupted.");
+            return;
+        } else if (errno == EAGAIN) {
+            // Keep waiting for the connection.
+            return;
+        } else {
+            LOG_ALWAYS_FATAL("Error reading connection from master: %s",
+                    strerror(errno));
+        }
+    } else if (size == 0) {
+        // EOF.
+        LOG_ALWAYS_FATAL("Received EOF from master.");
+    }
+
+    // Extract fd from message.
+    if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL 
+            && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
+        if (cmptr->cmsg_level != SOL_SOCKET) {
+            LOG_ALWAYS_FATAL("Expected SOL_SOCKET.");
+        }
+        if (cmptr->cmsg_type != SCM_RIGHTS) {
+            LOG_ALWAYS_FATAL("Expected SCM_RIGHTS.");
+        }
+        incomingFd = *((int*) CMSG_DATA(cmptr));
+    } else {
+        LOG_ALWAYS_FATAL("Expected fd.");
+    }
+    
+    // The peer proxy this connection is for.
+    PeerProxy* peerProxy = masterProxy->connecting;
+    if (peerProxy == NULL) {
+        LOGW("Received connection for unknown peer.");
+        closeWithWarning(incomingFd);
+    } else {
+        Peer* peer = masterProxy->peer;
+        
+        SelectableFd* selectableFd = selectorAdd(peer->selector, incomingFd);
+        if (selectableFd == NULL) {
+            LOGW("Error adding fd to selector for %d.",
+                    peerProxy->credentials.pid);
+            closeWithWarning(incomingFd);
+            peerProxyKill(peerProxy, false);
+        }
+
+        peerProxySetFd(peerProxy, selectableFd);
+    }
+    
+    peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Frees an outgoing packet containing a connection.
+ */
+static void outgoingPacketFreeSocket(OutgoingPacket* packet) {
+    closeWithWarning(packet->socket);
+    outgoingPacketFree(packet);
+}
+
+/**
+ * Connects two known peers.
+ */
+static void masterConnectPeers(PeerProxy* peerA, PeerProxy* peerB) {
+    int sockets[2];
+    int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets);
+    if (result == -1) {
+        LOGW("socketpair() error: %s", strerror(errno));
+        // TODO: Send CONNECTION_FAILED packets to peers.
+        return;
+    }
+
+    OutgoingPacket* packetA = calloc(1, sizeof(OutgoingPacket));
+    OutgoingPacket* packetB = calloc(1, sizeof(OutgoingPacket));
+    if (packetA == NULL || packetB == NULL) {
+        free(packetA);
+        free(packetB);
+        LOGW("malloc() error. Failed to tell process %d that process %d is"
+                " dead.", peerA->credentials.pid, peerB->credentials.pid);
+        return;
+    }
+   
+    packetA->header.type = CONNECTION;
+    packetB->header.type = CONNECTION;
+    
+    packetA->header.credentials = peerB->credentials;
+    packetB->header.credentials = peerA->credentials;
+
+    packetA->socket = sockets[0];
+    packetB->socket = sockets[1];
+
+    packetA->free = &outgoingPacketFreeSocket;
+    packetB->free = &outgoingPacketFreeSocket;
+   
+    peerLock(peerA->peer);
+    peerProxyEnqueueOutgoingPacket(peerA, packetA);   
+    peerProxyEnqueueOutgoingPacket(peerB, packetB);   
+    peerUnlock(peerA->peer);
+}
+
+/**
+ * Informs a peer that the peer they're trying to connect to couldn't be
+ * found.
+ */
+static void masterReportConnectionError(PeerProxy* peerProxy,
+        Credentials credentials) {
+    OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+    if (packet == NULL) {
+        LOGW("malloc() error. Failed to tell process %d that process %d is"
+                " dead.", peerProxy->credentials.pid, credentials.pid);
+        return;
+    }
+   
+    packet->header.type = CONNECTION_ERROR;
+    packet->header.credentials = credentials;
+    packet->free = &outgoingPacketFree;
+    
+    peerProxyLockAndEnqueueOutgoingPacket(peerProxy, packet);   
+}
+
+/**
+ * Handles a request to be connected to another peer.
+ */
+static void masterHandleConnectionRequest(PeerProxy* peerProxy, 
+        Header* header) {
+    Peer* master = peerProxy->peer;
+    pid_t targetPid = header->credentials.pid;
+    if (!hashmapContainsKey(peerProxy->connections, &targetPid)) {
+        // We haven't connected these peers yet.
+        PeerProxy* targetPeer 
+            = (PeerProxy*) hashmapGet(master->peerProxies, &targetPid);
+        if (targetPeer == NULL) {
+            // Unknown process.
+            masterReportConnectionError(peerProxy, header->credentials);
+        } else {
+            masterConnectPeers(peerProxy, targetPeer);
+        }
+    }
+    
+    // This packet is complete. Get ready for the next one.
+    peerProxyExpectHeader(peerProxy);
+}
+
+/**
+ * The master told us this peer is dead.
+ */
+static void masterProxyHandleConnectionError(PeerProxy* masterProxy,
+        Header* header) {
+    Peer* peer = masterProxy->peer;
+    
+    // Look up the peer proxy.
+    pid_t pid = header->credentials.pid;
+    PeerProxy* peerProxy = NULL;
+    peerLock(peer);
+    peerProxy = hashmapGet(peer->peerProxies, &pid);
+    peerUnlock(peer);
+
+    if (peerProxy != NULL) {
+        LOGI("Couldn't connect to %d.", pid);
+        peerProxyKill(peerProxy, false);
+    } else {
+        LOGW("Peer proxy for %d not found. This shouldn't happen.", pid);
+    }
+    
+    peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Handles a packet header.
+ */
+static void peerProxyHandleHeader(PeerProxy* peerProxy, Header* header) {
+    switch (header->type) {
+        case CONNECTION_REQUEST:
+            masterHandleConnectionRequest(peerProxy, header);
+            break;
+        case CONNECTION:
+            masterProxyExpectConnection(peerProxy, header);
+            break;
+        case CONNECTION_ERROR:
+            masterProxyHandleConnectionError(peerProxy, header);
+            break;
+        case BYTES:    
+            peerProxyExpectBytes(peerProxy, header);
+            break;
+        default:
+            LOGW("Invalid packet type from %d: %d", peerProxy->credentials.pid, 
+                    header->type);
+            peerProxyKill(peerProxy, false);
+    }
+}
+
+/**
+ * Buffers input sent by peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is full.
+ */
+static bool peerProxyBufferInput(PeerProxy* peerProxy) {
+    Buffer* in = peerProxy->inputBuffer;
+    ssize_t size = bufferRead(in, peerProxy->fd->fd);
+    if (size < 0) {
+        peerProxyHandleError(peerProxy, "read");
+        return false;
+    } else if (size == 0) {
+        // EOF.
+    	LOGI("EOF");
+        peerProxyKill(peerProxy, false);
+        return false;
+    } else if (bufferReadComplete(in)) {
+        // We're done!
+        return true;
+    } else {
+        // Continue reading.
+        return false;
+    }
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd) {
+    LOGD("Reading...");
+    PeerProxy* peerProxy = (PeerProxy*) fd->data;
+    int state = peerProxy->inputState;
+    Buffer* in = peerProxy->inputBuffer;
+    switch (state) {
+        case READING_HEADER:
+            if (peerProxyBufferInput(peerProxy)) {
+                LOGD("Header read.");
+                // We've read the complete header.
+                Header* header = (Header*) in->data;
+                peerProxyHandleHeader(peerProxy, header);
+            }
+            break;
+        case READING_BYTES:
+            LOGD("Reading bytes...");
+            if (peerProxyBufferInput(peerProxy)) {
+                LOGD("Bytes read.");
+                // We have the complete packet. Notify bytes listener.
+                peerProxy->peer->onBytes(peerProxy->credentials,
+                    in->data, in->size);
+                        
+                // Get ready for the next packet.
+                peerProxyExpectHeader(peerProxy);
+            }
+            break;
+        case ACCEPTING_CONNECTION:
+            masterProxyAcceptConnection(peerProxy);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Unknown state: %d", state);
+    }
+}
+
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials) {
+    PeerProxy* peerProxy = calloc(1, sizeof(PeerProxy));
+    if (peerProxy == NULL) {
+        return NULL;
+    }
+   
+    peerProxy->inputBuffer = bufferCreate(sizeof(Header));
+    if (peerProxy->inputBuffer == NULL) {
+        free(peerProxy);
+        return NULL;
+    }
+
+    peerProxy->peer = peer;
+    peerProxy->credentials = credentials;
+
+    // Initial state == expecting a header.
+    peerProxyExpectHeader(peerProxy); 
+  
+    // Add this proxy to the map. Make sure the key points to the stable memory
+    // inside of the peer proxy itself.
+    pid_t* pid = &(peerProxy->credentials.pid);
+    hashmapPut(peer->peerProxies, pid, peerProxy);
+    return peerProxy;
+}
+
+/** Accepts a connection to the master peer. */
+static void masterAcceptConnection(SelectableFd* listenerFd) {
+    // Accept connection.
+    int socket = accept(listenerFd->fd, NULL, NULL);
+    if (socket == -1) {
+        LOGW("accept() error: %s", strerror(errno));
+        return;
+    }
+    
+    LOGD("Accepted connection as fd %d.", socket);
+    
+    // Get credentials.
+    Credentials credentials;
+    struct ucred ucredentials;
+    socklen_t credentialsSize = sizeof(struct ucred);
+    int result = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, 
+                &ucredentials, &credentialsSize);
+    // We might want to verify credentialsSize.
+    if (result == -1) {
+        LOGW("getsockopt() error: %s", strerror(errno));
+        closeWithWarning(socket);
+        return;
+    }
+
+    // Copy values into our own structure so we know we have the types right.
+    credentials.pid = ucredentials.pid;
+    credentials.uid = ucredentials.uid;
+    credentials.gid = ucredentials.gid;
+    
+    LOGI("Accepted connection from process %d.", credentials.pid);
+   
+    Peer* masterPeer = (Peer*) listenerFd->data;
+    
+    peerLock(masterPeer);
+    
+    // Make sure we don't already have a connection from that process.
+    PeerProxy* peerProxy 
+        = hashmapGet(masterPeer->peerProxies, &credentials.pid);
+    if (peerProxy != NULL) {
+        peerUnlock(masterPeer);
+        LOGW("Alread connected to process %d.", credentials.pid);
+        closeWithWarning(socket);
+        return;
+    }
+   
+    // Add connection to the selector.
+    SelectableFd* socketFd = selectorAdd(masterPeer->selector, socket);
+    if (socketFd == NULL) {
+        peerUnlock(masterPeer);
+        LOGW("malloc() failed.");
+        closeWithWarning(socket);
+        return;
+    }
+
+    // Create a peer proxy.
+    peerProxy = peerProxyCreate(masterPeer, credentials);
+    peerUnlock(masterPeer);
+    if (peerProxy == NULL) {
+        LOGW("malloc() failed.");
+        socketFd->remove = true;
+        closeWithWarning(socket);
+    }
+    peerProxy->connections = hashmapCreate(10, &pidHash, &pidEquals);
+    peerProxySetFd(peerProxy, socketFd);
+}
+
+/**
+ * Creates the local peer.
+ */
+static Peer* peerCreate() {
+    Peer* peer = calloc(1, sizeof(Peer));
+    if (peer == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+    peer->peerProxies = hashmapCreate(10, &pidHash, &pidEquals);
+    peer->selector = selectorCreate();
+    
+    pthread_mutexattr_t attributes;
+    if (pthread_mutexattr_init(&attributes) != 0) {
+        LOG_ALWAYS_FATAL("pthread_mutexattr_init() error.");
+    }
+    if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) {
+        LOG_ALWAYS_FATAL("pthread_mutexattr_settype() error.");
+    }
+    if (pthread_mutex_init(&peer->mutex, &attributes) != 0) {
+        LOG_ALWAYS_FATAL("pthread_mutex_init() error.");
+    }
+    
+    peer->pid = getpid();
+    return peer;
+}
+
+/** The local peer. */
+static Peer* localPeer;
+
+/** Frees a packet of bytes. */
+static void outgoingPacketFreeBytes(OutgoingPacket* packet) {
+    LOGD("Freeing outgoing packet.");
+    bufferFree(packet->bytes);
+    free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer. Returns 0 on success.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendBytes(pid_t pid, const char* bytes, size_t size) {
+	Peer* peer = localPeer;
+    assert(peer != NULL);
+
+    OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+    if (packet == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    Buffer* copy = bufferCreate(size); 
+    if (copy == NULL) {
+        free(packet);
+        errno = ENOMEM;
+        return -1;
+    }
+
+    // Copy data.
+    memcpy(copy->data, bytes, size);
+    copy->size = size;
+    
+    packet->bytes = copy;
+    packet->header.type = BYTES;
+    packet->header.size = size;
+    packet->free = outgoingPacketFreeBytes;
+    bufferPrepareForWrite(packet->bytes);
+    
+    peerLock(peer);
+    
+    PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+    if (peerProxy == NULL) {
+        // The peer is already dead or we couldn't alloc memory. Either way,
+        // errno is set.
+        peerUnlock(peer);
+        packet->free(packet); 
+        return -1;
+    } else {
+        peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+        peerUnlock(peer);
+        selectorWakeUp(peer->selector);
+        return 0; 
+    }
+}
+
+/** Keeps track of how to free shared bytes. */
+typedef struct {
+    void (*free)(void* context);
+    void* context;
+} SharedBytesFreer;
+
+/** Frees shared bytes. */
+static void outgoingPacketFreeSharedBytes(OutgoingPacket* packet) {
+    SharedBytesFreer* sharedBytesFreer 
+        = (SharedBytesFreer*) packet->context;
+    sharedBytesFreer->free(sharedBytesFreer->context);
+    free(sharedBytesFreer);
+    free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer without copying the bytes. Calls
+ * free() with context after the bytes have been sent.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendSharedBytes(pid_t pid, char* bytes, size_t size,
+        void (*free)(void* context), void* context) {
+    Peer* peer = localPeer;
+    assert(peer != NULL);
+
+    OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+    if (packet == NULL) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    Buffer* wrapper = bufferWrap(bytes, size, size);
+    if (wrapper == NULL) {
+        free(packet);
+        errno = ENOMEM;
+        return -1;
+    }
+
+    SharedBytesFreer* sharedBytesFreer = malloc(sizeof(SharedBytesFreer));
+    if (sharedBytesFreer == NULL) {
+        free(packet);
+        free(wrapper);
+        errno = ENOMEM;
+        return -1;
+    }
+    sharedBytesFreer->free = free;
+    sharedBytesFreer->context = context;
+    
+    packet->bytes = wrapper;
+    packet->context = sharedBytesFreer;
+    packet->header.type = BYTES;
+    packet->header.size = size;
+    packet->free = &outgoingPacketFreeSharedBytes;
+    bufferPrepareForWrite(packet->bytes);
+    
+    peerLock(peer);
+    
+    PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+    if (peerProxy == NULL) {
+        // The peer is already dead or we couldn't alloc memory. Either way,
+        // errno is set.
+        peerUnlock(peer);
+        packet->free(packet); 
+        return -1;
+    } else {
+        peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+        peerUnlock(peer);
+        selectorWakeUp(peer->selector);
+        return 0; 
+    }
+}
+
+/**
+ * Starts the master peer. The master peer differs from other peers in that
+ * it is responsible for connecting the other peers. You can only have one
+ * master peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void masterPeerInitialize(BytesListener* bytesListener, 
+        DeathListener* deathListener) {
+    // Create and bind socket.
+    int listenerSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (listenerSocket == -1) {
+        LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+    }
+    unlink(MASTER_PATH);
+    int result = bind(listenerSocket, (SocketAddress*) getMasterAddress(), 
+            sizeof(UnixAddress));
+    if (result == -1) {
+        LOG_ALWAYS_FATAL("bind() error: %s", strerror(errno));
+    }
+
+    LOGD("Listener socket: %d",  listenerSocket);   
+    
+    // Queue up to 16 connections.
+    result = listen(listenerSocket, 16);
+    if (result != 0) {
+        LOG_ALWAYS_FATAL("listen() error: %s", strerror(errno));
+    }
+
+    // Make socket non-blocking.
+    setNonBlocking(listenerSocket);
+
+    // Create the peer for this process. Fail if we already have one.
+    if (localPeer != NULL) {
+        LOG_ALWAYS_FATAL("Peer is already initialized.");
+    }
+    localPeer = peerCreate();
+    if (localPeer == NULL) {
+        LOG_ALWAYS_FATAL("malloc() failed.");
+    }
+    localPeer->master = true;
+    localPeer->onBytes = bytesListener;
+    localPeer->onDeath = deathListener;
+    
+    // Make listener socket selectable.
+    SelectableFd* listenerFd = selectorAdd(localPeer->selector, listenerSocket);
+    if (listenerFd == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+    listenerFd->data = localPeer;
+    listenerFd->onReadable = &masterAcceptConnection;
+}
+
+/**
+ * Starts a local peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void peerInitialize(BytesListener* bytesListener, 
+        DeathListener* deathListener) {
+    // Connect to master peer.
+    int masterSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+    if (masterSocket == -1) {
+        LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+    }
+    int result = connect(masterSocket, (SocketAddress*) getMasterAddress(),
+            sizeof(UnixAddress));
+    if (result != 0) {
+        LOG_ALWAYS_FATAL("connect() error: %s", strerror(errno));
+    }
+
+    // Create the peer for this process. Fail if we already have one.
+    if (localPeer != NULL) {
+        LOG_ALWAYS_FATAL("Peer is already initialized.");
+    }
+    localPeer = peerCreate();
+    if (localPeer == NULL) {
+        LOG_ALWAYS_FATAL("malloc() failed.");
+    }
+    localPeer->onBytes = bytesListener;
+    localPeer->onDeath = deathListener;
+    
+    // Make connection selectable.
+    SelectableFd* masterFd = selectorAdd(localPeer->selector, masterSocket);
+    if (masterFd == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+
+    // Create a peer proxy for the master peer.
+    PeerProxy* masterProxy = peerProxyCreate(localPeer, MASTER_CREDENTIALS);
+    if (masterProxy == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+    peerProxySetFd(masterProxy, masterFd);
+    masterProxy->master = true;
+    localPeer->masterProxy = masterProxy;
+}
+
+/** Starts the master peer I/O loop. Doesn't return. */
+void peerLoop() {
+    assert(localPeer != NULL);
+    
+    // Start selector.
+    selectorLoop(localPeer->selector);
+}
+
diff --git a/libcutils/mspace.c b/libcutils/mspace.c
new file mode 100644
index 0000000..8fd5de7
--- /dev/null
+++ b/libcutils/mspace.c
@@ -0,0 +1,246 @@
+/* Copyright 2006 The Android Open Source Project */
+
+/* A wrapper file for dlmalloc.c that compiles in the
+ * mspace_*() functions, which provide an interface for
+ * creating multiple heaps.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+#include <cutils/ashmem.h>
+
+/* It's a pain getting the mallinfo stuff to work
+ * with Linux, OSX, and klibc, so just turn it off
+ * for now.
+ * TODO: make mallinfo work
+ */
+#define NO_MALLINFO 1
+
+/* Allow setting the maximum heap footprint.
+ */
+#define USE_MAX_ALLOWED_FOOTPRINT 1
+
+/* Don't try to trim memory.
+ * TODO: support this.
+ */
+#define MORECORE_CANNOT_TRIM 1
+
+/* Use mmap()d anonymous memory to guarantee
+ * that an mspace is contiguous.
+ *
+ * create_mspace() won't work right if this is
+ * defined, so hide the definition of it and
+ * break any users at build time.
+ */
+#define USE_CONTIGUOUS_MSPACES 1
+#if USE_CONTIGUOUS_MSPACES
+/* This combination of settings forces sys_alloc()
+ * to always use MORECORE().  It won't expect the
+ * results to be contiguous, but we'll guarantee
+ * that they are.
+ */
+#define HAVE_MMAP 0
+#define HAVE_MORECORE 1
+#define MORECORE_CONTIGUOUS 0
+/* m is always the appropriate local when MORECORE() is called. */
+#define MORECORE(S) contiguous_mspace_morecore(m, S)
+#define create_mspace   HIDDEN_create_mspace_HIDDEN
+#define destroy_mspace   HIDDEN_destroy_mspace_HIDDEN
+typedef struct malloc_state *mstate0;
+static void *contiguous_mspace_morecore(mstate0 m, ssize_t nb);
+#endif
+
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#include "../../../bionic/libc/bionic/dlmalloc.c"
+
+#ifndef PAGESIZE
+#define PAGESIZE  mparams.page_size
+#endif
+
+#define ALIGN_UP(p, alignment) \
+    (((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1))
+
+/* A direct copy of dlmalloc_usable_size(),
+ * which isn't compiled in when ONLY_MSPACES is set.
+ * The mspace parameter isn't actually necessary,
+ * but we include it to be consistent with the
+ * rest of the mspace_*() functions.
+ */
+size_t mspace_usable_size(mspace _unused, const void* mem) {
+  if (mem != 0) {
+    const mchunkptr p = mem2chunk(mem);
+    if (cinuse(p))
+      return chunksize(p) - overhead_for(p);
+  }
+  return 0;
+}
+
+#if USE_CONTIGUOUS_MSPACES
+#include <sys/mman.h>
+#include <limits.h>
+
+#define CONTIG_STATE_MAGIC  0xf00dd00d
+struct mspace_contig_state {
+  unsigned int magic;
+  char *brk;
+  char *top;
+  mspace m;
+};
+
+static void *contiguous_mspace_morecore(mstate m, ssize_t nb) {
+  struct mspace_contig_state *cs;
+  char *oldbrk;
+  const unsigned int pagesize = PAGESIZE;
+
+  cs = (struct mspace_contig_state *)((uintptr_t)m & ~(pagesize-1));
+  assert(cs->magic == CONTIG_STATE_MAGIC);
+  assert(cs->m == m);
+assert(nb >= 0);  //xxx deal with the trim case
+
+  oldbrk = cs->brk;
+  if (nb > 0) {
+    /* Break to the first page boundary that satisfies the request.
+     */
+    char *newbrk = (char *)ALIGN_UP(oldbrk + nb, pagesize);
+    if (newbrk > cs->top)
+      return CMFAIL;
+
+    /* Update the protection on the underlying memory.
+     * Pages we've given to dlmalloc are read/write, and
+     * pages we haven't are not accessable (read or write
+     * will cause a seg fault).
+     */
+    if (mprotect(cs, newbrk - (char *)cs, PROT_READ | PROT_WRITE) < 0)
+      return CMFAIL;
+    if (newbrk != cs->top) {
+      if (mprotect(newbrk, cs->top - newbrk, PROT_NONE) < 0)
+        return CMFAIL;
+    }
+
+    cs->brk = newbrk;
+
+    /* Make sure that dlmalloc will merge this block with the
+     * initial block that was passed to create_mspace_with_base().
+     * We don't care about extern vs. non-extern, so just clear it.
+     */
+    m->seg.sflags &= ~EXTERN_BIT;
+  }
+
+  return oldbrk;
+}
+
+mspace create_contiguous_mspace_with_name(size_t starting_capacity,
+    size_t max_capacity, int locked, char const * name) {
+  int fd, ret;
+  struct mspace_contig_state *cs;
+  char buf[ASHMEM_NAME_LEN] = "mspace";
+  void *base;
+  unsigned int pagesize;
+  mstate m;
+
+  if (starting_capacity > max_capacity)
+    return (mspace)0;
+
+  init_mparams();
+  pagesize = PAGESIZE;
+
+  /* Create the anonymous memory that will back the mspace.
+   * This reserves all of the virtual address space we could
+   * ever need.  Physical pages will be mapped as the memory
+   * is touched.
+   *
+   * Align max_capacity to a whole page.
+   */
+  max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize);
+
+  if (name)
+    snprintf(buf, sizeof(buf), "mspace/%s", name);
+  fd = ashmem_create_region(buf, max_capacity);
+  if (fd < 0)
+    return (mspace)0;
+
+  base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+  close(fd);
+  if (base == MAP_FAILED)
+    return (mspace)0;
+
+  /* Make sure that base is at the beginning of a page.
+   */
+  assert(((uintptr_t)base & (pagesize-1)) == 0);
+
+  /* Reserve some space for the information that our MORECORE needs.
+   */
+  cs = base;
+
+  /* Create the mspace, pointing to the memory we just reserved.
+   */
+  m = create_mspace_with_base(base + sizeof(*cs), starting_capacity, locked);
+  if (m == (mspace)0)
+    goto error;
+
+  /* Make sure that m is in the same page as cs.
+   */
+  assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base);
+
+  /* Find out exactly how much of the memory the mspace
+   * is using.
+   */
+  cs->brk = m->seg.base + m->seg.size;
+  cs->top = (char *)base + max_capacity;
+  assert((char *)base <= cs->brk);
+  assert(cs->brk <= cs->top);
+
+  /* Prevent access to the memory we haven't handed out yet.
+   */
+  if (cs->brk != cs->top) {
+    /* mprotect() requires page-aligned arguments, but it's possible
+     * for cs->brk not to be page-aligned at this point.
+     */
+    char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize);
+    if (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)
+      goto error;
+  }
+
+  cs->m = m;
+  cs->magic = CONTIG_STATE_MAGIC;
+
+  return (mspace)m;
+
+error:
+  munmap(base, max_capacity);
+  return (mspace)0;
+}
+
+mspace create_contiguous_mspace(size_t starting_capacity,
+    size_t max_capacity, int locked) {
+  return create_contiguous_mspace_with_name(starting_capacity,
+      max_capacity, locked, NULL);
+}
+
+size_t destroy_contiguous_mspace(mspace msp) {
+  mstate ms = (mstate)msp;
+
+  if (ok_magic(ms)) {
+    struct mspace_contig_state *cs;
+    size_t length;
+    const unsigned int pagesize = PAGESIZE;
+
+    cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
+    assert(cs->magic == CONTIG_STATE_MAGIC);
+    assert(cs->m == ms);
+
+    length = cs->top - (char *)cs;
+    if (munmap((char *)cs, length) != 0)
+      return length;
+  }
+  else {
+    USAGE_ERROR_ACTION(ms, ms);
+  }
+  return 0;
+}
+#endif
diff --git a/libcutils/private.h b/libcutils/private.h
new file mode 100644
index 0000000..2837b70
--- /dev/null
+++ b/libcutils/private.h
@@ -0,0 +1,368 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char	privatehid[] = "@(#)private.h	8.2";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#define GRANDPARENTED	"Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME		1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT		0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R	0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY	3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR		1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK		1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H		1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H		1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H		1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H		0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME		"/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h"	/* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h"	/* for CHAR_BIT et al. */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT
+#include "libintl.h"
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>	/* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WIFEXITED
+#define WIFEXITED(status)	(((status) & 0xff) == 0)
+#endif /* !defined WIFEXITED */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status)	(((status) >> 8) & 0xff)
+#endif /* !defined WEXITSTATUS */
+
+#if HAVE_UNISTD_H
+#include "unistd.h"	/* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H */
+
+#if !HAVE_UNISTD_H
+#ifndef F_OK
+#define F_OK	0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK	4
+#endif /* !defined R_OK */
+#endif /* !HAVE_UNISTD_H */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+	(199901 <= __STDC_VERSION__ || \
+	2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long	int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long		int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** If your compiler lacks prototypes, "#define P(x) ()".
+*/
+
+#ifndef P
+#define P(x)	x
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS	0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE	1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX	MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX	1024		/* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int	unlink P((const char * filename));
+#define remove	unlink
+#endif /* !defined remove */
+
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef asctime_r
+extern char *	asctime_r();
+#endif
+
+/*
+** Private function declarations.
+*/
+
+char *		icalloc P((int nelem, int elsize));
+char *		icatalloc P((char * old, const char * new));
+char *		icpyalloc P((const char * string));
+char *		imalloc P((int n));
+void *		irealloc P((void * pointer, int size));
+void		icfree P((char * pointer));
+void		ifree P((char * pointer));
+const char *	scheck P((const char * string, const char * format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE	1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE	0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type)	(sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+	((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+	1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x)	((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r P((struct tm const *, char *));
+char *ctime_r P((time_t const *, char *));
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT		400	/* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR		31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT		((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+ 
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS	34	/* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/libcutils/process_name.c b/libcutils/process_name.c
new file mode 100644
index 0000000..17f52e2
--- /dev/null
+++ b/libcutils/process_name.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
+
+static const char* process_name = "unknown";
+static int running_in_emulator = -1;
+
+void set_process_name(const char* new_name) {
+    char  propBuf[PROPERTY_VALUE_MAX];
+
+    if (new_name == NULL) {
+        return;
+    }
+
+    // We never free the old name. Someone else could be using it.
+    char* copy = (char*) malloc(strlen(new_name) + 1);
+    strcpy(copy, new_name);
+    process_name = (const char*) copy;
+
+    // If we know we are not running in the emulator, then return.
+    if (running_in_emulator == 0) {
+        return;
+    }
+
+    // If the "running_in_emulator" variable has not been initialized,
+    // then do it now.
+    if (running_in_emulator == -1) {
+        property_get("ro.kernel.qemu", propBuf, "");
+        if (propBuf[0] == '1') {
+            running_in_emulator = 1;
+        } else {
+            running_in_emulator = 0;
+            return;
+        }
+    }
+
+    // If the emulator was started with the "-trace file" command line option
+    // then we want to record the process name in the trace even if we are
+    // not currently tracing instructions (so that we will know the process
+    // name when we do start tracing instructions).  We do not need to execute
+    // this code if we are just running in the emulator without the "-trace"
+    // command line option, but we don't know that here and this function
+    // isn't called frequently enough to bother optimizing that case.
+    int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
+    if (fd < 0)
+        return;
+    write(fd, process_name, strlen(process_name) + 1);
+    close(fd);
+}
+
+const char* get_process_name(void) {
+    return process_name;
+}
diff --git a/libcutils/properties.c b/libcutils/properties.c
new file mode 100644
index 0000000..547cc6d
--- /dev/null
+++ b/libcutils/properties.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "properties"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <cutils/properties.h>
+#include "loghack.h"
+
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static int send_prop_msg(prop_msg *msg)
+{
+    int s;
+    int r;
+    
+    s = socket_local_client(PROP_SERVICE_NAME, 
+                            ANDROID_SOCKET_NAMESPACE_RESERVED,
+                            SOCK_STREAM);
+    if(s < 0) return -1;
+    
+    while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
+        if((errno == EINTR) || (errno == EAGAIN)) continue;
+        break;
+    }
+
+    if(r == sizeof(prop_msg)) {
+        r = 0;
+    } else {
+        r = -1;
+    }
+
+    close(s);
+    return r;
+}
+
+int property_set(const char *key, const char *value)
+{
+    prop_msg msg;
+    unsigned resp;
+
+    if(key == 0) return -1;
+    if(value == 0) value = "";
+    
+    if(strlen(key) >= PROP_NAME_MAX) return -1;
+    if(strlen(value) >= PROP_VALUE_MAX) return -1;
+    
+    msg.cmd = PROP_MSG_SETPROP;
+    strcpy((char*) msg.name, key);
+    strcpy((char*) msg.value, value);
+
+    return send_prop_msg(&msg);
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+    int len;
+
+    len = __system_property_get(key, value);
+    if(len > 0) {
+        return len;
+    }
+    
+    if(default_value) {
+        len = strlen(default_value);
+        memcpy(value, default_value, len + 1);
+    }
+    return len;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), 
+                  void *cookie)
+{
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    const prop_info *pi;
+    unsigned n;
+    
+    for(n = 0; (pi = __system_property_find_nth(n)); n++) {
+        __system_property_read(pi, name, value);
+        propfn(name, value, cookie);
+    }
+    return 0;
+}
+
+#elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
+
+/*
+ * The Linux simulator provides a "system property server" that uses IPC
+ * to set/get/list properties.  The file descriptor is shared by all
+ * threads in the process, so we use a mutex to ensure that requests
+ * from multiple threads don't get interleaved.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+
+static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER;
+static int gPropFd = -1;
+
+/*
+ * Connect to the properties server.
+ *
+ * Returns the socket descriptor on success.
+ */
+static int connectToServer(const char* fileName)
+{
+    int sock = -1;
+    int cc;
+
+    struct sockaddr_un addr;
+    
+    sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock < 0) {
+        LOGW("UNIX domain socket create failed (errno=%d)\n", errno);
+        return -1;
+    }
+
+    /* connect to socket; fails if file doesn't exist */
+    strcpy(addr.sun_path, fileName);    // max 108 bytes
+    addr.sun_family = AF_UNIX;
+    cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+    if (cc < 0) {
+        // ENOENT means socket file doesn't exist
+        // ECONNREFUSED means socket exists but nobody is listening
+        //LOGW("AF_UNIX connect failed for '%s': %s\n",
+        //    fileName, strerror(errno));
+        close(sock);
+        return -1;
+    }
+
+    return sock;
+}
+
+/*
+ * Perform one-time initialization.
+ */
+static void init(void)
+{
+    assert(gPropFd == -1);
+
+    gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME);
+    if (gPropFd < 0) {
+        //LOGW("not connected to system property server\n");
+    } else {
+        //LOGV("Connected to system property server\n");
+    }
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+    char sendBuf[1+PROPERTY_KEY_MAX];
+    char recvBuf[1+PROPERTY_VALUE_MAX];
+    int len = -1;
+
+    //LOGV("PROPERTY GET [%s]\n", key);
+
+    pthread_once(&gInitOnce, init);
+    if (gPropFd < 0) {
+        /* this mimics the behavior of the device implementation */
+        if (default_value != NULL) {
+            strcpy(value, default_value);
+            len = strlen(value);
+        }
+        return len;
+    }
+
+    if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+
+    memset(sendBuf, 0xdd, sizeof(sendBuf));    // placate valgrind
+
+    sendBuf[0] = (char) kSystemPropertyGet;
+    strcpy(sendBuf+1, key);
+
+    pthread_mutex_lock(&gPropertyFdLock);
+    if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+        pthread_mutex_unlock(&gPropertyFdLock);
+        return -1;
+    }
+    if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+        pthread_mutex_unlock(&gPropertyFdLock);
+        return -1;
+    }
+    pthread_mutex_unlock(&gPropertyFdLock);
+
+    /* first byte is 0 if value not defined, 1 if found */
+    if (recvBuf[0] == 0) {
+        if (default_value != NULL) {
+            strcpy(value, default_value);
+            len = strlen(value);
+        } else {
+            /*
+             * If the value isn't defined, hand back an empty string and
+             * a zero length, rather than a failure.  This seems wrong,
+             * since you can't tell the difference between "undefined" and
+             * "defined but empty", but it's what the device does.
+             */
+            value[0] = '\0';
+            len = 0;
+        }
+    } else if (recvBuf[0] == 1) {
+        strcpy(value, recvBuf+1);
+        len = strlen(value);
+    } else {
+        LOGE("Got strange response to property_get request (%d)\n",
+            recvBuf[0]);
+        assert(0);
+        return -1;
+    }
+    //LOGV("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n",
+    //    recvBuf[0], default_value, len, key, value);
+
+    return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+    char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
+    char recvBuf[1];
+    int result = -1;
+
+    //LOGV("PROPERTY SET [%s]: [%s]\n", key, value);
+
+    pthread_once(&gInitOnce, init);
+    if (gPropFd < 0)
+        return -1;
+
+    if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+    if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+    memset(sendBuf, 0xdd, sizeof(sendBuf));    // placate valgrind
+
+    sendBuf[0] = (char) kSystemPropertySet;
+    strcpy(sendBuf+1, key);
+    strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
+
+    pthread_mutex_lock(&gPropertyFdLock);
+    if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+        pthread_mutex_unlock(&gPropertyFdLock);
+        return -1;
+    }
+    if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+        pthread_mutex_unlock(&gPropertyFdLock);
+        return -1;
+    }
+    pthread_mutex_unlock(&gPropertyFdLock);
+
+    if (recvBuf[0] != 1)
+        return -1;
+    return 0;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), 
+                  void *cookie)
+{
+    //LOGV("PROPERTY LIST\n");
+    pthread_once(&gInitOnce, init);
+    if (gPropFd < 0)
+        return -1;
+
+    return 0;
+}
+
+#else
+
+/* SUPER-cheesy place-holder implementation for Win32 */
+
+#include <cutils/threads.h>
+
+static mutex_t  env_lock = MUTEX_INITIALIZER;
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+    char ename[PROPERTY_KEY_MAX + 6];
+    char *p;
+    int len;
+    
+    len = strlen(key);
+    if(len >= PROPERTY_KEY_MAX) return -1;
+    memcpy(ename, "PROP_", 5);
+    memcpy(ename + 5, key, len + 1);
+    
+    mutex_lock(&env_lock);
+
+    p = getenv(ename);
+    if(p == 0) p = "";
+    len = strlen(p);
+    if(len >= PROPERTY_VALUE_MAX) {
+        len = PROPERTY_VALUE_MAX - 1;
+    }
+    
+    if((len == 0) && default_value) {
+        len = strlen(default_value);
+        memcpy(value, default_value, len + 1);
+    } else {
+        memcpy(value, p, len);
+        value[len] = 0;
+    }
+
+    mutex_unlock(&env_lock);
+    
+    return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+    char ename[PROPERTY_KEY_MAX + 6];
+    char *p;
+    int len;
+    int r;
+
+    if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+    
+    len = strlen(key);
+    if(len >= PROPERTY_KEY_MAX) return -1;
+    memcpy(ename, "PROP_", 5);
+    memcpy(ename + 5, key, len + 1);
+
+    mutex_lock(&env_lock);
+#ifdef HAVE_MS_C_RUNTIME
+    {
+        char  temp[256];
+        snprintf( temp, sizeof(temp), "%s=%s", ename, value);
+        putenv(temp);
+        r = 0;
+    }
+#else    
+    r = setenv(ename, value, 1);
+#endif    
+    mutex_unlock(&env_lock);
+    
+    return r;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), 
+                  void *cookie)
+{
+    return 0;
+}
+
+#endif
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
new file mode 100644
index 0000000..274423b
--- /dev/null
+++ b/libcutils/record_stream.c
@@ -0,0 +1,186 @@
+/* libs/cutils/record_stream.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/record_stream.h>
+#include <string.h>
+#include <stdint.h>
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>   /* for ntohl */
+#else
+#include <netinet/in.h>
+#endif
+
+#define HEADER_SIZE 4
+
+struct RecordStream {
+    int fd;
+    size_t maxRecordLen;
+
+    unsigned char *buffer;
+
+    unsigned char *unconsumed;
+    unsigned char *read_end;
+    unsigned char *buffer_end;
+};
+
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
+{
+    RecordStream *ret;
+
+    assert (maxRecordLen <= 0xffff);
+
+    ret = (RecordStream *)calloc(1, sizeof(RecordStream));
+
+    ret->fd = fd;
+    ret->maxRecordLen = maxRecordLen;
+    ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
+    
+    ret->unconsumed = ret->buffer;
+    ret->read_end = ret->buffer;
+    ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
+
+    return ret;
+}
+
+
+extern void record_stream_free(RecordStream *rs)
+{
+    free(rs->buffer);
+    free(rs);
+}
+
+
+/* returns NULL; if there isn't a full record in the buffer */
+static unsigned char * getEndOfRecord (unsigned char *p_begin,
+                                            unsigned char *p_end)
+{
+    size_t len;
+    unsigned char * p_ret;
+
+    if (p_end < p_begin + HEADER_SIZE) {
+        return NULL;
+    }
+
+    //First four bytes are length
+    len = ntohl(*((uint32_t *)p_begin));
+
+    p_ret = p_begin + HEADER_SIZE + len;
+
+    if (p_end < p_ret) {
+        return NULL;
+    }
+
+    return p_ret;
+}
+
+static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
+{
+    unsigned char *record_start, *record_end;
+
+    record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
+
+    if (record_end != NULL) {
+        /* one full line in the buffer */
+        record_start = p_rs->unconsumed + HEADER_SIZE;
+        p_rs->unconsumed = record_end;
+
+        *p_outRecordLen = record_end - record_start;
+
+        return record_start;
+    }
+
+    return NULL;
+}
+
+/**
+ * Reads the next record from stream fd
+ * Records are prefixed by a 16-bit big endian length value
+ * Records may not be larger than maxRecordLen
+ *
+ * Doesn't guard against EINTR
+ *
+ * p_outRecord and p_outRecordLen may not be NULL
+ *
+ * Return 0 on success, -1 on fail
+ * Returns 0 with *p_outRecord set to NULL on end of stream
+ * Returns -1 / errno = EAGAIN if it needs to read again
+ */
+int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, 
+                                    size_t *p_outRecordLen)
+{
+    void *ret;
+
+    ssize_t countRead;
+
+    /* is there one record already in the buffer? */
+    ret = getNextRecord (p_rs, p_outRecordLen);
+
+    if (ret != NULL) {
+        *p_outRecord = ret;
+        return 0;
+    }
+
+    // if the buffer is full and we don't have a full record
+    if (p_rs->unconsumed == p_rs->buffer 
+        && p_rs->read_end == p_rs->buffer_end
+    ) {
+        // this should never happen
+        //LOGE("max record length exceeded\n");
+        assert (0);
+        errno = EFBIG;
+        return -1;
+    }
+
+    if (p_rs->unconsumed != p_rs->buffer) {
+        // move remainder to the beginning of the buffer
+        size_t toMove;
+
+        toMove = p_rs->read_end - p_rs->unconsumed;
+        if (toMove) {
+            memmove(p_rs->buffer, p_rs->unconsumed, toMove);
+        }
+
+        p_rs->read_end = p_rs->buffer + toMove;
+        p_rs->unconsumed = p_rs->buffer;
+    }
+
+    countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
+
+    if (countRead <= 0) {
+        /* note: end-of-stream drops through here too */
+        *p_outRecord = NULL;
+        return countRead;
+    }
+
+    p_rs->read_end += countRead;
+
+    ret = getNextRecord (p_rs, p_outRecordLen);
+
+    if (ret == NULL) {
+        /* not enough of a buffer to for a whole command */
+        errno = EAGAIN;
+        return -1;
+    }
+
+    *p_outRecord = ret;        
+    return 0;
+}
diff --git a/libcutils/selector.c b/libcutils/selector.c
new file mode 100644
index 0000000..9436393
--- /dev/null
+++ b/libcutils/selector.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "selector"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/array.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+
+struct Selector {
+    Array* selectableFds;
+    bool looping;
+    fd_set readFds;
+    fd_set writeFds;
+    fd_set exceptFds;
+    int maxFd;
+    int wakeupPipe[2];
+    SelectableFd* wakeupFd;
+
+    bool inSelect;
+    pthread_mutex_t inSelectLock; 
+};
+
+/** Reads and ignores wake up data. */ 
+static void eatWakeupData(SelectableFd* wakeupFd) {
+    static char garbage[64];
+    if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) {
+        if (errno == EINTR) {
+            LOGI("read() interrupted.");    
+        } else {
+            LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+        }
+    }
+}
+
+static void setInSelect(Selector* selector, bool inSelect) {
+    pthread_mutex_lock(&selector->inSelectLock);
+    selector->inSelect = inSelect;
+    pthread_mutex_unlock(&selector->inSelectLock);
+}
+
+static bool isInSelect(Selector* selector) {
+    pthread_mutex_lock(&selector->inSelectLock);
+    bool inSelect = selector->inSelect;
+    pthread_mutex_unlock(&selector->inSelectLock);
+    return inSelect;
+}
+
+void selectorWakeUp(Selector* selector) {
+    if (!isInSelect(selector)) {
+        // We only need to write wake-up data if we're blocked in select().
+        return;
+    }
+    
+    static char garbage[1];
+    if (write(selector->wakeupPipe[1], garbage, sizeof(garbage)) < 0) {
+        if (errno == EINTR) {
+            LOGI("read() interrupted.");    
+        } else {
+            LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+        }
+    }
+}
+
+Selector* selectorCreate(void) {
+    Selector* selector = calloc(1, sizeof(Selector));
+    if (selector == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+    selector->selectableFds = arrayCreate();
+    
+    // Set up wake-up pipe.
+    if (pipe(selector->wakeupPipe) < 0) {
+        LOG_ALWAYS_FATAL("pipe() error: %s", strerror(errno));
+    }
+    
+    LOGD("Wakeup fd: %d", selector->wakeupPipe[0]);
+    
+    SelectableFd* wakeupFd = selectorAdd(selector, selector->wakeupPipe[0]);
+    if (wakeupFd == NULL) {
+        LOG_ALWAYS_FATAL("malloc() error.");
+    }
+    wakeupFd->onReadable = &eatWakeupData; 
+    
+    pthread_mutex_init(&selector->inSelectLock, NULL);
+
+    return selector;
+}
+
+SelectableFd* selectorAdd(Selector* selector, int fd) {
+    assert(selector != NULL);
+
+    SelectableFd* selectableFd = calloc(1, sizeof(SelectableFd));
+    if (selectableFd != NULL) {
+        selectableFd->selector = selector;
+        selectableFd->fd = fd;
+    
+        arrayAdd(selector->selectableFds, selectableFd);
+    }
+
+    return selectableFd;
+}
+
+/**
+ * Adds an fd to the given set if the callback is non-null. Returns true
+ * if the fd was added.
+ */
+static inline bool maybeAdd(SelectableFd* selectableFd,
+        void (*callback)(SelectableFd*), fd_set* fdSet) {
+    if (callback != NULL) {
+        FD_SET(selectableFd->fd, fdSet);
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Removes stale file descriptors and initializes file descriptor sets.
+ */
+static void prepareForSelect(Selector* selector) {
+    fd_set* exceptFds = &selector->exceptFds;
+    fd_set* readFds = &selector->readFds;
+    fd_set* writeFds = &selector->writeFds;
+    
+    FD_ZERO(exceptFds);
+    FD_ZERO(readFds);
+    FD_ZERO(writeFds);
+
+    Array* selectableFds = selector->selectableFds;
+    int i = 0;
+    selector->maxFd = 0;
+    int size = arraySize(selectableFds);
+    while (i < size) {
+        SelectableFd* selectableFd = arrayGet(selectableFds, i);
+        if (selectableFd->remove) {
+            // This descriptor should be removed.
+            arrayRemove(selectableFds, i);
+            size--;
+            if (selectableFd->onRemove != NULL) {
+                selectableFd->onRemove(selectableFd);
+            }
+            free(selectableFd);
+        } else {
+            if (selectableFd->beforeSelect != NULL) {
+                selectableFd->beforeSelect(selectableFd);
+            }
+            
+            bool inSet = false;
+            if (maybeAdd(selectableFd, selectableFd->onExcept, exceptFds)) {
+            	LOGD("Selecting fd %d for writing...", selectableFd->fd);
+                inSet = true;
+            }
+            if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) {
+            	LOGD("Selecting fd %d for reading...", selectableFd->fd);
+                inSet = true;
+            }
+            if (maybeAdd(selectableFd, selectableFd->onWritable, writeFds)) {
+                inSet = true;
+            }
+
+            if (inSet) {
+                // If the fd is in a set, check it against max.
+                int fd = selectableFd->fd;
+                if (fd > selector->maxFd) {
+                    selector->maxFd = fd;
+                }
+            }
+            
+            // Move to next descriptor.
+            i++;
+        }
+    }
+}
+
+/**
+ * Invokes a callback if the callback is non-null and the fd is in the given
+ * set.
+ */
+static inline void maybeInvoke(SelectableFd* selectableFd,
+        void (*callback)(SelectableFd*), fd_set* fdSet) {
+	if (callback != NULL && !selectableFd->remove && 
+            FD_ISSET(selectableFd->fd, fdSet)) {
+		LOGD("Selected fd %d.", selectableFd->fd);
+        callback(selectableFd);
+    }
+}
+
+/**
+ * Notifies user if file descriptors are readable or writable, or if
+ * out-of-band data is present.
+ */
+static void fireEvents(Selector* selector) {
+    Array* selectableFds = selector->selectableFds;
+    int size = arraySize(selectableFds);
+    int i;
+    for (i = 0; i < size; i++) {
+        SelectableFd* selectableFd = arrayGet(selectableFds, i);
+        maybeInvoke(selectableFd, selectableFd->onExcept,
+                &selector->exceptFds);
+        maybeInvoke(selectableFd, selectableFd->onReadable,
+                &selector->readFds);
+        maybeInvoke(selectableFd, selectableFd->onWritable,
+                &selector->writeFds);
+    }
+}
+
+void selectorLoop(Selector* selector) {
+    // Make sure we're not already looping.
+    if (selector->looping) {
+        LOG_ALWAYS_FATAL("Already looping.");
+    }
+    selector->looping = true;
+    
+    while (true) {
+        setInSelect(selector, true);
+        
+        prepareForSelect(selector);
+
+        LOGD("Entering select().");
+        
+        // Select file descriptors.
+        int result = select(selector->maxFd + 1, &selector->readFds, 
+                &selector->writeFds, &selector->exceptFds, NULL);
+        
+        LOGD("Exiting select().");
+        
+        setInSelect(selector, false);
+        
+        if (result == -1) {
+            // Abort on everything except EINTR.
+            if (errno == EINTR) {
+                LOGI("select() interrupted.");    
+            } else {
+                LOG_ALWAYS_FATAL("select() error: %s", 
+                        strerror(errno));
+            }
+        } else if (result > 0) {
+            fireEvents(selector);
+        }
+    }
+}
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
new file mode 100644
index 0000000..7d5dab4
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server.c
@@ -0,0 +1,70 @@
+/* libs/cutils/socket_inaddr_any_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+    struct sockaddr_in addr;
+    size_t alen;
+    int s, n;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    s = socket(AF_INET, type, 0);
+    if(s < 0) return -1;
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+
+        if (ret < 0) {
+            close(s);
+            return -1; 
+        }
+    }
+
+    return s;
+}
diff --git a/libcutils/socket_local.h b/libcutils/socket_local.h
new file mode 100644
index 0000000..45b9856
--- /dev/null
+++ b/libcutils/socket_local.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SOCKET_LOCAL_H
+#define __SOCKET_LOCAL_H
+
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/" 
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+
+/*
+ * Set up a given sockaddr_un, to have it refer to the given
+ * name in the given namespace. The namespace must be one
+ * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,
+ * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or
+ * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,
+ * the pointed at sockaddr_un is filled in and the pointed at
+ * socklen_t is set to indicate the final length. This function
+ * will fail if the namespace is invalid (not one of the indicated
+ * constants) or if the name is too long.
+ * 
+ * @return 0 on success or -1 on failure
+ */ 
+int socket_make_sockaddr_un(const char *name, int namespaceId, 
+        struct sockaddr_un *p_addr, socklen_t *alen);
+
+#endif
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
new file mode 100644
index 0000000..036ce2e
--- /dev/null
+++ b/libcutils/socket_local_client.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId, 
+        struct sockaddr_un *p_addr, socklen_t *alen)
+{
+    memset (p_addr, 0, sizeof (*p_addr));
+    size_t namelen;
+
+    switch (namespaceId) {
+        case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+            namelen  = strlen(name);
+
+            // Test with length +1 for the *initial* '\0'.
+            if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+                goto error;
+            }
+
+            /*
+             * Note: The path in this case is *not* supposed to be
+             * '\0'-terminated. ("man 7 unix" for the gory details.)
+             */
+            
+            p_addr->sun_path[0] = 0;
+            memcpy(p_addr->sun_path + 1, name, namelen);
+#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+            /* this OS doesn't have the Linux abstract namespace */
+
+            namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+            strcat(p_addr->sun_path, name);
+#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+        break;
+
+        case ANDROID_SOCKET_NAMESPACE_RESERVED:
+            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+            strcat(p_addr->sun_path, name);
+        break;
+
+        case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+            namelen = strlen(name);
+            /* unix_path_max appears to be missing on linux */
+            if (namelen > sizeof(*p_addr) 
+                    - offsetof(struct sockaddr_un, sun_path) - 1) {
+                goto error;
+            }
+
+            strcpy(p_addr->sun_path, name);
+        break;
+        default:
+            // invalid namespace id
+            return -1;
+    }
+
+    p_addr->sun_family = AF_LOCAL;
+    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+    return 0;
+error:
+    return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ * 
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId, 
+        int type)
+{
+    struct sockaddr_un addr;
+    socklen_t alen;
+    size_t namelen;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        goto error;
+    }
+
+    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+        goto error;
+    }
+
+    return fd;
+
+error:
+    return -1;
+}
+
+/** 
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+    int s;
+
+    s = socket(AF_LOCAL, type, 0);
+    if(s < 0) return -1;
+
+    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
new file mode 100644
index 0000000..4971b1b
--- /dev/null
+++ b/libcutils/socket_local_server.c
@@ -0,0 +1,124 @@
+/* libs/cutils/socket_local_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+    errno = ENOSYS;
+    return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+    struct sockaddr_un addr;
+    socklen_t alen;
+    int n;
+    int err;
+
+    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+    if (err < 0) {
+        return -1;
+    }
+
+    /* basically: if this is a filesystem path, unlink first */
+#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+    if (1) {
+#else
+    if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+        || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+        /*ignore ENOENT*/
+        unlink(addr.sun_path);
+    }
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+        return -1;
+    }
+
+    return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem 
+ *  namespace
+ *
+ *  Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+    int err;
+    int s;
+    
+    s = socket(AF_LOCAL, type, 0);
+    if (s < 0) return -1;
+
+    err = socket_local_server_bind(s, name, namespace);
+
+    if (err < 0) {
+        close(s);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+
+        if (ret < 0) {
+            close(s);
+            return -1;
+        }
+    }
+
+    return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
new file mode 100644
index 0000000..cb82c5e
--- /dev/null
+++ b/libcutils/socket_loopback_client.c
@@ -0,0 +1,59 @@
+/* libs/cutils/socket_loopback_client.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* Connect to port on the loopback IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM. 
+ * return is a file descriptor or -1 on error
+ */
+int socket_loopback_client(int port, int type)
+{
+    struct sockaddr_in addr;
+    socklen_t alen;
+    int s;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket(AF_INET, type, 0);
+    if(s < 0) return -1;
+
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+
+}
+
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
new file mode 100644
index 0000000..3208488
--- /dev/null
+++ b/libcutils/socket_loopback_server.c
@@ -0,0 +1,71 @@
+/* libs/cutils/socket_loopback_server.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#define LISTEN_BACKLOG 4
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+    struct sockaddr_in addr;
+    size_t alen;
+    int s, n;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    s = socket(AF_INET, type, 0);
+    if(s < 0) return -1;
+
+    n = 1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    if (type == SOCK_STREAM) {
+        int ret;
+
+        ret = listen(s, LISTEN_BACKLOG);
+
+        if (ret < 0) {
+            close(s);
+            return -1; 
+        }
+    }
+
+    return s;
+}
+
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
new file mode 100644
index 0000000..a64006c
--- /dev/null
+++ b/libcutils/socket_network_client.c
@@ -0,0 +1,65 @@
+/* libs/cutils/socket_network_client.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+
+/* Connect to port on the IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM. 
+ * return is a file descriptor or -1 on error
+ */
+int socket_network_client(const char *host, int port, int type)
+{
+    struct hostent *hp;
+    struct sockaddr_in addr;
+    socklen_t alen;
+    int s;
+
+    hp = gethostbyname(host);
+    if(hp == 0) return -1;
+    
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = hp->h_addrtype;
+    addr.sin_port = htons(port);
+    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+    s = socket(hp->h_addrtype, type, 0);
+    if(s < 0) return -1;
+
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(s);
+        return -1;
+    }
+
+    return s;
+
+}
+
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
new file mode 100644
index 0000000..fadaabe
--- /dev/null
+++ b/libcutils/strdup16to8.c
@@ -0,0 +1,104 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+   size_t utf8Len = 0;
+
+   while (len--) {
+       unsigned int uic = *utf16Str++;
+
+       if (uic > 0x07ff)
+           utf8Len += 3;
+       else if (uic > 0x7f || uic == 0)
+           utf8Len += 2;
+       else
+           utf8Len++;
+   }
+   return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ * 
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+    char* utf8cur = utf8Str;
+
+    while (len--) {
+        unsigned int uic = *utf16Str++;
+
+        if (uic > 0x07ff) {
+            *utf8cur++ = (uic >> 12) | 0xe0;
+            *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else if (uic > 0x7f || uic == 0) {
+            *utf8cur++ = (uic >> 6) | 0xc0;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else {
+            *utf8cur++ = uic;
+
+            if (uic == 0) {
+                break;
+            }           
+        }       
+    }
+
+   *utf8cur = '\0';
+
+   return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ * Make sure you allocate "dest" with the result of strblen16to8(),
+ * not just "strlen16()".
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+    char *ret;
+
+    if (s == NULL) {
+        return NULL;
+    }
+
+    ret = malloc(strnlen16to8(s, n) + 1);
+
+    strncpy16to8 (ret, s, n);
+    
+    return ret;    
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
new file mode 100644
index 0000000..8654b04
--- /dev/null
+++ b/libcutils/strdup8to16.c
@@ -0,0 +1,209 @@
+/* libs/cutils/strdup8to16.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte)  \
+            (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd    
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+    char16_t *ret;
+    size_t len;
+
+    if (s == NULL) return NULL;
+
+    len = strlen8to16(s);
+
+    // no plus-one here. UTF-16 strings are not null terminated
+    ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+    return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+    size_t len = 0;
+    int ic;
+    int expected = 0;
+
+    while ((ic = *utf8Str++) != '\0') {
+        /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+        /* bytes that start 10 are extention bytes and are not counted */
+         
+        if ((ic & 0xc0) == 0x80) {
+            /* count the 0x80 extention bytes. if we have more than
+             * expected, then start counting them because strcpy8to16
+             * will insert UTF16_REPLACEMENT_CHAR's
+             */
+            expected--;
+            if (expected < 0) {
+                len++;
+            }
+        } else {
+            len++;
+            expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+            /* this will result in a surrogate pair */
+            if (expected == 3) {
+                len++;
+            }
+        }
+    }
+
+    return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+    uint32_t ret;
+    int seq_len;
+    int i;
+
+    /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+    static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+    /* Bytes that start with bits "10" are not leading characters. */
+    if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+        (*pUtf8Ptr)++;
+        return UTF16_REPLACEMENT_CHAR;
+    }
+
+    /* note we tolerate invalid leader 11111xxx here */    
+    seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+    ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+    if (**pUtf8Ptr == '\0') return ret;
+
+    (*pUtf8Ptr)++;
+    for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+        if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+        if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+        UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+    }
+
+    return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, 
+                                       size_t *out_len)
+{   
+    char16_t *dest = utf16Str;
+
+    while (*utf8Str != '\0') {
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+                                       int length, size_t *out_len)
+{
+    /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+    
+    char16_t *dest = utf16Str;
+
+    const char *end = utf8Str + length; /* This line */
+    while (utf8Str < end) {             /* and this line changed. */
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
diff --git a/libcutils/threads.c b/libcutils/threads.c
new file mode 100644
index 0000000..42cc928
--- /dev/null
+++ b/libcutils/threads.c
@@ -0,0 +1,84 @@
+/* libs/cutils/threads.c
+**
+** Copyright (C) 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+#include <cutils/threads.h>
+
+#ifdef HAVE_PTHREADS
+void*  thread_store_get( thread_store_t*  store )
+{
+    const pthread_key_t  k = store->tls;
+
+    if (!store->has_tls)
+        return NULL;
+
+    return pthread_getspecific( store->tls );
+}
+    
+extern void   thread_store_set( thread_store_t*          store, 
+                                void*                    value,
+                                thread_store_destruct_t  destroy)
+{
+    pthread_mutex_lock( &store->lock );
+    if (!store->has_tls) {
+        if (pthread_key_create( &store->tls, destroy) != 0) {
+            pthread_mutex_unlock(&store->lock);
+            return;
+        }
+        store->has_tls = 1;
+    }
+    pthread_mutex_unlock( &store->lock );
+
+    pthread_setspecific( store->tls, value );
+}
+
+#endif
+
+#ifdef HAVE_WIN32_THREADS
+void*  thread_store_get( thread_store_t*  store )
+{
+    if (!store->has_tls)
+        return NULL;
+    
+    return (void*) TlsGetValue( store->tls );
+}
+
+void   thread_store_set( thread_store_t*          store,
+                         void*                    value,
+                         thread_store_destruct_t  destroy )
+{
+    /* XXX: can't use destructor on thread exit */
+    if (!store->lock_init) {
+        store->lock_init = -1;
+        InitializeCriticalSection( &store->lock );
+        store->lock_init = -2;
+    } else while (store->lock_init != -2) {
+        Sleep(10); /* 10ms */
+    }
+        
+    EnterCriticalSection( &store->lock );
+    if (!store->has_tls) {
+        store->tls = TlsAlloc();
+        if (store->tls == TLS_OUT_OF_INDEXES) {
+            LeaveCriticalSection( &store->lock );
+            return;
+        }
+        store->has_tls = 1;
+    }
+    LeaveCriticalSection( &store->lock );
+    
+    TlsSetValue( store->tls, value );
+}
+#endif
diff --git a/libcutils/tzfile.h b/libcutils/tzfile.h
new file mode 100644
index 0000000..8c70375
--- /dev/null
+++ b/libcutils/tzfile.h
@@ -0,0 +1,180 @@
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char	tzfilehid[] = "@(#)tzfile.h	8.1";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/share/zoneinfo" /* "/android/usr/share/zoneinfo" */ /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT	"localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES	"posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define	TZ_MAGIC	"TZif"
+
+struct tzhead {
+	char	tzh_magic[4];		/* TZ_MAGIC */
+	char	tzh_version[1];		/* '\0' or '2' as of 2005 */
+	char	tzh_reserved[15];	/* reserved--must be zero */
+	char	tzh_ttisgmtcnt[4];	/* coded number of trans. time flags */
+	char	tzh_ttisstdcnt[4];	/* coded number of trans. time flags */
+	char	tzh_leapcnt[4];		/* coded number of leap seconds */
+	char	tzh_timecnt[4];		/* coded number of transition times */
+	char	tzh_typecnt[4];		/* coded number of local time types */
+	char	tzh_charcnt[4];		/* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+**	tzh_timecnt (char [4])s		coded transition times a la time(2)
+**	tzh_timecnt (unsigned char)s	types of local time starting at above
+**	tzh_typecnt repetitions of
+**		one (char [4])		coded UTC offset in seconds
+**		one (unsigned char)	used to set tm_isdst
+**		one (unsigned char)	that's an abbreviation list index
+**	tzh_charcnt (char)s		'\0'-terminated zone abbreviations
+**	tzh_leapcnt repetitions of
+**		one (char [4])		coded leap second transition times
+**		one (char [4])		total correction after above
+**	tzh_ttisstdcnt (char)s		indexed by type; if TRUE, transition
+**					time is standard time, if FALSE,
+**					transition time is wall clock time
+**					if absent, transition times are
+**					assumed to be wall clock time
+**	tzh_ttisgmtcnt (char)s		indexed by type; if TRUE, transition
+**					time is UTC, if FALSE,
+**					transition time is local time
+**					if absent, transition times are
+**					assumed to be local time
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES	1200
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES	256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew.
+*/
+#define TZ_MAX_TYPES	20	/* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS	50	/* Maximum number of abbreviation characters */
+				/* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS	50	/* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN	60
+#define MINSPERHOUR	60
+#define HOURSPERDAY	24
+#define DAYSPERWEEK	7
+#define DAYSPERNYEAR	365
+#define DAYSPERLYEAR	366
+#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY	((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR	12
+
+#define TM_SUNDAY	0
+#define TM_MONDAY	1
+#define TM_TUESDAY	2
+#define TM_WEDNESDAY	3
+#define TM_THURSDAY	4
+#define TM_FRIDAY	5
+#define TM_SATURDAY	6
+
+#define TM_JANUARY	0
+#define TM_FEBRUARY	1
+#define TM_MARCH	2
+#define TM_APRIL	3
+#define TM_MAY		4
+#define TM_JUNE		5
+#define TM_JULY		6
+#define TM_AUGUST	7
+#define TM_SEPTEMBER	8
+#define TM_OCTOBER	9
+#define TM_NOVEMBER	10
+#define TM_DECEMBER	11
+
+#define TM_YEAR_BASE	1900
+
+#define EPOCH_YEAR	1970
+#define EPOCH_WDAY	TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+**	isleap(y) == isleap(y % 400)
+** and so
+**	isleap(a + b) == isleap((a + b) % 400)
+** or
+**	isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b)	isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined TZFILE_H */
diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c
new file mode 100644
index 0000000..29c5015
--- /dev/null
+++ b/libcutils/tzstrftime.c
@@ -0,0 +1,834 @@
+#ifndef lint
+#ifndef NOID
+static char	elsieid[] = "@(#)strftime.c	8.1";
+/*
+** Based on the UCB version with the ID appearing below.
+** This is ANSIish only when "multibyte character == plain character".
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#include <time.h>
+#include <tzfile.h>
+#include <limits.h>
+#include <cutils/tztime.h>
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley. The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef LIBC_SCCS
+#ifndef lint
+static const char	sccsid[] = "@(#)strftime.c	5.4 (Berkeley) 3/14/89";
+#endif /* !defined lint */
+#endif /* !defined LIBC_SCCS */
+
+#include <ctype.h>
+
+#define P(x) x
+
+static char *	_add P((const char *, char *, const char *, int));
+static char *	_conv P((int, const char *, char *, const char *));
+static char *	_fmt P((const char *, const struct tm *, char *, const char *,
+			int *, const struct strftime_locale *Locale));
+static char *	_yconv P((int, int, int, int, char *, const char *, int));
+static char *	getformat P((int, char *, char *, char *, char *));
+
+extern char *	tzname[];
+
+
+
+
+
+/* from private.h */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type)  (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+ * ** 302 / 1000 is log10(2.0) rounded up.
+ * ** Subtract one for the sign bit if the type is signed;
+ * ** add one for integer division truncation;
+ * ** add one more for a minus sign if the type is signed.
+ * */
+#define INT_STRLEN_MAXIMUM(type) \
+    ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+    1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/* end of part from private.h */
+
+
+
+
+#ifndef YEAR_2000_NAME
+#define YEAR_2000_NAME	"CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+#endif /* !defined YEAR_2000_NAME */
+
+#define IN_NONE	0
+#define IN_SOME	1
+#define IN_THIS	2
+#define IN_ALL	3
+
+#define FORCE_LOWER_CASE 0x100
+
+size_t
+strftime_tz(s, maxsize, format, t, Locale)
+char * const		s;
+const size_t		maxsize;
+const char * const	format;
+const struct tm * const	t;
+const struct strftime_locale *Locale;
+{
+	char *	p;
+	int	warn;
+
+	warn = IN_NONE;
+	p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale);
+#if 0
+	if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
+		(void) fprintf(stderr, "\n");
+		if (format == NULL)
+			(void) fprintf(stderr, "NULL strftime format ");
+		else	(void) fprintf(stderr, "strftime format \"%s\" ",
+				format);
+		(void) fprintf(stderr, "yields only two digits of years in ");
+		if (warn == IN_SOME)
+			(void) fprintf(stderr, "some locales");
+		else if (warn == IN_THIS)
+			(void) fprintf(stderr, "the current locale");
+		else	(void) fprintf(stderr, "all locales");
+		(void) fprintf(stderr, "\n");
+	}
+#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
+	if (p == s + maxsize)
+		return 0;
+	*p = '\0';
+	return p - s;
+}
+
+static char *getformat(int modifier, char *normal, char *underscore,
+                       char *dash, char *zero) {
+    switch (modifier) {
+    case '_':
+        return underscore;
+
+    case '-':
+        return dash;
+
+    case '0':
+        return zero;
+    }
+
+    return normal;
+}
+
+static char *
+_fmt(format, t, pt, ptlim, warnp, Locale)
+const char *		format;
+const struct tm * const	t;
+char *			pt;
+const char * const	ptlim;
+int *			warnp;
+const struct strftime_locale *Locale;
+{
+	for ( ; *format; ++format) {
+		if (*format == '%') {
+            int modifier = 0;
+label:
+			switch (*++format) {
+			case '\0':
+				--format;
+				break;
+			case 'A':
+				pt = _add((t->tm_wday < 0 ||
+					t->tm_wday >= DAYSPERWEEK) ?
+					"?" : Locale->weekday[t->tm_wday],
+					pt, ptlim, modifier);
+				continue;
+			case 'a':
+				pt = _add((t->tm_wday < 0 ||
+					t->tm_wday >= DAYSPERWEEK) ?
+					"?" : Locale->wday[t->tm_wday],
+					pt, ptlim, modifier);
+				continue;
+			case 'B':
+				pt = _add((t->tm_mon < 0 ||
+					t->tm_mon >= MONSPERYEAR) ?
+					"?" : Locale->month[t->tm_mon],
+					pt, ptlim, modifier);
+				continue;
+			case 'b':
+			case 'h':
+				pt = _add((t->tm_mon < 0 ||
+					t->tm_mon >= MONSPERYEAR) ?
+					"?" : Locale->mon[t->tm_mon],
+					pt, ptlim, modifier);
+				continue;
+			case 'C':
+				/*
+				** %C used to do a...
+				**	_fmt("%a %b %e %X %Y", t);
+				** ...whereas now POSIX 1003.2 calls for
+				** something completely different.
+				** (ado, 1993-05-24)
+				*/
+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
+					pt, ptlim, modifier);
+				continue;
+			case 'c':
+				{
+				int warn2 = IN_SOME;
+
+				pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale);
+				if (warn2 == IN_ALL)
+					warn2 = IN_THIS;
+				if (warn2 > *warnp)
+					*warnp = warn2;
+				}
+				continue;
+			case 'D':
+                                pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'd':
+                                pt = _conv(t->tm_mday,
+                                           getformat(modifier, "%02d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 'E':
+			case 'O':
+				/*
+				** C99 locale modifiers.
+				** The sequences
+				**	%Ec %EC %Ex %EX %Ey %EY
+				**	%Od %oe %OH %OI %Om %OM
+				**	%OS %Ou %OU %OV %Ow %OW %Oy
+				** are supposed to provide alternate
+				** representations.
+				*/
+				goto label;
+            case '_':
+            case '-':
+            case '0':
+            case '^':
+            case '#':
+                modifier = *format;
+                goto label;
+			case 'e':
+				pt = _conv(t->tm_mday,
+                                           getformat(modifier, "%2d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 'F':
+				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'H':
+				pt = _conv(t->tm_hour,
+                                           getformat(modifier, "%02d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 'I':
+				pt = _conv((t->tm_hour % 12) ?
+					(t->tm_hour % 12) : 12,
+					getformat(modifier, "%02d",
+                                                  "%2d", "%d", "%02d"),
+                                        pt, ptlim);
+				continue;
+			case 'j':
+				pt = _conv(t->tm_yday + 1,
+                           getformat(modifier, "%03d", "%3d", "%d", "%03d"),
+                           pt, ptlim);
+				continue;
+			case 'k':
+				/*
+				** This used to be...
+				**	_conv(t->tm_hour % 12 ?
+				**		t->tm_hour % 12 : 12, 2, ' ');
+				** ...and has been changed to the below to
+				** match SunOS 4.1.1 and Arnold Robbins'
+				** strftime version 3.0. That is, "%k" and
+				** "%l" have been swapped.
+				** (ado, 1993-05-24)
+				*/
+				pt = _conv(t->tm_hour,
+                                           getformat(modifier, "%2d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+#ifdef KITCHEN_SINK
+			case 'K':
+				/*
+				** After all this time, still unclaimed!
+				*/
+				pt = _add("kitchen sink", pt, ptlim, modifier);
+				continue;
+#endif /* defined KITCHEN_SINK */
+			case 'l':
+				/*
+				** This used to be...
+				**	_conv(t->tm_hour, 2, ' ');
+				** ...and has been changed to the below to
+				** match SunOS 4.1.1 and Arnold Robbin's
+				** strftime version 3.0. That is, "%k" and
+				** "%l" have been swapped.
+				** (ado, 1993-05-24)
+				*/
+				pt = _conv((t->tm_hour % 12) ?
+					(t->tm_hour % 12) : 12,
+					getformat(modifier, "%2d",
+                                                  "%2d", "%d", "%02d"),
+                                        pt, ptlim);
+				continue;
+			case 'M':
+				pt = _conv(t->tm_min,
+                                           getformat(modifier, "%02d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 'm':
+				pt = _conv(t->tm_mon + 1,
+                                           getformat(modifier, "%02d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 'n':
+				pt = _add("\n", pt, ptlim, modifier);
+				continue;
+			case 'p':
+				pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+					Locale->pm :
+					Locale->am,
+					pt, ptlim, modifier);
+				continue;
+			case 'P':
+				pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+					Locale->pm :
+					Locale->am,
+					pt, ptlim, FORCE_LOWER_CASE);
+				continue;
+			case 'R':
+				pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'r':
+				pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'S':
+				pt = _conv(t->tm_sec,
+                                           getformat(modifier, "%02d",
+                                                     "%2d", "%d", "%02d"),
+                                           pt, ptlim);
+				continue;
+			case 's':
+				{
+					struct tm	tm;
+					char		buf[INT_STRLEN_MAXIMUM(
+								time_t) + 1];
+					time_t		mkt;
+
+					tm = *t;
+					mkt = mktime(&tm);
+					if (TYPE_SIGNED(time_t))
+						(void) sprintf(buf, "%ld",
+							(long) mkt);
+					else	(void) sprintf(buf, "%lu",
+							(unsigned long) mkt);
+					pt = _add(buf, pt, ptlim, modifier);
+				}
+				continue;
+			case 'T':
+				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 't':
+				pt = _add("\t", pt, ptlim, modifier);
+				continue;
+			case 'U':
+				pt = _conv((t->tm_yday + DAYSPERWEEK -
+					t->tm_wday) / DAYSPERWEEK,
+					getformat(modifier, "%02d",
+                                                  "%2d", "%d", "%02d"),
+                                        pt, ptlim);
+				continue;
+			case 'u':
+				/*
+				** From Arnold Robbins' strftime version 3.0:
+				** "ISO 8601: Weekday as a decimal number
+				** [1 (Monday) - 7]"
+				** (ado, 1993-05-24)
+				*/
+				pt = _conv((t->tm_wday == 0) ?
+					DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim);
+				continue;
+			case 'V':	/* ISO 8601 week number */
+			case 'G':	/* ISO 8601 year (four digits) */
+			case 'g':	/* ISO 8601 year (two digits) */
+/*
+** From Arnold Robbins' strftime version 3.0: "the week number of the
+** year (the first Monday as the first day of week 1) as a decimal number
+** (01-53)."
+** (ado, 1993-05-24)
+**
+** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
+** "Week 01 of a year is per definition the first week which has the
+** Thursday in this year, which is equivalent to the week which contains
+** the fourth day of January. In other words, the first week of a new year
+** is the week which has the majority of its days in the new year. Week 01
+** might also contain days from the previous year and the week before week
+** 01 of a year is the last week (52 or 53) of the previous year even if
+** it contains days from the new year. A week starts with Monday (day 1)
+** and ends with Sunday (day 7). For example, the first week of the year
+** 1997 lasts from 1996-12-30 to 1997-01-05..."
+** (ado, 1996-01-02)
+*/
+				{
+					int	year;
+					int	base;
+					int	yday;
+					int	wday;
+					int	w;
+
+					year = t->tm_year;
+					base = TM_YEAR_BASE;
+					yday = t->tm_yday;
+					wday = t->tm_wday;
+					for ( ; ; ) {
+						int	len;
+						int	bot;
+						int	top;
+
+						len = isleap_sum(year, base) ?
+							DAYSPERLYEAR :
+							DAYSPERNYEAR;
+						/*
+						** What yday (-3 ... 3) does
+						** the ISO year begin on?
+						*/
+						bot = ((yday + 11 - wday) %
+							DAYSPERWEEK) - 3;
+						/*
+						** What yday does the NEXT
+						** ISO year begin on?
+						*/
+						top = bot -
+							(len % DAYSPERWEEK);
+						if (top < -3)
+							top += DAYSPERWEEK;
+						top += len;
+						if (yday >= top) {
+							++base;
+							w = 1;
+							break;
+						}
+						if (yday >= bot) {
+							w = 1 + ((yday - bot) /
+								DAYSPERWEEK);
+							break;
+						}
+						--base;
+						yday += isleap_sum(year, base) ?
+							DAYSPERLYEAR :
+							DAYSPERNYEAR;
+					}
+#ifdef XPG4_1994_04_09
+					if ((w == 52 &&
+						t->tm_mon == TM_JANUARY) ||
+						(w == 1 &&
+						t->tm_mon == TM_DECEMBER))
+							w = 53;
+#endif /* defined XPG4_1994_04_09 */
+					if (*format == 'V')
+						pt = _conv(w,
+                                                           getformat(modifier,
+                                                                     "%02d",
+                                                                     "%2d",
+                                                                     "%d",
+                                                                     "%02d"),
+							   pt, ptlim);
+					else if (*format == 'g') {
+						*warnp = IN_ALL;
+						pt = _yconv(year, base, 0, 1,
+							pt, ptlim, modifier);
+					} else	pt = _yconv(year, base, 1, 1,
+							pt, ptlim, modifier);
+				}
+				continue;
+			case 'v':
+				/*
+				** From Arnold Robbins' strftime version 3.0:
+				** "date as dd-bbb-YYYY"
+				** (ado, 1993-05-24)
+				*/
+				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'W':
+				pt = _conv((t->tm_yday + DAYSPERWEEK -
+					(t->tm_wday ?
+					(t->tm_wday - 1) :
+					(DAYSPERWEEK - 1))) / DAYSPERWEEK,
+					getformat(modifier, "%02d",
+                                                  "%2d", "%d", "%02d"),
+                                        pt, ptlim);
+				continue;
+			case 'w':
+				pt = _conv(t->tm_wday, "%d", pt, ptlim);
+				continue;
+			case 'X':
+				pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale);
+				continue;
+			case 'x':
+				{
+				int	warn2 = IN_SOME;
+
+				pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale);
+				if (warn2 == IN_ALL)
+					warn2 = IN_THIS;
+				if (warn2 > *warnp)
+					*warnp = warn2;
+				}
+				continue;
+			case 'y':
+				*warnp = IN_ALL;
+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+					pt, ptlim, modifier);
+				continue;
+			case 'Y':
+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
+					pt, ptlim, modifier);
+				continue;
+			case 'Z':
+#ifdef TM_ZONE
+				if (t->TM_ZONE != NULL)
+					pt = _add(t->TM_ZONE, pt, ptlim,
+                                                  modifier);
+				else
+#endif /* defined TM_ZONE */
+				if (t->tm_isdst >= 0)
+					pt = _add(tzname[t->tm_isdst != 0],
+						pt, ptlim, modifier);
+				/*
+				** C99 says that %Z must be replaced by the
+				** empty string if the time zone is not
+				** determinable.
+				*/
+				continue;
+			case 'z':
+				{
+				int		diff;
+				char const *	sign;
+
+				if (t->tm_isdst < 0)
+					continue;
+#ifdef TM_GMTOFF
+				diff = t->TM_GMTOFF;
+#else /* !defined TM_GMTOFF */
+				/*
+				** C99 says that the UTC offset must
+				** be computed by looking only at
+				** tm_isdst. This requirement is
+				** incorrect, since it means the code
+				** must rely on magic (in this case
+				** altzone and timezone), and the
+				** magic might not have the correct
+				** offset. Doing things correctly is
+				** tricky and requires disobeying C99;
+				** see GNU C strftime for details.
+				** For now, punt and conform to the
+				** standard, even though it's incorrect.
+				**
+				** C99 says that %z must be replaced by the
+				** empty string if the time zone is not
+				** determinable, so output nothing if the
+				** appropriate variables are not available.
+				*/
+				if (t->tm_isdst == 0)
+#ifdef USG_COMPAT
+					diff = -timezone;
+#else /* !defined USG_COMPAT */
+					continue;
+#endif /* !defined USG_COMPAT */
+				else
+#ifdef ALTZONE
+					diff = -altzone;
+#else /* !defined ALTZONE */
+					continue;
+#endif /* !defined ALTZONE */
+#endif /* !defined TM_GMTOFF */
+				if (diff < 0) {
+					sign = "-";
+					diff = -diff;
+				} else	sign = "+";
+				pt = _add(sign, pt, ptlim, modifier);
+				diff /= SECSPERMIN;
+				diff = (diff / MINSPERHOUR) * 100 +
+					(diff % MINSPERHOUR);
+				pt = _conv(diff,
+                                           getformat(modifier, "%04d",
+                                                     "%4d", "%d", "%04d"),
+                                           pt, ptlim);
+				}
+				continue;
+			case '+':
+				pt = _fmt(Locale->date_fmt, t, pt, ptlim,
+					warnp, Locale);
+				continue;
+			case '%':
+			/*
+			** X311J/88-090 (4.12.3.5): if conversion char is
+			** undefined, behavior is undefined. Print out the
+			** character itself as printf(3) also does.
+			*/
+			default:
+				break;
+			}
+		}
+		if (pt == ptlim)
+			break;
+		*pt++ = *format;
+	}
+	return pt;
+}
+
+static char *
+_conv(n, format, pt, ptlim)
+const int		n;
+const char * const	format;
+char * const		pt;
+const char * const	ptlim;
+{
+	char	buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+	(void) sprintf(buf, format, n);
+	return _add(buf, pt, ptlim, 0);
+}
+
+static char *
+_add(str, pt, ptlim, modifier)
+const char *		str;
+char *			pt;
+const char * const	ptlim;
+int                     modifier;
+{
+        int c;
+
+        switch (modifier) {
+        case FORCE_LOWER_CASE:
+                while (pt < ptlim && (*pt = tolower(*str++)) != '\0') {
+                        ++pt;
+                }
+                break;
+
+        case '^':
+                while (pt < ptlim && (*pt = toupper(*str++)) != '\0') {
+                        ++pt;
+                }
+                break;
+
+        case '#':
+                while (pt < ptlim && (c = *str++) != '\0') {
+                        if (isupper(c)) {
+                                c = tolower(c);
+                        } else if (islower(c)) {
+                                c = toupper(c);
+                        }
+                        *pt = c;
+                        ++pt;
+                }
+
+                break;
+
+        default:
+                while (pt < ptlim && (*pt = *str++) != '\0') {
+                        ++pt;
+                }
+        }
+
+	return pt;
+}
+
+/*
+** POSIX and the C Standard are unclear or inconsistent about
+** what %C and %y do if the year is negative or exceeds 9999.
+** Use the convention that %C concatenated with %y yields the
+** same output as %Y, and that %Y contains at least 4 bytes,
+** with more only if necessary.
+*/
+
+static char *
+_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier)
+const int		a;
+const int		b;
+const int		convert_top;
+const int		convert_yy;
+char *			pt;
+const char * const	ptlim;
+int                     modifier;
+{
+	register int	lead;
+	register int	trail;
+
+#define DIVISOR	100
+	trail = a % DIVISOR + b % DIVISOR;
+	lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
+	trail %= DIVISOR;
+	if (trail < 0 && lead > 0) {
+		trail += DIVISOR;
+		--lead;
+	} else if (lead < 0 && trail > 0) {
+		trail -= DIVISOR;
+		++lead;
+	}
+	if (convert_top) {
+		if (lead == 0 && trail < 0)
+			pt = _add("-0", pt, ptlim, modifier);
+		else	pt = _conv(lead, getformat(modifier, "%02d",
+                                                   "%2d", "%d", "%02d"),
+                                   pt, ptlim);
+	}
+	if (convert_yy)
+		pt = _conv(((trail < 0) ? -trail : trail),
+                           getformat(modifier, "%02d", "%2d", "%d", "%02d"),
+                           pt, ptlim);
+	return pt;
+}
+
+#ifdef LOCALE_HOME
+static struct lc_time_T *
+_loc P((void))
+{
+	static const char	locale_home[] = LOCALE_HOME;
+	static const char	lc_time[] = "LC_TIME";
+	static char *		locale_buf;
+
+	int			fd;
+	int			oldsun;	/* "...ain't got nothin' to do..." */
+	char *			lbuf;
+	char *			name;
+	char *			p;
+	const char **		ap;
+	const char *		plim;
+	char			filename[FILENAME_MAX];
+	struct stat		st;
+	size_t			namesize;
+	size_t			bufsize;
+
+	/*
+	** Use localebuf.mon[0] to signal whether locale is already set up.
+	*/
+	if (localebuf.mon[0])
+		return &localebuf;
+	name = setlocale(LC_TIME, (char *) NULL);
+	if (name == NULL || *name == '\0')
+		goto no_locale;
+	/*
+	** If the locale name is the same as our cache, use the cache.
+	*/
+	lbuf = locale_buf;
+	if (lbuf != NULL && strcmp(name, lbuf) == 0) {
+		p = lbuf;
+		for (ap = (const char **) &localebuf;
+			ap < (const char **) (&localebuf + 1);
+				++ap)
+					*ap = p += strlen(p) + 1;
+		return &localebuf;
+	}
+	/*
+	** Slurp the locale file into the cache.
+	*/
+	namesize = strlen(name) + 1;
+	if (sizeof filename <
+		((sizeof locale_home) + namesize + (sizeof lc_time)))
+			goto no_locale;
+	oldsun = 0;
+	(void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time);
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		/*
+		** Old Sun systems have a different naming and data convention.
+		*/
+		oldsun = 1;
+		(void) sprintf(filename, "%s/%s/%s", locale_home,
+			lc_time, name);
+		fd = open(filename, O_RDONLY);
+		if (fd < 0)
+			goto no_locale;
+	}
+	if (fstat(fd, &st) != 0)
+		goto bad_locale;
+	if (st.st_size <= 0)
+		goto bad_locale;
+	bufsize = namesize + st.st_size;
+	locale_buf = NULL;
+	lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
+	if (lbuf == NULL)
+		goto bad_locale;
+	(void) strcpy(lbuf, name);
+	p = lbuf + namesize;
+	plim = p + st.st_size;
+	if (read(fd, p, (size_t) st.st_size) != st.st_size)
+		goto bad_lbuf;
+	if (close(fd) != 0)
+		goto bad_lbuf;
+	/*
+	** Parse the locale file into localebuf.
+	*/
+	if (plim[-1] != '\n')
+		goto bad_lbuf;
+	for (ap = (const char **) &localebuf;
+		ap < (const char **) (&localebuf + 1);
+			++ap) {
+				if (p == plim)
+					goto bad_lbuf;
+				*ap = p;
+				while (*p != '\n')
+					++p;
+				*p++ = '\0';
+	}
+	if (oldsun) {
+		/*
+		** SunOS 4 used an obsolescent format; see localdtconv(3).
+		** c_fmt had the ``short format for dates and times together''
+		** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
+		** date_fmt had the ``long format for dates''
+		** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
+		** Discard the latter in favor of the former.
+		*/
+		localebuf.date_fmt = localebuf.c_fmt;
+	}
+	/*
+	** Record the successful parse in the cache.
+	*/
+	locale_buf = lbuf;
+
+	return &localebuf;
+
+bad_lbuf:
+	free(lbuf);
+bad_locale:
+	(void) close(fd);
+no_locale:
+	localebuf = C_time_locale;
+	locale_buf = NULL;
+	return &localebuf;
+}
+#endif /* defined LOCALE_HOME */
diff --git a/libcutils/tztime.c b/libcutils/tztime.c
new file mode 100644
index 0000000..93bbb29
--- /dev/null
+++ b/libcutils/tztime.c
@@ -0,0 +1,1915 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+#include <stdio.h>
+
+#ifndef lint
+#ifndef NOID
+static char	elsieid[] = "@(#)localtime.c	8.3";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+#include "tzfile.h"
+#include "fcntl.h"
+#include "float.h"	/* for FLT_MAX and DBL_MAX */
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN	16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+	"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR	'_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
+
+#define INDEXFILE "/system/usr/share/zoneinfo/zoneinfo.idx"
+#define DATAFILE "/system/usr/share/zoneinfo/zoneinfo.dat"
+#define NAMELEN 40
+#define INTLEN 4
+#define READLEN (NAMELEN + 3 * INTLEN)
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE	(O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE	O_RDONLY
+#endif /* !defined O_BINARY */
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+**	1.	They might reference tzname[0] before calling tzset (explicitly
+**		or implicitly).
+**	2.	They might reference tzname[1] before calling tzset (explicitly
+**		or implicitly).
+**	3.	They might reference tzname[1] after setting to a time zone
+**		in which Daylight Saving Time is never observed.
+**	4.	They might reference tzname[0] after setting to a time zone
+**		in which Standard Time is never observed.
+**	5.	They might reference tm.TM_ZONE after calling offtime.
+** What's best to do in the above cases is open to debate;
+** for now, we just set things up so that in any of the five cases
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
+** string "tzname[0] used before set", and similarly for the other cases.
+** And another: initialize tzname[0] to "ERA", with an explanation in the
+** manual page of what this "time zone abbreviation" means (doing this so
+** that tzname[0] has the "normal" length of three characters).
+*/
+#define WILDABBR	"   "
+#endif /* !defined WILDABBR */
+
+static char		wildabbr[] = WILDABBR;
+
+static const char	gmt[] = "GMT";
+
+/*
+** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+** We default to US rules as of 1999-08-17.
+** POSIX 1003.1 section 8.1.1 says that the default DST rules are
+** implementation dependent; for historical reasons, US rules are a
+** common default.
+*/
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#endif /* !defined TZDEFDST */
+
+struct ttinfo {				/* time type information */
+	long		tt_gmtoff;	/* UTC offset in seconds */
+	int		tt_isdst;	/* used to set tm_isdst */
+	int		tt_abbrind;	/* abbreviation list index */
+	int		tt_ttisstd;	/* TRUE if transition is std time */
+	int		tt_ttisgmt;	/* TRUE if transition is UTC */
+};
+
+struct lsinfo {				/* leap second information */
+	time_t		ls_trans;	/* transition time */
+	long		ls_corr;	/* correction to apply */
+};
+
+#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX	TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX	255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+	int		leapcnt;
+	int		timecnt;
+	int		typecnt;
+	int		charcnt;
+	int		goback;
+	int		goahead;
+	time_t		ats[TZ_MAX_TIMES];
+	unsigned char	types[TZ_MAX_TIMES];
+	struct ttinfo	ttis[TZ_MAX_TYPES];
+	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+				(2 * (MY_TZNAME_MAX + 1)))];
+	struct lsinfo	lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+	int		r_type;		/* type of rule--see below */
+	int		r_day;		/* day number of rule */
+	int		r_week;		/* week number of rule */
+	int		r_mon;		/* month number of rule */
+	long		r_time;		/* transition time of rule */
+};
+
+#define JULIAN_DAY		0	/* Jn - Julian day */
+#define DAY_OF_YEAR		1	/* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long		detzcode P((const char * codep));
+static time_t		detzcode64 P((const char * codep));
+static int		differ_by_repeat P((time_t t1, time_t t0));
+static const char *	getzname P((const char * strp));
+static const char *	getqzname P((const char * strp, const int delim));
+static const char *	getnum P((const char * strp, int * nump, int min,
+				int max));
+static const char *	getsecs P((const char * strp, long * secsp));
+static const char *	getoffset P((const char * strp, long * offsetp));
+static const char *	getrule P((const char * strp, struct rule * rulep));
+static void		gmtload P((struct state * sp));
+static struct tm *	gmtsub P((const time_t * timep, long offset,
+				struct tm * tmp));
+static struct tm *	localsub P((const time_t * timep, long offset,
+				struct tm * tmp, struct state *sp));
+static int		increment_overflow P((int * number, int delta));
+static int		leaps_thru_end_of P((int y));
+static int		long_increment_overflow P((long * number, int delta));
+static int		long_normalize_overflow P((long * tensptr,
+				int * unitsptr, int base));
+static int		normalize_overflow P((int * tensptr, int * unitsptr,
+				int base));
+static void		settzname P((void));
+static time_t		time1 P((struct tm * tmp,
+				struct tm * (*funcp) P((const time_t *,
+				long, struct tm *, const struct state* sp)),
+				long offset, const struct state *	sp));
+static time_t		time2 P((struct tm *tmp,
+				struct tm * (*funcp) P((const time_t *,
+				long, struct tm*, const struct state* sp)),
+				long offset, int * okayp, const struct state *	sp));
+static time_t		time2sub P((struct tm *tmp,
+				struct tm * (*funcp) P((const time_t*, long, struct tm*,const struct state *sp)),
+				long offset, int * okayp, int do_norm_secs,
+                const struct state *sp));
+static struct tm *	timesub P((const time_t * timep, long offset,
+				const struct state * sp, struct tm * tmp));
+static int		tmcomp P((const struct tm * atmp,
+				const struct tm * btmp));
+static time_t		transtime P((time_t janfirst, int year,
+				const struct rule * rulep, long offset));
+static int		tzload P((const char * name, struct state * sp,
+				int doextend));
+static int		tzload_uncached P((const char * name, struct state * sp,
+				int doextend));
+static int		tzparse P((const char * name, struct state * sp,
+				int lastditch));
+
+#ifdef ALL_STATE
+static struct state *	gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state	gmtmem;
+#define gmtptr		(&gmtmem)
+#endif /* State Farm */
+
+#define CACHE_COUNT 4
+static char * g_cacheNames[CACHE_COUNT] = {0,0};
+static struct state	g_cacheStates[CACHE_COUNT];
+static int g_lastCache = 0;
+static struct state g_utc;
+unsigned char g_utcSet = 0;
+
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+static char		lcl_TZname[TZ_STRLEN_MAX + 1];
+static int		lcl_is_set;
+static int		gmt_is_set;
+
+char *			tzname[2] = {
+	wildabbr,
+	wildabbr
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+**	Except for the strftime function, these functions [asctime,
+**	ctime, gmtime, localtime] return values in one of two static
+**	objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert for noting this.
+*/
+
+static struct tm	tm;
+
+#ifdef USG_COMPAT
+time_t			timezone = 0;
+int			daylight = 0;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t			altzone = 0;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(codep)
+const char * const	codep;
+{
+	register long	result;
+	register int	i;
+
+	result = (codep[0] & 0x80) ? ~0L : 0;
+	for (i = 0; i < 4; ++i)
+		result = (result << 8) | (codep[i] & 0xff);
+	return result;
+}
+
+static time_t
+detzcode64(codep)
+const char * const	codep;
+{
+	register time_t	result;
+	register int	i;
+
+	result = (codep[0] & 0x80) ?  (~(int_fast64_t) 0) : 0;
+	for (i = 0; i < 8; ++i)
+		result = result * 256 + (codep[i] & 0xff);
+	return result;
+}
+
+static int
+differ_by_repeat(t1, t0)
+const time_t	t1;
+const time_t	t0;
+{
+	if (TYPE_INTEGRAL(time_t) &&
+		TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+			return 0;
+	return t1 - t0 == SECSPERREPEAT;
+}
+
+static int toint(unsigned char *s) {
+    return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
+}
+
+static int
+tzload(const char *name, struct state * const sp, const int doextend)
+{
+    if (name) {
+        int i, err;
+        if (0 == strcmp(name, "UTC")) {
+            if (!g_utcSet) {
+                tzload_uncached(name, &g_utc, 1);
+                g_utcSet = 1;
+            }
+            //printf("tzload: utc\n");
+            *sp = g_utc;
+            return 0;
+        }
+        for (i=0; i<CACHE_COUNT; i++) {
+            if (g_cacheNames[i] && 0 == strcmp(name, g_cacheNames[i])) {
+                *sp = g_cacheStates[i];
+                //printf("tzload: hit: %s\n", name);
+                return 0;
+            }
+        }
+        //printf("tzload: miss: %s\n", name);
+        g_lastCache++;
+        if (g_lastCache >= CACHE_COUNT) {
+            g_lastCache = 0;
+        }
+        i = g_lastCache;
+        if (g_cacheNames[i]) {
+            free(g_cacheNames[i]);
+        }
+        err = tzload_uncached(name, &(g_cacheStates[i]), 1);
+        if (err == 0) {
+            g_cacheNames[i] = strdup(name);
+            *sp = g_cacheStates[i];
+            return 0;
+        } else {
+            g_cacheNames[i] = NULL;
+            return err;
+        }
+    }
+    return tzload_uncached(name, sp, doextend);
+}
+
+static int
+tzload_uncached(name, sp, doextend)
+register const char *		name;
+register struct state * const	sp;
+register const int		doextend;
+{
+	register const char *		p;
+	register int			i;
+	register int			fid;
+	register int			stored;
+	register int			nread;
+	union {
+		struct tzhead	tzhead;
+		char		buf[2 * sizeof(struct tzhead) +
+					2 * sizeof *sp +
+					4 * TZ_MAX_TIMES];
+	} u;
+    int                     toread = sizeof u.buf;
+
+	if (name == NULL && (name = TZDEFAULT) == NULL)
+		return -1;
+	{
+		register int	doaccess;
+		/*
+		** Section 4.9.1 of the C standard says that
+		** "FILENAME_MAX expands to an integral constant expression
+		** that is the size needed for an array of char large enough
+		** to hold the longest file name string that the implementation
+		** guarantees can be opened."
+		*/
+		char		fullname[FILENAME_MAX + 1];
+		const char	*origname = name;
+
+		if (name[0] == ':')
+			++name;
+		doaccess = name[0] == '/';
+		if (!doaccess) {
+			if ((p = TZDIR) == NULL)
+				return -1;
+			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+				return -1;
+			(void) strcpy(fullname, p);
+			(void) strcat(fullname, "/");
+			(void) strcat(fullname, name);
+			/*
+			** Set doaccess if '.' (as in "../") shows up in name.
+			*/
+			if (strchr(name, '.') != NULL)
+				doaccess = TRUE;
+			name = fullname;
+		}
+		if (doaccess && access(name, R_OK) != 0)
+			return -1;
+		if ((fid = open(name, OPEN_MODE)) == -1) {
+            char buf[READLEN];
+            char name[NAMELEN + 1];
+            int fidix = open(INDEXFILE, OPEN_MODE);
+            int off = -1;
+
+            if (fidix < 0) {
+                return -1;
+            }
+
+            while (read(fidix, buf, sizeof(buf)) == sizeof(buf)) {
+                memcpy(name, buf, NAMELEN);
+                name[NAMELEN] = '\0';
+
+                if (strcmp(name, origname) == 0) {
+                    off = toint((unsigned char *) buf + NAMELEN);
+                    toread = toint((unsigned char *) buf + NAMELEN + INTLEN);
+                    break;
+                }
+            }
+
+            close(fidix);
+
+            if (off < 0)
+                return -1;
+
+            fid = open(DATAFILE, OPEN_MODE);
+
+            if (fid < 0) {
+                return -1;
+            }
+
+            if (lseek(fid, off, SEEK_SET) < 0) {
+                return -1;
+            }
+        }
+	}
+	nread = read(fid, u.buf, toread);
+	if (close(fid) < 0 || nread <= 0)
+		return -1;
+	for (stored = 4; stored <= 8; stored *= 2) {
+		int		ttisstdcnt;
+		int		ttisgmtcnt;
+
+		ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+		ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+		sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+		sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+		sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+		sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+		p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+		if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+			sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+			sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+			sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+			(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+			(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+				return -1;
+		if (nread - (p - u.buf) <
+			sp->timecnt * stored +		/* ats */
+			sp->timecnt +			/* types */
+			sp->typecnt * 6 +		/* ttinfos */
+			sp->charcnt +			/* chars */
+			sp->leapcnt * (stored + 4) +	/* lsinfos */
+			ttisstdcnt +			/* ttisstds */
+			ttisgmtcnt)			/* ttisgmts */
+				return -1;
+		for (i = 0; i < sp->timecnt; ++i) {
+			sp->ats[i] = (stored == 4) ?
+				detzcode(p) : detzcode64(p);
+			p += stored;
+		}
+		for (i = 0; i < sp->timecnt; ++i) {
+			sp->types[i] = (unsigned char) *p++;
+			if (sp->types[i] >= sp->typecnt)
+				return -1;
+		}
+		for (i = 0; i < sp->typecnt; ++i) {
+			register struct ttinfo *	ttisp;
+
+			ttisp = &sp->ttis[i];
+			ttisp->tt_gmtoff = detzcode(p);
+			p += 4;
+			ttisp->tt_isdst = (unsigned char) *p++;
+			if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+				return -1;
+			ttisp->tt_abbrind = (unsigned char) *p++;
+			if (ttisp->tt_abbrind < 0 ||
+				ttisp->tt_abbrind > sp->charcnt)
+					return -1;
+		}
+		for (i = 0; i < sp->charcnt; ++i)
+			sp->chars[i] = *p++;
+		sp->chars[i] = '\0';	/* ensure '\0' at end */
+		for (i = 0; i < sp->leapcnt; ++i) {
+			register struct lsinfo *	lsisp;
+
+			lsisp = &sp->lsis[i];
+			lsisp->ls_trans = (stored == 4) ?
+				detzcode(p) : detzcode64(p);
+			p += stored;
+			lsisp->ls_corr = detzcode(p);
+			p += 4;
+		}
+		for (i = 0; i < sp->typecnt; ++i) {
+			register struct ttinfo *	ttisp;
+
+			ttisp = &sp->ttis[i];
+			if (ttisstdcnt == 0)
+				ttisp->tt_ttisstd = FALSE;
+			else {
+				ttisp->tt_ttisstd = *p++;
+				if (ttisp->tt_ttisstd != TRUE &&
+					ttisp->tt_ttisstd != FALSE)
+						return -1;
+			}
+		}
+		for (i = 0; i < sp->typecnt; ++i) {
+			register struct ttinfo *	ttisp;
+
+			ttisp = &sp->ttis[i];
+			if (ttisgmtcnt == 0)
+				ttisp->tt_ttisgmt = FALSE;
+			else {
+				ttisp->tt_ttisgmt = *p++;
+				if (ttisp->tt_ttisgmt != TRUE &&
+					ttisp->tt_ttisgmt != FALSE)
+						return -1;
+			}
+		}
+		/*
+		** Out-of-sort ats should mean we're running on a
+		** signed time_t system but using a data file with
+		** unsigned values (or vice versa).
+		*/
+		for (i = 0; i < sp->timecnt - 2; ++i)
+			if (sp->ats[i] > sp->ats[i + 1]) {
+				++i;
+				if (TYPE_SIGNED(time_t)) {
+					/*
+					** Ignore the end (easy).
+					*/
+					sp->timecnt = i;
+				} else {
+					/*
+					** Ignore the beginning (harder).
+					*/
+					register int	j;
+
+					for (j = 0; j + i < sp->timecnt; ++j) {
+						sp->ats[j] = sp->ats[j + i];
+						sp->types[j] = sp->types[j + i];
+					}
+					sp->timecnt = j;
+				}
+				break;
+			}
+		/*
+		** If this is an old file, we're done.
+		*/
+		if (u.tzhead.tzh_version[0] == '\0')
+			break;
+		nread -= p - u.buf;
+		for (i = 0; i < nread; ++i)
+			u.buf[i] = p[i];
+		/*
+		** If this is a narrow integer time_t system, we're done.
+		*/
+		if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+			break;
+	}
+	if (doextend && nread > 2 &&
+		u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+		sp->typecnt + 2 <= TZ_MAX_TYPES) {
+			struct state	ts;
+			register int	result;
+
+			u.buf[nread - 1] = '\0';
+			result = tzparse(&u.buf[1], &ts, FALSE);
+			if (result == 0 && ts.typecnt == 2 &&
+				sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+					for (i = 0; i < 2; ++i)
+						ts.ttis[i].tt_abbrind +=
+							sp->charcnt;
+					for (i = 0; i < ts.charcnt; ++i)
+						sp->chars[sp->charcnt++] =
+							ts.chars[i];
+					i = 0;
+					while (i < ts.timecnt &&
+						ts.ats[i] <=
+						sp->ats[sp->timecnt - 1])
+							++i;
+					while (i < ts.timecnt &&
+					    sp->timecnt < TZ_MAX_TIMES) {
+						sp->ats[sp->timecnt] =
+							ts.ats[i];
+						sp->types[sp->timecnt] =
+							sp->typecnt +
+							ts.types[i];
+						++sp->timecnt;
+						++i;
+					}
+					sp->ttis[sp->typecnt++] = ts.ttis[0];
+					sp->ttis[sp->typecnt++] = ts.ttis[1];
+			}
+	}
+	i = 2 * YEARSPERREPEAT;
+	sp->goback = sp->goahead = sp->timecnt > i;
+	sp->goback &= sp->types[i] == sp->types[0] &&
+		differ_by_repeat(sp->ats[i], sp->ats[0]);
+	sp->goahead &=
+		sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
+		differ_by_repeat(sp->ats[sp->timecnt - 1],
+			 sp->ats[sp->timecnt - 1 - i]);
+	return 0;
+}
+
+static const int	mon_lengths[2][MONSPERYEAR] = {
+	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int	year_lengths[2] = {
+	DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char *
+getzname(strp)
+register const char *	strp;
+{
+	register char	c;
+
+	while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+		c != '+')
+			++strp;
+	return strp;
+}
+
+/*
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char *
+getqzname(register const char *strp, const int delim)
+{
+	register int	c;
+
+	while ((c = *strp) != '\0' && c != delim)
+		++strp;
+	return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(strp, nump, min, max)
+register const char *	strp;
+int * const		nump;
+const int		min;
+const int		max;
+{
+	register char	c;
+	register int	num;
+
+	if (strp == NULL || !is_digit(c = *strp))
+		return NULL;
+	num = 0;
+	do {
+		num = num * 10 + (c - '0');
+		if (num > max)
+			return NULL;	/* illegal value */
+		c = *++strp;
+	} while (is_digit(c));
+	if (num < min)
+		return NULL;		/* illegal value */
+	*nump = num;
+	return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(strp, secsp)
+register const char *	strp;
+long * const		secsp;
+{
+	int	num;
+
+	/*
+	** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+	** "M10.4.6/26", which does not conform to Posix,
+	** but which specifies the equivalent of
+	** ``02:00 on the first Sunday on or after 23 Oct''.
+	*/
+	strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+	if (strp == NULL)
+		return NULL;
+	*secsp = num * (long) SECSPERHOUR;
+	if (*strp == ':') {
+		++strp;
+		strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+		if (strp == NULL)
+			return NULL;
+		*secsp += num * SECSPERMIN;
+		if (*strp == ':') {
+			++strp;
+			/* `SECSPERMIN' allows for leap seconds. */
+			strp = getnum(strp, &num, 0, SECSPERMIN);
+			if (strp == NULL)
+				return NULL;
+			*secsp += num;
+		}
+	}
+	return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(strp, offsetp)
+register const char *	strp;
+long * const		offsetp;
+{
+	register int	neg = 0;
+
+	if (*strp == '-') {
+		neg = 1;
+		++strp;
+	} else if (*strp == '+')
+		++strp;
+	strp = getsecs(strp, offsetp);
+	if (strp == NULL)
+		return NULL;		/* illegal time */
+	if (neg)
+		*offsetp = -*offsetp;
+	return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(strp, rulep)
+const char *			strp;
+register struct rule * const	rulep;
+{
+	if (*strp == 'J') {
+		/*
+		** Julian day.
+		*/
+		rulep->r_type = JULIAN_DAY;
+		++strp;
+		strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+	} else if (*strp == 'M') {
+		/*
+		** Month, week, day.
+		*/
+		rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+		++strp;
+		strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+		if (strp == NULL)
+			return NULL;
+		if (*strp++ != '.')
+			return NULL;
+		strp = getnum(strp, &rulep->r_week, 1, 5);
+		if (strp == NULL)
+			return NULL;
+		if (*strp++ != '.')
+			return NULL;
+		strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+	} else if (is_digit(*strp)) {
+		/*
+		** Day of year.
+		*/
+		rulep->r_type = DAY_OF_YEAR;
+		strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+	} else	return NULL;		/* invalid format */
+	if (strp == NULL)
+		return NULL;
+	if (*strp == '/') {
+		/*
+		** Time specified.
+		*/
+		++strp;
+		strp = getsecs(strp, &rulep->r_time);
+	} else	rulep->r_time = 2 * SECSPERHOUR;	/* default = 2:00:00 */
+	return strp;
+}
+
+/*
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t
+transtime(janfirst, year, rulep, offset)
+const time_t				janfirst;
+const int				year;
+register const struct rule * const	rulep;
+const long				offset;
+{
+	register int	leapyear;
+	register time_t	value;
+	register int	i;
+	int		d, m1, yy0, yy1, yy2, dow;
+
+	INITIALIZE(value);
+	leapyear = isleap(year);
+	switch (rulep->r_type) {
+
+	case JULIAN_DAY:
+		/*
+		** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+		** years.
+		** In non-leap years, or if the day number is 59 or less, just
+		** add SECSPERDAY times the day number-1 to the time of
+		** January 1, midnight, to get the day.
+		*/
+		value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+		if (leapyear && rulep->r_day >= 60)
+			value += SECSPERDAY;
+		break;
+
+	case DAY_OF_YEAR:
+		/*
+		** n - day of year.
+		** Just add SECSPERDAY times the day number to the time of
+		** January 1, midnight, to get the day.
+		*/
+		value = janfirst + rulep->r_day * SECSPERDAY;
+		break;
+
+	case MONTH_NTH_DAY_OF_WEEK:
+		/*
+		** Mm.n.d - nth "dth day" of month m.
+		*/
+		value = janfirst;
+		for (i = 0; i < rulep->r_mon - 1; ++i)
+			value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+		/*
+		** Use Zeller's Congruence to get day-of-week of first day of
+		** month.
+		*/
+		m1 = (rulep->r_mon + 9) % 12 + 1;
+		yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+		yy1 = yy0 / 100;
+		yy2 = yy0 % 100;
+		dow = ((26 * m1 - 2) / 10 +
+			1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+		if (dow < 0)
+			dow += DAYSPERWEEK;
+
+		/*
+		** "dow" is the day-of-week of the first day of the month. Get
+		** the day-of-month (zero-origin) of the first "dow" day of the
+		** month.
+		*/
+		d = rulep->r_day - dow;
+		if (d < 0)
+			d += DAYSPERWEEK;
+		for (i = 1; i < rulep->r_week; ++i) {
+			if (d + DAYSPERWEEK >=
+				mon_lengths[leapyear][rulep->r_mon - 1])
+					break;
+			d += DAYSPERWEEK;
+		}
+
+		/*
+		** "d" is the day-of-month (zero-origin) of the day we want.
+		*/
+		value += d * SECSPERDAY;
+		break;
+	}
+
+	/*
+	** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+	** question. To get the Epoch-relative time of the specified local
+	** time on that day, add the transition time and the current offset
+	** from UTC.
+	*/
+	return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char *			name;
+register struct state * const	sp;
+const int			lastditch;
+{
+	const char *			stdname;
+	const char *			dstname;
+	size_t				stdlen;
+	size_t				dstlen;
+	long				stdoffset;
+	long				dstoffset;
+	register time_t *		atp;
+	register unsigned char *	typep;
+	register char *			cp;
+	register int			load_result;
+
+	INITIALIZE(dstname);
+	stdname = name;
+	if (lastditch) {
+		stdlen = strlen(name);	/* length of standard zone name */
+		name += stdlen;
+		if (stdlen >= sizeof sp->chars)
+			stdlen = (sizeof sp->chars) - 1;
+		stdoffset = 0;
+	} else {
+		if (*name == '<') {
+			name++;
+			stdname = name;
+			name = getqzname(name, '>');
+			if (*name != '>')
+				return (-1);
+			stdlen = name - stdname;
+			name++;
+		} else {
+			name = getzname(name);
+			stdlen = name - stdname;
+		}
+		if (*name == '\0')
+			return -1;
+		name = getoffset(name, &stdoffset);
+		if (name == NULL)
+			return -1;
+	}
+	load_result = tzload(TZDEFRULES, sp, FALSE);
+	if (load_result != 0)
+		sp->leapcnt = 0;		/* so, we're off a little */
+	sp->timecnt = 0;
+	if (*name != '\0') {
+		if (*name == '<') {
+			dstname = ++name;
+			name = getqzname(name, '>');
+			if (*name != '>')
+				return -1;
+			dstlen = name - dstname;
+			name++;
+		} else {
+			dstname = name;
+			name = getzname(name);
+			dstlen = name - dstname; /* length of DST zone name */
+		}
+		if (*name != '\0' && *name != ',' && *name != ';') {
+			name = getoffset(name, &dstoffset);
+			if (name == NULL)
+				return -1;
+		} else	dstoffset = stdoffset - SECSPERHOUR;
+		if (*name == '\0' && load_result != 0)
+			name = TZDEFRULESTRING;
+		if (*name == ',' || *name == ';') {
+			struct rule	start;
+			struct rule	end;
+			register int	year;
+			register time_t	janfirst;
+			time_t		starttime;
+			time_t		endtime;
+
+			++name;
+			if ((name = getrule(name, &start)) == NULL)
+				return -1;
+			if (*name++ != ',')
+				return -1;
+			if ((name = getrule(name, &end)) == NULL)
+				return -1;
+			if (*name != '\0')
+				return -1;
+			sp->typecnt = 2;	/* standard time and DST */
+			/*
+			** Two transitions per year, from EPOCH_YEAR forward.
+			*/
+			sp->ttis[0].tt_gmtoff = -dstoffset;
+			sp->ttis[0].tt_isdst = 1;
+			sp->ttis[0].tt_abbrind = stdlen + 1;
+			sp->ttis[1].tt_gmtoff = -stdoffset;
+			sp->ttis[1].tt_isdst = 0;
+			sp->ttis[1].tt_abbrind = 0;
+			atp = sp->ats;
+			typep = sp->types;
+			janfirst = 0;
+			for (year = EPOCH_YEAR;
+			    sp->timecnt + 2 <= TZ_MAX_TIMES;
+			    ++year) {
+			    	time_t	newfirst;
+
+				starttime = transtime(janfirst, year, &start,
+					stdoffset);
+				endtime = transtime(janfirst, year, &end,
+					dstoffset);
+				if (starttime > endtime) {
+					*atp++ = endtime;
+					*typep++ = 1;	/* DST ends */
+					*atp++ = starttime;
+					*typep++ = 0;	/* DST begins */
+				} else {
+					*atp++ = starttime;
+					*typep++ = 0;	/* DST begins */
+					*atp++ = endtime;
+					*typep++ = 1;	/* DST ends */
+				}
+				sp->timecnt += 2;
+				newfirst = janfirst;
+				newfirst += year_lengths[isleap(year)] *
+					SECSPERDAY;
+				if (newfirst <= janfirst)
+					break;
+				janfirst = newfirst;
+			}
+		} else {
+			register long	theirstdoffset;
+			register long	theirdstoffset;
+			register long	theiroffset;
+			register int	isdst;
+			register int	i;
+			register int	j;
+
+			if (*name != '\0')
+				return -1;
+			/*
+			** Initial values of theirstdoffset and theirdstoffset.
+			*/
+			theirstdoffset = 0;
+			for (i = 0; i < sp->timecnt; ++i) {
+				j = sp->types[i];
+				if (!sp->ttis[j].tt_isdst) {
+					theirstdoffset =
+						-sp->ttis[j].tt_gmtoff;
+					break;
+				}
+			}
+			theirdstoffset = 0;
+			for (i = 0; i < sp->timecnt; ++i) {
+				j = sp->types[i];
+				if (sp->ttis[j].tt_isdst) {
+					theirdstoffset =
+						-sp->ttis[j].tt_gmtoff;
+					break;
+				}
+			}
+			/*
+			** Initially we're assumed to be in standard time.
+			*/
+			isdst = FALSE;
+			theiroffset = theirstdoffset;
+			/*
+			** Now juggle transition times and types
+			** tracking offsets as you do.
+			*/
+			for (i = 0; i < sp->timecnt; ++i) {
+				j = sp->types[i];
+				sp->types[i] = sp->ttis[j].tt_isdst;
+				if (sp->ttis[j].tt_ttisgmt) {
+					/* No adjustment to transition time */
+				} else {
+					/*
+					** If summer time is in effect, and the
+					** transition time was not specified as
+					** standard time, add the summer time
+					** offset to the transition time;
+					** otherwise, add the standard time
+					** offset to the transition time.
+					*/
+					/*
+					** Transitions from DST to DDST
+					** will effectively disappear since
+					** POSIX provides for only one DST
+					** offset.
+					*/
+					if (isdst && !sp->ttis[j].tt_ttisstd) {
+						sp->ats[i] += dstoffset -
+							theirdstoffset;
+					} else {
+						sp->ats[i] += stdoffset -
+							theirstdoffset;
+					}
+				}
+				theiroffset = -sp->ttis[j].tt_gmtoff;
+				if (sp->ttis[j].tt_isdst)
+					theirdstoffset = theiroffset;
+				else	theirstdoffset = theiroffset;
+			}
+			/*
+			** Finally, fill in ttis.
+			** ttisstd and ttisgmt need not be handled.
+			*/
+			sp->ttis[0].tt_gmtoff = -stdoffset;
+			sp->ttis[0].tt_isdst = FALSE;
+			sp->ttis[0].tt_abbrind = 0;
+			sp->ttis[1].tt_gmtoff = -dstoffset;
+			sp->ttis[1].tt_isdst = TRUE;
+			sp->ttis[1].tt_abbrind = stdlen + 1;
+			sp->typecnt = 2;
+		}
+	} else {
+		dstlen = 0;
+		sp->typecnt = 1;		/* only standard time */
+		sp->timecnt = 0;
+		sp->ttis[0].tt_gmtoff = -stdoffset;
+		sp->ttis[0].tt_isdst = 0;
+		sp->ttis[0].tt_abbrind = 0;
+	}
+	sp->charcnt = stdlen + 1;
+	if (dstlen != 0)
+		sp->charcnt += dstlen + 1;
+	if ((size_t) sp->charcnt > sizeof sp->chars)
+		return -1;
+	cp = sp->chars;
+	(void) strncpy(cp, stdname, stdlen);
+	cp += stdlen;
+	*cp++ = '\0';
+	if (dstlen != 0) {
+		(void) strncpy(cp, dstname, dstlen);
+		*(cp + dstlen) = '\0';
+	}
+	return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const	sp;
+{
+	if (tzload(gmt, sp, TRUE) != 0)
+		(void) tzparse(gmt, sp, TRUE);
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static struct tm *
+localsub(timep, offset, tmp, sp)
+const time_t * const	timep;
+const long		offset;
+struct tm * const	tmp;
+struct state *		sp;
+{
+	register const struct ttinfo *	ttisp;
+	register int			i;
+	register struct tm *		result;
+	const time_t			t = *timep;
+
+#ifdef ALL_STATE
+	if (sp == NULL)
+		return gmtsub(timep, offset, tmp);
+#endif /* defined ALL_STATE */
+	if ((sp->goback && t < sp->ats[0]) ||
+		(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+			time_t			newt = t;
+			register time_t		seconds;
+			register time_t		tcycles;
+			register int_fast64_t	icycles;
+
+			if (t < sp->ats[0])
+				seconds = sp->ats[0] - t;
+			else	seconds = t - sp->ats[sp->timecnt - 1];
+			--seconds;
+			tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+			++tcycles;
+			icycles = tcycles;
+			if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+				return NULL;
+			seconds = icycles;
+			seconds *= YEARSPERREPEAT;
+			seconds *= AVGSECSPERYEAR;
+			if (t < sp->ats[0])
+				newt += seconds;
+			else	newt -= seconds;
+			if (newt < sp->ats[0] ||
+				newt > sp->ats[sp->timecnt - 1])
+					return NULL;	/* "cannot happen" */
+			result = localsub(&newt, offset, tmp, sp);
+			if (result == tmp) {
+				register time_t	newy;
+
+				newy = tmp->tm_year;
+				if (t < sp->ats[0])
+					newy -= icycles * YEARSPERREPEAT;
+				else	newy += icycles * YEARSPERREPEAT;
+				tmp->tm_year = newy;
+				if (tmp->tm_year != newy)
+					return NULL;
+			}
+			return result;
+	}
+	if (sp->timecnt == 0 || t < sp->ats[0]) {
+		i = 0;
+		while (sp->ttis[i].tt_isdst)
+			if (++i >= sp->typecnt) {
+				i = 0;
+				break;
+			}
+	} else {
+		register int	lo = 1;
+		register int	hi = sp->timecnt;
+
+		while (lo < hi) {
+			register int	mid = (lo + hi) >> 1;
+
+			if (t < sp->ats[mid])
+				hi = mid;
+			else	lo = mid + 1;
+		}
+		i = (int) sp->types[lo - 1];
+	}
+	ttisp = &sp->ttis[i];
+	/*
+	** To get (wrong) behavior that's compatible with System V Release 2.0
+	** you'd replace the statement below with
+	**	t += ttisp->tt_gmtoff;
+	**	timesub(&t, 0L, sp, tmp);
+	*/
+	result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+	tmp->tm_isdst = ttisp->tt_isdst;
+#ifdef HAVE_TM_GMTOFF
+	tmp->tm_gmtoff = ttisp->tt_gmtoff;
+#endif
+	tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
+#ifdef TM_ZONE
+	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+	return result;
+}
+
+
+// ============================================================================
+#if 0
+struct tm *
+localtime(timep)
+const time_t * const	timep;
+{
+	tzset();
+	return localsub(timep, 0L, &tm);
+}
+#endif
+
+/*
+** Re-entrant version of localtime.
+*/
+
+// ============================================================================
+void
+localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz)
+{
+    struct state st;
+    if (tzload(tz, &st, TRUE) != 0) {
+        // not sure what's best here, but for now, we fall back to gmt
+        gmtload(&st);
+    }
+
+	localsub(timep, 0L, tmp, &st);
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *
+gmtsub(timep, offset, tmp)
+const time_t * const	timep;
+const long		offset;
+struct tm * const	tmp;
+{
+	register struct tm *	result;
+
+	if (!gmt_is_set) {
+		gmt_is_set = TRUE;
+#ifdef ALL_STATE
+		gmtptr = (struct state *) malloc(sizeof *gmtptr);
+		if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+			gmtload(gmtptr);
+	}
+	result = timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+	/*
+	** Could get fancy here and deliver something such as
+	** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+	** but this is no time for a treasure hunt.
+	*/
+	if (offset != 0)
+		tmp->TM_ZONE = wildabbr;
+	else {
+#ifdef ALL_STATE
+		if (gmtptr == NULL)
+			tmp->TM_ZONE = gmt;
+		else	tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+		tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+	}
+#endif /* defined TM_ZONE */
+	return result;
+}
+
+// ============================================================================
+#if 0
+struct tm *
+gmtime(timep)
+const time_t * const	timep;
+{
+	return gmtsub(timep, 0L, &tm);
+}
+#endif
+
+/*
+* Re-entrant version of gmtime.
+*/
+
+// ============================================================================
+#if 0
+struct tm *
+gmtime_r(timep, tmp)
+const time_t * const	timep;
+struct tm *		tmp;
+{
+	return gmtsub(timep, 0L, tmp);
+}
+#endif
+
+#ifdef STD_INSPIRED
+
+// ============================================================================
+#if 0
+struct tm *
+offtime(timep, offset)
+const time_t * const	timep;
+const long		offset;
+{
+	return gmtsub(timep, offset, &tm);
+}
+#endif
+
+#endif /* defined STD_INSPIRED */
+
+/*
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+
+static int
+leaps_thru_end_of(y)
+register const int	y;
+{
+	return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+		-(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct tm *
+timesub(timep, offset, sp, tmp)
+const time_t * const			timep;
+const long				offset;
+register const struct state * const	sp;
+register struct tm * const		tmp;
+{
+	register const struct lsinfo *	lp;
+	register time_t			tdays;
+	register int			idays;	/* unsigned would be so 2003 */
+	register long			rem;
+	int				y;
+	register const int *		ip;
+	register long			corr;
+	register int			hit;
+	register int			i;
+
+	corr = 0;
+	hit = 0;
+#ifdef ALL_STATE
+	i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+	i = sp->leapcnt;
+#endif /* State Farm */
+	while (--i >= 0) {
+		lp = &sp->lsis[i];
+		if (*timep >= lp->ls_trans) {
+			if (*timep == lp->ls_trans) {
+				hit = ((i == 0 && lp->ls_corr > 0) ||
+					lp->ls_corr > sp->lsis[i - 1].ls_corr);
+				if (hit)
+					while (i > 0 &&
+						sp->lsis[i].ls_trans ==
+						sp->lsis[i - 1].ls_trans + 1 &&
+						sp->lsis[i].ls_corr ==
+						sp->lsis[i - 1].ls_corr + 1) {
+							++hit;
+							--i;
+					}
+			}
+			corr = lp->ls_corr;
+			break;
+		}
+	}
+	y = EPOCH_YEAR;
+	tdays = *timep / SECSPERDAY;
+	rem = *timep - tdays * SECSPERDAY;
+	while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+		int		newy;
+		register time_t	tdelta;
+		register int	idelta;
+		register int	leapdays;
+
+		tdelta = tdays / DAYSPERLYEAR;
+		idelta = tdelta;
+		if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+			return NULL;
+		if (idelta == 0)
+			idelta = (tdays < 0) ? -1 : 1;
+		newy = y;
+		if (increment_overflow(&newy, idelta))
+			return NULL;
+		leapdays = leaps_thru_end_of(newy - 1) -
+			leaps_thru_end_of(y - 1);
+		tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+		tdays -= leapdays;
+		y = newy;
+	}
+	{
+		register long	seconds;
+
+		seconds = tdays * SECSPERDAY + 0.5;
+		tdays = seconds / SECSPERDAY;
+		rem += seconds - tdays * SECSPERDAY;
+	}
+	/*
+	** Given the range, we can now fearlessly cast...
+	*/
+	idays = tdays;
+	rem += offset - corr;
+	while (rem < 0) {
+		rem += SECSPERDAY;
+		--idays;
+	}
+	while (rem >= SECSPERDAY) {
+		rem -= SECSPERDAY;
+		++idays;
+	}
+	while (idays < 0) {
+		if (increment_overflow(&y, -1))
+			return NULL;
+		idays += year_lengths[isleap(y)];
+	}
+	while (idays >= year_lengths[isleap(y)]) {
+		idays -= year_lengths[isleap(y)];
+		if (increment_overflow(&y, 1))
+			return NULL;
+	}
+	tmp->tm_year = y;
+	if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+		return NULL;
+	tmp->tm_yday = idays;
+	/*
+	** The "extra" mods below avoid overflow problems.
+	*/
+	tmp->tm_wday = EPOCH_WDAY +
+		((y - EPOCH_YEAR) % DAYSPERWEEK) *
+		(DAYSPERNYEAR % DAYSPERWEEK) +
+		leaps_thru_end_of(y - 1) -
+		leaps_thru_end_of(EPOCH_YEAR - 1) +
+		idays;
+	tmp->tm_wday %= DAYSPERWEEK;
+	if (tmp->tm_wday < 0)
+		tmp->tm_wday += DAYSPERWEEK;
+	tmp->tm_hour = (int) (rem / SECSPERHOUR);
+	rem %= SECSPERHOUR;
+	tmp->tm_min = (int) (rem / SECSPERMIN);
+	/*
+	** A positive leap second requires a special
+	** representation. This uses "... ??:59:60" et seq.
+	*/
+	tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+	ip = mon_lengths[isleap(y)];
+	for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+		idays -= ip[tmp->tm_mon];
+	tmp->tm_mday = (int) (idays + 1);
+	tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+	tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+	return tmp;
+}
+
+// ============================================================================
+#if 0
+char *
+ctime(timep)
+const time_t * const	timep;
+{
+/*
+** Section 4.12.3.2 of X3.159-1989 requires that
+**	The ctime function converts the calendar time pointed to by timer
+**	to local time in the form of a string. It is equivalent to
+**		asctime(localtime(timer))
+*/
+	return asctime(localtime(timep));
+}
+#endif
+
+// ============================================================================
+#if 0
+char *
+ctime_r(timep, buf)
+const time_t * const	timep;
+char *			buf;
+{
+	struct tm	mytm;
+
+	return asctime_r(localtime_r(timep, &mytm), buf);
+}
+#endif
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+**	The "best" way to do mktime I think is based on an idea of Bob
+**	Kridle's (so its said...) from a long time ago.
+**	It does a binary search of the time_t space. Since time_t's are
+**	just 32 bits, its a max of 32 iterations (even at 64 bits it
+**	would still be very reasonable).
+*/
+
+#ifndef WRONG
+#define WRONG	(-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert.
+*/
+
+static int
+increment_overflow(number, delta)
+int *	number;
+int	delta;
+{
+	int	number0;
+
+	number0 = *number;
+	*number += delta;
+	return (*number < number0) != (delta < 0);
+}
+
+static int
+long_increment_overflow(number, delta)
+long *	number;
+int	delta;
+{
+	long	number0;
+
+	number0 = *number;
+	*number += delta;
+	return (*number < number0) != (delta < 0);
+}
+
+static int
+normalize_overflow(tensptr, unitsptr, base)
+int * const	tensptr;
+int * const	unitsptr;
+const int	base;
+{
+	register int	tensdelta;
+
+	tensdelta = (*unitsptr >= 0) ?
+		(*unitsptr / base) :
+		(-1 - (-1 - *unitsptr) / base);
+	*unitsptr -= tensdelta * base;
+	return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+long_normalize_overflow(tensptr, unitsptr, base)
+long * const	tensptr;
+int * const	unitsptr;
+const int	base;
+{
+	register int	tensdelta;
+
+	tensdelta = (*unitsptr >= 0) ?
+		(*unitsptr / base) :
+		(-1 - (-1 - *unitsptr) / base);
+	*unitsptr -= tensdelta * base;
+	return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int
+tmcomp(atmp, btmp)
+register const struct tm * const atmp;
+register const struct tm * const btmp;
+{
+	register int	result;
+
+	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+		(result = (atmp->tm_min - btmp->tm_min)) == 0)
+			result = atmp->tm_sec - btmp->tm_sec;
+	return result;
+}
+
+static time_t
+time2sub(tmp, funcp, offset, okayp, do_norm_secs, sp)
+struct tm * const	tmp;
+struct tm * (* const	funcp) P((const time_t*, long, struct tm*,const struct state *sp));
+const long		offset;
+int * const		okayp;
+const int		do_norm_secs;
+const struct state *	sp;
+{
+	register int			dir;
+	register int			i, j;
+	register int			saved_seconds;
+	register long			li;
+	register time_t			lo;
+	register time_t			hi;
+	long				y;
+	time_t				newt;
+	time_t				t;
+	struct tm			yourtm, mytm;
+
+	*okayp = FALSE;
+	yourtm = *tmp;
+	if (do_norm_secs) {
+		if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+			SECSPERMIN))
+				return WRONG;
+	}
+	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+		return WRONG;
+	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+		return WRONG;
+	y = yourtm.tm_year;
+	if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
+		return WRONG;
+	/*
+	** Turn y into an actual year number for now.
+	** It is converted back to an offset from TM_YEAR_BASE later.
+	*/
+	if (long_increment_overflow(&y, TM_YEAR_BASE))
+		return WRONG;
+	while (yourtm.tm_mday <= 0) {
+		if (long_increment_overflow(&y, -1))
+			return WRONG;
+		li = y + (1 < yourtm.tm_mon);
+		yourtm.tm_mday += year_lengths[isleap(li)];
+	}
+	while (yourtm.tm_mday > DAYSPERLYEAR) {
+		li = y + (1 < yourtm.tm_mon);
+		yourtm.tm_mday -= year_lengths[isleap(li)];
+		if (long_increment_overflow(&y, 1))
+			return WRONG;
+	}
+	for ( ; ; ) {
+		i = mon_lengths[isleap(y)][yourtm.tm_mon];
+		if (yourtm.tm_mday <= i)
+			break;
+		yourtm.tm_mday -= i;
+		if (++yourtm.tm_mon >= MONSPERYEAR) {
+			yourtm.tm_mon = 0;
+			if (long_increment_overflow(&y, 1))
+				return WRONG;
+		}
+	}
+	if (long_increment_overflow(&y, -TM_YEAR_BASE))
+		return WRONG;
+	yourtm.tm_year = y;
+	if (yourtm.tm_year != y)
+		return WRONG;
+	if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+		saved_seconds = 0;
+	else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+		/*
+		** We can't set tm_sec to 0, because that might push the
+		** time below the minimum representable time.
+		** Set tm_sec to 59 instead.
+		** This assumes that the minimum representable time is
+		** not in the same minute that a leap second was deleted from,
+		** which is a safer assumption than using 58 would be.
+		*/
+		if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+			return WRONG;
+		saved_seconds = yourtm.tm_sec;
+		yourtm.tm_sec = SECSPERMIN - 1;
+	} else {
+		saved_seconds = yourtm.tm_sec;
+		yourtm.tm_sec = 0;
+	}
+	/*
+	** Do a binary search (this works whatever time_t's type is).
+	*/
+	if (!TYPE_SIGNED(time_t)) {
+		lo = 0;
+		hi = lo - 1;
+	} else if (!TYPE_INTEGRAL(time_t)) {
+		if (sizeof(time_t) > sizeof(float))
+			hi = (time_t) DBL_MAX;
+		else	hi = (time_t) FLT_MAX;
+		lo = -hi;
+	} else {
+		lo = 1;
+		for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+			lo *= 2;
+		hi = -(lo + 1);
+	}
+	for ( ; ; ) {
+		t = lo / 2 + hi / 2;
+		if (t < lo)
+			t = lo;
+		else if (t > hi)
+			t = hi;
+		if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
+			/*
+			** Assume that t is too extreme to be represented in
+			** a struct tm; arrange things so that it is less
+			** extreme on the next pass.
+			*/
+			dir = (t > 0) ? 1 : -1;
+		} else	dir = tmcomp(&mytm, &yourtm);
+		if (dir != 0) {
+			if (t == lo) {
+				++t;
+				if (t <= lo)
+					return WRONG;
+				++lo;
+			} else if (t == hi) {
+				--t;
+				if (t >= hi)
+					return WRONG;
+				--hi;
+			}
+			if (lo > hi)
+				return WRONG;
+			if (dir > 0)
+				hi = t;
+			else	lo = t;
+			continue;
+		}
+		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+			break;
+		/*
+		** Right time, wrong type.
+		** Hunt for right time, right type.
+		** It's okay to guess wrong since the guess
+		** gets checked.
+		*/
+		/*
+		** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+		*/
+#ifdef ALL_STATE
+		if (sp == NULL)
+			return WRONG;
+#endif /* defined ALL_STATE */
+		for (i = sp->typecnt - 1; i >= 0; --i) {
+			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+				continue;
+			for (j = sp->typecnt - 1; j >= 0; --j) {
+				if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+					continue;
+				newt = t + sp->ttis[j].tt_gmtoff -
+					sp->ttis[i].tt_gmtoff;
+				if ((*funcp)(&newt, offset, &mytm, sp) == NULL)
+					continue;
+				if (tmcomp(&mytm, &yourtm) != 0)
+					continue;
+				if (mytm.tm_isdst != yourtm.tm_isdst)
+					continue;
+				/*
+				** We have a match.
+				*/
+				t = newt;
+				goto label;
+			}
+		}
+		return WRONG;
+	}
+label:
+	newt = t + saved_seconds;
+	if ((newt < t) != (saved_seconds < 0))
+		return WRONG;
+	t = newt;
+	if ((*funcp)(&t, offset, tmp, sp))
+		*okayp = TRUE;
+	return t;
+}
+
+static time_t
+time2(tmp, funcp, offset, okayp, sp)
+struct tm * const	tmp;
+struct tm * (* const	funcp) P((const time_t*, long, struct tm*,
+            const struct state* sp));
+const long		offset;
+int * const		okayp;
+const struct state *	sp;
+{
+	time_t	t;
+
+	/*
+	** First try without normalization of seconds
+	** (in case tm_sec contains a value associated with a leap second).
+	** If that fails, try with normalization of seconds.
+	*/
+	t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
+	return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
+}
+
+static time_t
+time1(tmp, funcp, offset, sp)
+struct tm * const	tmp;
+struct tm * (* const	funcp) P((const time_t *, long, struct tm *, const struct state* sp));
+const long		offset;
+const struct state *	sp;
+{
+	register time_t			t;
+	register int			samei, otheri;
+	register int			sameind, otherind;
+	register int			i;
+	register int			nseen;
+	int				seen[TZ_MAX_TYPES];
+	int				types[TZ_MAX_TYPES];
+	int				okay;
+
+	if (tmp->tm_isdst > 1)
+		tmp->tm_isdst = 1;
+	t = time2(tmp, funcp, offset, &okay, sp);
+#define PCTS 1
+#ifdef PCTS
+	/*
+	** PCTS code courtesy Grant Sullivan.
+	*/
+	if (okay)
+		return t;
+	if (tmp->tm_isdst < 0)
+		tmp->tm_isdst = 0;	/* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+	if (okay || tmp->tm_isdst < 0)
+		return t;
+#endif /* !defined PCTS */
+	/*
+	** We're supposed to assume that somebody took a time of one type
+	** and did some math on it that yielded a "struct tm" that's bad.
+	** We try to divine the type they started from and adjust to the
+	** type they need.
+	*/
+	/*
+	** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+	*/
+#ifdef ALL_STATE
+	if (sp == NULL)
+		return WRONG;
+#endif /* defined ALL_STATE */
+	for (i = 0; i < sp->typecnt; ++i)
+		seen[i] = FALSE;
+	nseen = 0;
+	for (i = sp->timecnt - 1; i >= 0; --i)
+		if (!seen[sp->types[i]]) {
+			seen[sp->types[i]] = TRUE;
+			types[nseen++] = sp->types[i];
+		}
+	for (sameind = 0; sameind < nseen; ++sameind) {
+		samei = types[sameind];
+		if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+			continue;
+		for (otherind = 0; otherind < nseen; ++otherind) {
+			otheri = types[otherind];
+			if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+				continue;
+			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+					sp->ttis[samei].tt_gmtoff;
+			tmp->tm_isdst = !tmp->tm_isdst;
+			t = time2(tmp, funcp, offset, &okay, sp);
+			if (okay)
+				return t;
+			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+					sp->ttis[samei].tt_gmtoff;
+			tmp->tm_isdst = !tmp->tm_isdst;
+		}
+	}
+	return WRONG;
+}
+
+// ============================================================================
+time_t
+mktime_tz(struct tm * const	tmp, char const * tz)
+{
+    struct state st;
+    if (tzload(tz, &st, TRUE) != 0) {
+        // not sure what's best here, but for now, we fall back to gmt
+        gmtload(&st);
+    }
+	return time1(tmp, localsub, 0L, &st);
+}
diff --git a/libcutils/uio.c b/libcutils/uio.c
new file mode 100644
index 0000000..baa8051
--- /dev/null
+++ b/libcutils/uio.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HAVE_SYS_UIO_H
+
+#include <cutils/uio.h>
+#include <unistd.h>
+
+int  readv( int  fd, struct iovec*  vecs, int  count )
+{
+    int   total = 0;
+
+    for ( ; count > 0; count--, vecs++ ) {
+        const char*  buf = vecs->iov_base;
+        int          len = vecs->iov_len;
+        
+        while (len > 0) {
+            int  ret = read( fd, buf, len );
+            if (ret < 0) {
+                if (total == 0)
+                    total = -1;
+                goto Exit;
+            }
+            if (ret == 0)
+                goto Exit;
+
+            total += ret;
+            buf   += ret;
+            len   -= ret;
+        }
+    }
+Exit:
+    return total;
+}
+
+int  writev( int  fd, const struct iovec*  vecs, int  count )
+{
+    int   total = 0;
+
+    for ( ; count > 0; count--, vecs++ ) {
+        const char*  buf = (const char*)vecs->iov_base;
+        int          len = (int)vecs->iov_len;
+        
+        while (len > 0) {
+            int  ret = write( fd, buf, len );
+            if (ret < 0) {
+                if (total == 0)
+                    total = -1;
+                goto Exit;
+            }
+            if (ret == 0)
+                goto Exit;
+
+            total += ret;
+            buf   += ret;
+            len   -= ret;
+        }
+    }
+Exit:    
+    return total;
+}
+
+#endif /* !HAVE_SYS_UIO_H */
diff --git a/libcutils/zygote.c b/libcutils/zygote.c
new file mode 100644
index 0000000..aa060c0
--- /dev/null
+++ b/libcutils/zygote.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Zygote"
+
+#include <cutils/sockets.h>
+#include <cutils/zygote.h>
+#include <cutils/log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define ZYGOTE_SOCKET "zygote"
+
+#define ZYGOTE_RETRY_COUNT 1000
+#define ZYGOTE_RETRY_MILLIS 500
+
+static void replace_nl(char *str);
+
+/*
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ */
+static int send_request(int fd, int sendStdio, int argc, const char **argv)
+{
+#ifndef HAVE_ANDROID_OS
+    // not supported on simulator targets
+    //LOGE("zygote_* not supported on simulator targets");
+    return -1;
+#else /* HAVE_ANDROID_OS */
+    uint32_t pid;
+    int i;
+    struct iovec ivs[2];
+    struct msghdr msg;
+    char argc_buffer[12];
+    const char *newline_string = "\n";
+    struct cmsghdr *cmsg;
+    char msgbuf[CMSG_SPACE(sizeof(int) * 3)];
+    int *cmsg_payload;
+    ssize_t ret;
+
+    memset(&msg, 0, sizeof(msg));
+    memset(&ivs, 0, sizeof(ivs));
+
+    // First line is arg count 
+    snprintf(argc_buffer, sizeof(argc_buffer), "%d\n", argc);
+
+    ivs[0].iov_base = argc_buffer;
+    ivs[0].iov_len = strlen(argc_buffer);
+
+    msg.msg_iov = ivs;
+    msg.msg_iovlen = 1;
+
+    if (sendStdio != 0) {
+        // Pass the file descriptors with the first write
+        msg.msg_control = msgbuf;
+        msg.msg_controllen = sizeof msgbuf;
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+
+        cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+
+        cmsg_payload = (int *)CMSG_DATA(cmsg);
+        cmsg_payload[0] = STDIN_FILENO;
+        cmsg_payload[1] = STDOUT_FILENO;
+        cmsg_payload[2] = STDERR_FILENO;
+    }
+
+    do {
+        ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        return -1;
+    }
+
+    // Only send the fd's once
+    msg.msg_control = NULL;
+    msg.msg_controllen = 0;
+
+    // replace any newlines with spaces and send the args
+    for (i = 0; i < argc; i++) {
+        char *tofree = NULL;
+        const char *toprint;
+
+        toprint = argv[i];
+
+        if (strchr(toprint, '\n') != NULL) {
+            tofree = strdup(toprint);
+            toprint = tofree;
+            replace_nl(tofree);
+        }
+
+        ivs[0].iov_base = (char *)toprint;
+        ivs[0].iov_len = strlen(toprint);
+        ivs[1].iov_base = (char *)newline_string;
+        ivs[1].iov_len = 1;
+
+        msg.msg_iovlen = 2;
+
+        do {
+            ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+        } while (ret < 0 && errno == EINTR);
+
+        if (tofree != NULL) {
+            free(tofree);
+        }
+
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    // Read the pid, as a 4-byte network-order integer
+
+    ivs[0].iov_base = &pid;
+    ivs[0].iov_len = sizeof(pid);
+    msg.msg_iovlen = 1;
+
+    do {
+        do {
+            ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);
+        } while (ret < 0 && errno == EINTR);
+
+        if (ret < 0) {
+            return -1;
+        }
+
+        ivs[0].iov_len -= ret;
+        ivs[0].iov_base += ret;
+    } while (ivs[0].iov_len > 0);
+
+    pid = ntohl(pid);
+
+    return pid;
+#endif /* HAVE_ANDROID_OS */
+}
+
+int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int))
+{
+    int fd;
+    int pid;
+    int err;
+    const char *newargv[argc + 1];
+
+    fd = socket_local_client(ZYGOTE_SOCKET, 
+            ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL);
+
+    if (fd < 0) {
+        return -1;
+    }
+
+    // The command socket is passed to the peer as close-on-exec
+    // and will close when the peer dies
+    newargv[0] = "--peer-wait";
+    memcpy(newargv + 1, argv, argc * sizeof(*argv)); 
+
+    pid = send_request(fd, 1, argc + 1, newargv);
+
+    if (pid > 0 && post_run_func != NULL) {
+        post_run_func(pid);
+    }
+
+    // Wait for socket to close
+    do {
+        int dummy;
+        err = read(fd, &dummy, sizeof(dummy));
+    } while ((err < 0 && errno == EINTR) || err != 0);
+
+    do {
+        err = close(fd);
+    } while (err < 0 && errno == EINTR);
+
+    return 0;
+}
+
+/**
+ * Spawns a new dalvik instance via the Zygote process. The non-zygote
+ * arguments are passed to com.android.internal.os.RuntimeInit(). The
+ * first non-option argument should be a class name in the system class path.
+ *
+ * The arg list  may start with zygote params such as --set-uid.
+ *
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ *
+ * The pid of the child process is returned, or -1 if an error was 
+ * encountered.
+ *
+ * zygote_run_oneshot waits up to ZYGOTE_RETRY_COUNT * 
+ * ZYGOTE_RETRY_MILLIS for the zygote socket to be available.
+ */
+int zygote_run_oneshot(int sendStdio, int argc, const char **argv) 
+{
+    int fd = -1;
+    int err;
+    int i;
+    int retries;
+    int pid;
+    const char **newargv = argv;
+    const int newargc = argc;
+
+    for (retries = 0; (fd < 0) && (retries < ZYGOTE_RETRY_COUNT); retries++) {
+        if (retries > 0) { 
+            struct timespec ts;
+
+            memset(&ts, 0, sizeof(ts));
+            ts.tv_nsec = ZYGOTE_RETRY_MILLIS * 1000 * 1000;
+
+            do {
+                err = nanosleep (&ts, &ts);
+            } while (err < 0 && errno == EINTR);
+        }
+        fd = socket_local_client(ZYGOTE_SOCKET, AF_LOCAL, 
+                ANDROID_SOCKET_NAMESPACE_RESERVED);
+    }
+
+    if (fd < 0) {
+        return -1;
+    }
+
+    pid = send_request(fd, 0, newargc, newargv);
+
+    do {
+        err = close(fd);
+    } while (err < 0 && errno == EINTR);
+
+    return pid;
+}
+
+/**
+ * Replaces all occurrances of newline with space.
+ */
+static void replace_nl(char *str)
+{
+    for(; *str; str++) {
+        if (*str == '\n') {
+            *str = ' ';
+        }
+    }
+}
+
+
+
diff --git a/liblog/Android.mk b/liblog/Android.mk
new file mode 100644
index 0000000..0eec87f
--- /dev/null
+++ b/liblog/Android.mk
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+liblog_sources := logd_write.c
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+WITH_MINGW :=
+ifeq ($(HOST_OS),windows)
+    ifeq ($(strip $(USE_CYGWIN)),)
+        WITH_MINGW := true
+    endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+    WITH_MINGW := true
+endif
+
+ifndef WITH_MINGW
+    liblog_sources += \
+        logprint.c \
+        event_tag_map.c
+endif
+
+liblog_host_sources := $(liblog_sources) fake_log_device.c
+
+# Static library for host
+# ========================================================
+LOCAL_MODULE := liblog
+LOCAL_SRC_FILES := $(liblog_host_sources)
+LOCAL_LDLIBS := -lpthread
+LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+ifeq ($(TARGET_SIMULATOR),true)
+  # Shared library for simulator
+  # ========================================================
+  include $(CLEAR_VARS)
+  LOCAL_MODULE := liblog
+  LOCAL_SRC_FILES := $(liblog_host_sources)
+  LOCAL_LDLIBS := -lpthread
+  LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1
+  include $(BUILD_SHARED_LIBRARY)
+else # !sim
+  # Shared and static library for target
+  # ========================================================
+  include $(CLEAR_VARS)
+  LOCAL_MODULE := liblog
+  LOCAL_SRC_FILES := $(liblog_sources)
+  include $(BUILD_STATIC_LIBRARY)
+
+  include $(CLEAR_VARS)
+  LOCAL_MODULE := liblog
+  LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+  include $(BUILD_SHARED_LIBRARY)
+endif # !sim
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
new file mode 100644
index 0000000..e70754e
--- /dev/null
+++ b/liblog/event_tag_map.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "cutils/event_tag_map.h"
+#include "cutils/log.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <assert.h>
+
+#define OUT_TAG "EventTagMap"
+
+/*
+ * Single entry.
+ */
+typedef struct EventTag {
+    unsigned int    tagIndex;
+    const char*     tagStr;
+} EventTag;
+
+/*
+ * Map.
+ */
+struct EventTagMap {
+    /* memory-mapped source file; we get strings from here */
+    void*           mapAddr;
+    size_t          mapLen;
+
+    /* array of event tags, sorted numerically by tag index */
+    EventTag*       tagArray;
+    int             numTags;
+};
+
+/* fwd */
+static int processFile(EventTagMap* map);
+static int countMapLines(const EventTagMap* map);
+static int parseMapLines(EventTagMap* map);
+static int scanTagLine(char** pData, EventTag* tag, int lineNum);
+static int sortTags(EventTagMap* map);
+static void dumpTags(const EventTagMap* map);
+
+
+/*
+ * Open the map file and allocate a structure to manage it.
+ *
+ * We create a private mapping because we want to terminate the log tag
+ * strings with '\0'.
+ */
+EventTagMap* android_openEventTagMap(const char* fileName)
+{
+    EventTagMap* newTagMap;
+    off_t end;
+    int fd = -1;
+
+    newTagMap = calloc(1, sizeof(EventTagMap));
+    if (newTagMap == NULL)
+        return NULL;
+
+    fd = open(fileName, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "%s: unable to open map '%s': %s\n",
+            OUT_TAG, fileName, strerror(errno));
+        goto fail;
+    }
+
+    end = lseek(fd, 0L, SEEK_END);
+    (void) lseek(fd, 0L, SEEK_SET);
+    if (end < 0) {
+        fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName);
+        goto fail;
+    }
+
+    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+                                fd, 0);
+    if (newTagMap->mapAddr == MAP_FAILED) {
+        fprintf(stderr, "%s: mmap(%s) failed: %s\n",
+            OUT_TAG, fileName, strerror(errno));
+        goto fail;
+    }
+    newTagMap->mapLen = end;
+
+    if (processFile(newTagMap) != 0)
+        goto fail;
+
+    return newTagMap;
+
+fail:
+    android_closeEventTagMap(newTagMap);
+    if (fd >= 0)
+        close(fd);
+    return NULL;
+}
+
+/*
+ * Close the map.
+ */
+void android_closeEventTagMap(EventTagMap* map)
+{
+    if (map == NULL)
+        return;
+
+    munmap(map->mapAddr, map->mapLen);
+    free(map);
+}
+
+/*
+ * Look up an entry in the map.
+ *
+ * The entries are sorted by tag number, so we can do a binary search.
+ */
+const char* android_lookupEventTag(const EventTagMap* map, int tag)
+{
+    int hi, lo, mid;
+
+    lo = 0;
+    hi = map->numTags-1;
+
+    while (lo <= hi) {
+        int cmp;
+
+        mid = (lo+hi)/2;
+        cmp = map->tagArray[mid].tagIndex - tag;
+        if (cmp < 0) {
+            /* tag is bigger */
+            lo = mid + 1;
+        } else if (cmp > 0) {
+            /* tag is smaller */
+            hi = mid - 1;
+        } else {
+            /* found */
+            return map->tagArray[mid].tagStr;
+        }
+    }
+
+    return NULL;
+}
+
+
+
+/*
+ * Determine whether "c" is a whitespace char.
+ */
+static inline int isCharWhitespace(char c)
+{
+    return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
+}
+
+/*
+ * Determine whether "c" is a valid tag char.
+ */
+static inline int isCharValidTag(char c)
+{
+    return ((c >= 'A' && c <= 'Z') ||
+            (c >= 'a' && c <= 'z') ||
+            (c >= '0' && c <= '9') ||
+            (c == '_'));
+}
+
+/*
+ * Determine whether "c" is a valid decimal digit.
+ */
+static inline int isCharDigit(char c)
+{
+    return (c >= '0' && c <= '9');
+}
+
+
+/*
+ * Crunch through the file, parsing the contents and creating a tag index.
+ */
+static int processFile(EventTagMap* map)
+{
+    EventTag* tagArray = NULL;
+
+    /* get a tag count */
+    map->numTags = countMapLines(map);
+    if (map->numTags < 0)
+        return -1;
+
+    //printf("+++ found %d tags\n", map->numTags);
+
+    /* allocate storage for the tag index array */
+    map->tagArray = calloc(1, sizeof(EventTag) * map->numTags);
+    if (map->tagArray == NULL)
+        return -1;
+
+    /* parse the file, null-terminating tag strings */
+    if (parseMapLines(map) != 0) {
+        fprintf(stderr, "%s: file parse failed\n", OUT_TAG);
+        return -1;
+    }
+
+    /* sort the tags and check for duplicates */
+    if (sortTags(map) != 0)
+        return -1;
+
+    return 0;
+}
+
+/*
+ * Run through all lines in the file, determining whether they're blank,
+ * comments, or possibly have a tag entry.
+ *
+ * This is a very "loose" scan.  We don't try to detect syntax errors here.
+ * The later pass is more careful, but the number of tags found there must
+ * match the number of tags found here.
+ *
+ * Returns the number of potential tag entries found.
+ */
+static int countMapLines(const EventTagMap* map)
+{
+    int numTags, unknown;
+    const char* cp;
+    const char* endp;
+
+    cp = (const char*) map->mapAddr;
+    endp = cp + map->mapLen;
+
+    numTags = 0;
+    unknown = 1;
+    while (cp < endp) {
+        if (*cp == '\n') {
+            unknown = 1;
+        } else if (unknown) {
+            if (isCharDigit(*cp)) {
+                /* looks like a tag to me */
+                numTags++;
+                unknown = 0;
+            } else if (isCharWhitespace(*cp)) {
+                /* might be leading whitespace before tag num, keep going */
+            } else {
+                /* assume comment; second pass can complain in detail */
+                unknown = 0;
+            }
+        } else {
+            /* we've made up our mind; just scan to end of line */
+        }
+        cp++;
+    }
+
+    return numTags;
+}
+
+/*
+ * Parse the tags out of the file.
+ */
+static int parseMapLines(EventTagMap* map)
+{
+    int tagNum, lineStart, lineNum;
+    char* cp;
+    char* endp;
+
+    cp = (char*) map->mapAddr;
+    endp = cp + map->mapLen;
+
+    /* insist on EOL at EOF; simplifies parsing and null-termination */
+    if (*(endp-1) != '\n') {
+        fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
+        return -1;
+    }
+
+    tagNum = 0;
+    lineStart = 1;
+    lineNum = 1;
+    while (cp < endp) {
+        //printf("{%02x}", *cp); fflush(stdout);
+        if (*cp == '\n') {
+            lineStart = 1;
+            lineNum++;
+        } else if (lineStart) {
+            if (*cp == '#') {
+                /* comment; just scan to end */
+                lineStart = 0;
+            } else if (isCharDigit(*cp)) {
+                /* looks like a tag; scan it out */
+                if (tagNum >= map->numTags) {
+                    fprintf(stderr,
+                        "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
+                    return -1;
+                }
+                if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0)
+                    return -1;
+                tagNum++;
+                lineNum++;      // we eat the '\n'
+                /* leave lineStart==1 */
+            } else if (isCharWhitespace(*cp)) {
+                /* looks like leading whitespace; keep scanning */
+            } else {
+                fprintf(stderr,
+                    "%s: unexpected chars (0x%02x) in tag number on line %d\n",
+                    OUT_TAG, *cp, lineNum);
+                return -1;
+            }
+        } else {
+            /* this is a blank or comment line */
+        }
+        cp++;
+    }
+
+    if (tagNum != map->numTags) {
+        fprintf(stderr, "%s: parsed %d tags, expected %d\n",
+            OUT_TAG, tagNum, map->numTags);
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Scan one tag line.
+ *
+ * "*pData" should be pointing to the first digit in the tag number.  On
+ * successful return, it will be pointing to the last character in the
+ * tag line (i.e. the character before the start of the next line).
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+static int scanTagLine(char** pData, EventTag* tag, int lineNum)
+{
+    char* cp = *pData;
+    char* startp;
+    char* endp;
+    unsigned long val;
+
+    startp = cp;
+    while (isCharDigit(*++cp))
+        ;
+    *cp = '\0';
+
+    val = strtoul(startp, &endp, 10);
+    assert(endp == cp);
+    if (endp != cp)
+        fprintf(stderr, "ARRRRGH\n");
+
+    tag->tagIndex = val;
+
+    while (*++cp != '\n' && isCharWhitespace(*cp))
+        ;
+
+    if (*cp == '\n') {
+        fprintf(stderr,
+            "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+        return -1;
+    }
+
+    tag->tagStr = cp;
+
+    while (isCharValidTag(*++cp))
+        ;
+
+    if (*cp == '\n') {
+        /* null terminate and return */
+        *cp = '\0';
+    } else if (isCharWhitespace(*cp)) {
+        /* CRLF or trailin spaces; zap this char, then scan for the '\n' */
+        *cp = '\0';
+
+        /* just ignore the rest of the line till \n
+        TODO: read the tag description that follows the tag name
+        */
+        while (*++cp != '\n') {
+        }
+    } else {
+        fprintf(stderr,
+            "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+        return -1;
+    }
+
+    *pData = cp;
+
+    //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr);
+    return 0;
+}
+
+/*
+ * Compare two EventTags.
+ */
+static int compareEventTags(const void* v1, const void* v2)
+{
+    const EventTag* tag1 = (const EventTag*) v1;
+    const EventTag* tag2 = (const EventTag*) v2;
+
+    return tag1->tagIndex - tag2->tagIndex;
+}
+
+/*
+ * Sort the EventTag array so we can do fast lookups by tag index.  After
+ * the sort we do a quick check for duplicate tag indices.
+ *
+ * Returns 0 on success.
+ */
+static int sortTags(EventTagMap* map)
+{
+    int i;
+
+    qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
+
+    for (i = 1; i < map->numTags; i++) {
+        if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) {
+            fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n",
+                OUT_TAG,
+                map->tagArray[i].tagIndex, map->tagArray[i].tagStr,
+                map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Dump the tag array for debugging.
+ */
+static void dumpTags(const EventTagMap* map)
+{
+    int i;
+
+    for (i = 0; i < map->numTags; i++) {
+        const EventTag* tag = &map->tagArray[i];
+        printf("  %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr);
+    }
+}
+
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
new file mode 100644
index 0000000..ed9d699
--- /dev/null
+++ b/liblog/fake_log_device.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Intercepts log messages intended for the Android log device.
+ * When running in the context of the simulator, the messages are
+ * passed on to the underlying (fake) log device.  When not in the
+ * simulator, messages are printed to stderr.
+ */
+#include "cutils/logd.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+
+#define kMaxTagLen  16      /* from the long-dead utils/Log.cpp */
+
+#define kTagSetSize 16      /* arbitrary */
+
+#if 0
+#define TRACE(...) printf("fake_log_device: " __VA_ARGS__)
+#else
+#define TRACE(...) ((void)0)
+#endif
+
+/* from the long-dead utils/Log.cpp */
+typedef enum {
+    FORMAT_OFF = 0,
+    FORMAT_BRIEF,
+    FORMAT_PROCESS,
+    FORMAT_TAG,
+    FORMAT_THREAD,
+    FORMAT_RAW,
+    FORMAT_TIME,
+    FORMAT_THREADTIME,
+    FORMAT_LONG
+} LogFormat;
+
+
+/*
+ * Log driver state.
+ */
+typedef struct LogState {
+    /* the fake fd that's seen by the user */
+    int     fakeFd;
+
+    /* a printable name for this fake device */
+    char   *debugName;
+
+    /* nonzero if this is a binary log */
+    int     isBinary;
+
+    /* global minimum priority */
+    int     globalMinPriority;
+
+    /* output format */
+    LogFormat outputFormat;
+
+    /* tags and priorities */
+    struct {
+        char    tag[kMaxTagLen];
+        int     minPriority;
+    } tagSet[kTagSetSize];
+} LogState;
+
+
+#ifdef HAVE_PTHREADS
+/*
+ * Locking.  Since we're emulating a device, we need to be prepared
+ * to have multiple callers at the same time.  This lock is used
+ * to both protect the fd list and to prevent LogStates from being
+ * freed out from under a user.
+ */
+static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
+
+static void lock()
+{
+    pthread_mutex_lock(&fakeLogDeviceLock);
+}
+
+static void unlock()
+{
+    pthread_mutex_unlock(&fakeLogDeviceLock);
+}
+#else   // !HAVE_PTHREADS
+#define lock() ((void)0)
+#define unlock() ((void)0)
+#endif  // !HAVE_PTHREADS
+
+
+/*
+ * File descriptor management.
+ */
+#define FAKE_FD_BASE 10000
+#define MAX_OPEN_LOGS 16
+static LogState *openLogTable[MAX_OPEN_LOGS];
+
+/*
+ * Allocate an fd and associate a new LogState with it.
+ * The fd is available via the fakeFd field of the return value.
+ */
+static LogState *createLogState()
+{
+    size_t i;
+
+    for (i = 0; i < sizeof(openLogTable); i++) {
+        if (openLogTable[i] == NULL) {
+            openLogTable[i] = calloc(1, sizeof(LogState));
+            openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
+            return openLogTable[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Translate an fd to a LogState.
+ */
+static LogState *fdToLogState(int fd)
+{
+    if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
+        return openLogTable[fd - FAKE_FD_BASE];
+    }
+    return NULL;
+}
+
+/*
+ * Unregister the fake fd and free the memory it pointed to.
+ */
+static void deleteFakeFd(int fd)
+{
+    LogState *ls;
+
+    lock();
+
+    ls = fdToLogState(fd);
+    if (ls != NULL) {
+        openLogTable[fd - FAKE_FD_BASE] = NULL;
+        free(ls->debugName);
+        free(ls);
+    }
+
+    unlock();
+}
+
+/*
+ * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
+ * need to parse a string that looks like
+ *
+ *   *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
+ *
+ * The tag (or '*' for the global level) comes first, followed by a colon
+ * and a letter indicating the minimum priority level we're expected to log.
+ * This can be used to reveal or conceal logs with specific tags.
+ *
+ * We also want to check ANDROID_PRINTF_LOG to determine how the output
+ * will look.
+ */
+static void configureInitialState(const char* pathName, LogState* logState)
+{
+    static const int kDevLogLen = sizeof("/dev/log/") - 1;
+
+    logState->debugName = strdup(pathName);
+
+    /* identify binary logs */
+    if (strcmp(pathName + kDevLogLen, "events") == 0) {
+        logState->isBinary = 1;
+    }
+
+    /* global min priority defaults to "info" level */
+    logState->globalMinPriority = ANDROID_LOG_INFO;
+
+    /*
+     * This is based on the the long-dead utils/Log.cpp code.
+     */
+    const char* tags = getenv("ANDROID_LOG_TAGS");
+    TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags);
+    if (tags != NULL) {
+        int entry = 0;
+
+        while (*tags != '\0') {
+            char tagName[kMaxTagLen];
+            int i, minPrio;
+
+            while (isspace(*tags))
+                tags++;
+
+            i = 0;
+            while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
+                i < kMaxTagLen)
+            {
+                tagName[i++] = *tags++;
+            }
+            if (i == kMaxTagLen) {
+                TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
+                return;
+            }
+            tagName[i] = '\0';
+
+            /* default priority, if there's no ":" part; also zero out '*' */
+            minPrio = ANDROID_LOG_VERBOSE;
+            if (tagName[0] == '*' && tagName[1] == '\0') {
+                minPrio = ANDROID_LOG_DEBUG;
+                tagName[0] = '\0';
+            }
+
+            if (*tags == ':') {
+                tags++;
+                if (*tags >= '0' && *tags <= '9') {
+                    if (*tags >= ('0' + ANDROID_LOG_SILENT))
+                        minPrio = ANDROID_LOG_VERBOSE;
+                    else
+                        minPrio = *tags - '\0';
+                } else {
+                    switch (*tags) {
+                    case 'v':   minPrio = ANDROID_LOG_VERBOSE;  break;
+                    case 'd':   minPrio = ANDROID_LOG_DEBUG;    break;
+                    case 'i':   minPrio = ANDROID_LOG_INFO;     break;
+                    case 'w':   minPrio = ANDROID_LOG_WARN;     break;
+                    case 'e':   minPrio = ANDROID_LOG_ERROR;    break;
+                    case 'f':   minPrio = ANDROID_LOG_FATAL;    break;
+                    case 's':   minPrio = ANDROID_LOG_SILENT;   break;
+                    default:    minPrio = ANDROID_LOG_DEFAULT;  break;
+                    }
+                }
+
+                tags++;
+                if (*tags != '\0' && !isspace(*tags)) {
+                    TRACE("ERROR: garbage in tag env; expected whitespace\n");
+                    TRACE("       env='%s'\n", tags);
+                    return;
+                }
+            }
+
+            if (tagName[0] == 0) {
+                logState->globalMinPriority = minPrio;
+                TRACE("+++ global min prio %d\n", logState->globalMinPriority);
+            } else {
+                logState->tagSet[entry].minPriority = minPrio;
+                strcpy(logState->tagSet[entry].tag, tagName);
+                TRACE("+++ entry %d: %s:%d\n",
+                    entry,
+                    logState->tagSet[entry].tag,
+                    logState->tagSet[entry].minPriority);
+                entry++;
+            }
+        }
+    }
+
+
+    /*
+     * Taken from the long-dead utils/Log.cpp
+     */
+    const char* fstr = getenv("ANDROID_PRINTF_LOG");
+    LogFormat format;
+    if (fstr == NULL) {
+        format = FORMAT_BRIEF;
+    } else {
+        if (strcmp(fstr, "brief") == 0)
+            format = FORMAT_BRIEF;
+        else if (strcmp(fstr, "process") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "tag") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "thread") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "raw") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "time") == 0)
+            format = FORMAT_PROCESS;
+        else if (strcmp(fstr, "long") == 0)
+            format = FORMAT_PROCESS;
+        else
+            format = (LogFormat) atoi(fstr);        // really?!
+    }
+
+    logState->outputFormat = format;
+}
+
+/*
+ * Return a human-readable string for the priority level.  Always returns
+ * a valid string.
+ */
+static const char* getPriorityString(int priority)
+{
+    /* the first character of each string should be unique */
+    static const char* priorityStrings[] = {
+        "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
+    };
+    int idx;
+
+    idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+    if (idx < 0 ||
+        idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+        return "?unknown?";
+    return priorityStrings[idx];
+}
+
+#ifndef HAVE_WRITEV
+/*
+ * Some platforms like WIN32 do not have writev().
+ * Make up something to replace it.
+ */
+static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
+    int result = 0;
+    struct iovec* end = iov + iovcnt;
+    for (; iov < end; iov++) {
+        int w = write(fd, iov->iov_base, iov->iov_len);
+        if (w != iov->iov_len) {
+            if (w < 0)
+                return w;
+            return result + w;
+        }
+        result += w;
+    }
+    return result;
+}
+
+#define writev fake_writev
+#endif
+
+
+/*
+ * Write a filtered log message to stderr.
+ *
+ * Log format parsing taken from the long-dead utils/Log.cpp.
+ */
+static void showLog(LogState *state,
+        int logPrio, const char* tag, const char* msg)
+{
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    char timeBuf[32];
+    char prefixBuf[128], suffixBuf[128];
+    char priChar;
+    time_t when;
+    pid_t pid, tid;
+
+    TRACE("LOG %d: %s %s", logPrio, tag, msg);
+
+    priChar = getPriorityString(logPrio)[0];
+    when = time(NULL);
+    pid = tid = getpid();       // find gettid()?
+
+    /*
+     * Get the current date/time in pretty form
+     *
+     * It's often useful when examining a log with "less" to jump to
+     * a specific point in the file by searching for the date/time stamp.
+     * For this reason it's very annoying to have regexp meta characters
+     * in the time stamp.  Don't use forward slashes, parenthesis,
+     * brackets, asterisks, or other special chars here.
+     */
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&when, &tmBuf);
+#else
+    ptm = localtime(&when);
+#endif
+    //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+    /*
+     * Construct a buffer containing the log header and log message.
+     */
+    size_t prefixLen, suffixLen;
+
+    switch (state->outputFormat) {
+    case FORMAT_TAG:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c/%-8s: ", priChar, tag);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_PROCESS:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c(%5d) ", priChar, pid);
+        suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+            "  (%s)\n", tag);
+        break;
+    case FORMAT_THREAD:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c(%5d:%p) ", priChar, pid, (void*)tid);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_RAW:
+        prefixBuf[0] = 0; prefixLen = 0;
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_TIME:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%s %-8s\n\t", timeBuf, tag);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_THREADTIME:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+    case FORMAT_LONG:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "[ %s %5d:%p %c/%-8s ]\n",
+            timeBuf, pid, (void*)tid, priChar, tag);
+        strcpy(suffixBuf, "\n\n"); suffixLen = 2;
+        break;
+    default:
+        prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+            "%c/%-8s(%5d): ", priChar, tag, pid);
+        strcpy(suffixBuf, "\n"); suffixLen = 1;
+        break;
+     }
+
+    /*
+     * Figure out how many lines there will be.
+     */
+    const char* end = msg + strlen(msg);
+    size_t numLines = 0;
+    const char* p = msg;
+    while (p < end) {
+        if (*p++ == '\n') numLines++;
+    }
+    if (p > msg && *(p-1) != '\n') numLines++;
+
+    /*
+     * Create an array of iovecs large enough to write all of
+     * the lines with a prefix and a suffix.
+     */
+    const size_t INLINE_VECS = 6;
+    const size_t MAX_LINES   = ((size_t)~0)/(3*sizeof(struct iovec*));
+    struct iovec stackVec[INLINE_VECS];
+    struct iovec* vec = stackVec;
+    size_t numVecs;
+
+    if (numLines > MAX_LINES)
+        numLines = MAX_LINES;
+
+    numVecs = numLines*3;  // 3 iovecs per line.
+    if (numVecs > INLINE_VECS) {
+        vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines);
+        if (vec == NULL) {
+            msg = "LOG: write failed, no memory";
+            numVecs = 3;
+            numLines = 1;
+            vec = stackVec;
+        }
+    }
+
+    /*
+     * Fill in the iovec pointers.
+     */
+    p = msg;
+    struct iovec* v = vec;
+    int totalLen = 0;
+    while (numLines > 0 && p < end) {
+        if (prefixLen > 0) {
+            v->iov_base = prefixBuf;
+            v->iov_len = prefixLen;
+            totalLen += prefixLen;
+            v++;
+        }
+        const char* start = p;
+        while (p < end && *p != '\n') p++;
+        if ((p-start) > 0) {
+            v->iov_base = (void*)start;
+            v->iov_len = p-start;
+            totalLen += p-start;
+            v++;
+        }
+        if (*p == '\n') p++;
+        if (suffixLen > 0) {
+            v->iov_base = suffixBuf;
+            v->iov_len = suffixLen;
+            totalLen += suffixLen;
+            v++;
+        }
+        numLines -= 1;
+    }
+    
+    /*
+     * Write the entire message to the log file with a single writev() call.
+     * We need to use this rather than a collection of printf()s on a FILE*
+     * because of multi-threading and multi-process issues.
+     *
+     * If the file was not opened with O_APPEND, this will produce interleaved
+     * output when called on the same file from multiple processes.
+     *
+     * If the file descriptor is actually a network socket, the writev()
+     * call may return with a partial write.  Putting the writev() call in
+     * a loop can result in interleaved data.  This can be alleviated
+     * somewhat by wrapping the writev call in the Mutex.
+     */
+
+    for(;;) {
+        int cc = writev(fileno(stderr), vec, v-vec);
+
+        if (cc == totalLen) break;
+        
+        if (cc < 0) {
+            if(errno == EINTR) continue;
+            
+                /* can't really log the failure; for now, throw out a stderr */
+            fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+            break;
+        } else {
+                /* shouldn't happen when writing to file or tty */
+            fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
+            break;
+        }
+    }
+
+    /* if we allocated storage for the iovecs, free it */
+    if (vec != stackVec)
+        free(vec);
+}
+
+
+/*
+ * Receive a log message.  We happen to know that "vector" has three parts:
+ *
+ *  priority (1 byte)
+ *  tag (N bytes -- null-terminated ASCII string)
+ *  message (N bytes -- null-terminated ASCII string)
+ */
+static ssize_t logWritev(int fd, const struct iovec* vector, int count)
+{
+    LogState* state;
+
+    /* Make sure that no-one frees the LogState while we're using it.
+     * Also guarantees that only one thread is in showLog() at a given
+     * time (if it matters).
+     */
+    lock();
+
+    state = fdToLogState(fd);
+    if (state == NULL) {
+        errno = EBADF;
+        goto error;
+    }
+
+    if (state->isBinary) {
+        TRACE("%s: ignoring binary log\n", state->debugName);
+        goto bail;
+    }
+
+    if (count != 3) {
+        TRACE("%s: writevLog with count=%d not expected\n",
+            state->debugName, count);
+        goto error;
+    }
+
+    /* pull out the three fields */
+    int logPrio = *(const char*)vector[0].iov_base;
+    const char* tag = (const char*) vector[1].iov_base;
+    const char* msg = (const char*) vector[2].iov_base;
+
+    /* see if this log tag is configured */
+    int i;
+    int minPrio = state->globalMinPriority;
+    for (i = 0; i < kTagSetSize; i++) {
+        if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
+            break;      /* reached end of configured values */
+
+        if (strcmp(state->tagSet[i].tag, tag) == 0) {
+            //TRACE("MATCH tag '%s'\n", tag);
+            minPrio = state->tagSet[i].minPriority;
+            break;
+        }
+    }
+
+    if (logPrio >= minPrio) {
+        showLog(state, logPrio, tag, msg);
+    } else {
+        //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
+    }
+
+bail:
+    unlock();
+    return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
+error:
+    unlock();
+    return -1;
+}
+
+/*
+ * Free up our state and close the fake descriptor.
+ */
+static int logClose(int fd)
+{
+    deleteFakeFd(fd);
+    return 0;
+}
+
+/*
+ * Open a log output device and return a fake fd.
+ */
+static int logOpen(const char* pathName, int flags)
+{
+    LogState *logState;
+    int fd = -1;
+
+    lock();
+
+    logState = createLogState();
+    if (logState != NULL) {
+        configureInitialState(pathName, logState);
+        fd = logState->fakeFd;
+    } else  {
+        errno = ENFILE;
+    }
+
+    unlock();
+
+    return fd;
+}
+
+
+/*
+ * Runtime redirection.  If this binary is running in the simulator,
+ * just pass log messages to the emulated device.  If it's running
+ * outside of the simulator, write the log messages to stderr.
+ */
+
+static int (*redirectOpen)(const char *pathName, int flags) = NULL;
+static int (*redirectClose)(int fd) = NULL;
+static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count)
+        = NULL;
+
+static void setRedirects()
+{
+    const char *ws;
+
+    /* Wrapsim sets this environment variable on children that it's
+     * created using its LD_PRELOAD wrapper.
+     */
+    ws = getenv("ANDROID_WRAPSIM");
+    if (ws != NULL && strcmp(ws, "1") == 0) {
+        /* We're running inside wrapsim, so we can just write to the device. */
+        redirectOpen = (int (*)(const char *pathName, int flags))open;
+        redirectClose = close;
+        redirectWritev = writev;
+    } else {
+        /* There's no device to delegate to; handle the logging ourselves. */
+        redirectOpen = logOpen;
+        redirectClose = logClose;
+        redirectWritev = logWritev;
+    }
+}
+
+int fakeLogOpen(const char *pathName, int flags)
+{
+    if (redirectOpen == NULL) {
+        setRedirects();
+    }
+    return redirectOpen(pathName, flags);
+}
+
+int fakeLogClose(int fd)
+{
+    /* Assume that open() was called first. */
+    return redirectClose(fd);
+}
+
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+{
+    /* Assume that open() was called first. */
+    return redirectWritev(fd, vector, count);
+}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
new file mode 100644
index 0000000..80867d1
--- /dev/null
+++ b/liblog/logd_write.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <time.h>
+#include <stdio.h>
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#endif
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <cutils/logger.h>
+#include <cutils/logd.h>
+
+#define LOG_BUF_SIZE	1024
+
+#if FAKE_LOG_DEVICE
+// This will be defined when building for the host.
+#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
+#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
+#define log_close(filedes) fakeLogClose(filedes)
+#else
+#define log_open(pathname, flags) open(pathname, flags)
+#define log_writev(filedes, vector, count) writev(filedes, vector, count)
+#define log_close(filedes) close(filedes)
+#endif
+
+typedef enum {
+    LOG_ID_MAIN = 0,
+    LOG_ID_RADIO,
+    LOG_ID_EVENTS,
+    LOG_ID_MAX
+} log_id_t;
+
+static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
+static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) =
+    __write_to_log_init;
+#ifdef HAVE_PTHREADS
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 };
+
+/*
+ * This is used by the C++ code to decide if it should write logs through
+ * the C code.  Basically, if /dev/log/... is available, we're running in
+ * the simulator rather than a desktop tool and want to use the device.
+ */
+static enum {
+    kLogUninitialized, kLogNotAvailable, kLogAvailable 
+} g_log_status = kLogUninitialized;
+int __android_log_dev_available(void)
+{
+    if (g_log_status == kLogUninitialized) {
+        if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0)
+            g_log_status = kLogAvailable;
+        else
+            g_log_status = kLogNotAvailable;
+    }
+
+    return (g_log_status == kLogAvailable);
+}
+
+static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
+{
+    return -1;
+}
+
+static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+    ssize_t ret;
+    int log_fd;
+
+    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+        log_fd = log_fds[(int)log_id];
+    } else {
+        return EBADF;
+    }
+
+    do {
+        ret = log_writev(log_fd, vec, nr);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
+{
+#ifdef HAVE_PTHREADS
+    pthread_mutex_lock(&log_init_lock);
+#endif
+
+    if (write_to_log == __write_to_log_init) {
+        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
+        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
+        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
+
+        write_to_log = __write_to_log_kernel;
+
+        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
+                log_fds[LOG_ID_EVENTS] < 0) {
+            log_close(log_fds[LOG_ID_MAIN]);
+            log_close(log_fds[LOG_ID_RADIO]);
+            log_close(log_fds[LOG_ID_EVENTS]);
+            log_fds[LOG_ID_MAIN] = -1;
+            log_fds[LOG_ID_RADIO] = -1;
+            log_fds[LOG_ID_EVENTS] = -1;
+            write_to_log = __write_to_log_null;
+        }
+    }
+
+#ifdef HAVE_PTHREADS
+    pthread_mutex_unlock(&log_init_lock);
+#endif
+
+    return write_to_log(log_id, vec, nr);
+}
+
+int __android_log_write(int prio, const char *tag, const char *msg)
+{
+    struct iovec vec[3];
+    log_id_t log_id = LOG_ID_MAIN;
+
+    if (!tag)
+        tag = "";
+
+    /* XXX: This needs to go! */
+    if (!strcmp(tag, "HTC_RIL") ||
+        !strcmp(tag, "RILJ") ||
+        !strcmp(tag, "RILC") ||
+        !strcmp(tag, "RILD") ||
+        !strcmp(tag, "RIL") ||
+        !strcmp(tag, "AT") ||
+        !strcmp(tag, "GSM") ||
+        !strcmp(tag, "STK"))
+            log_id = LOG_ID_RADIO;
+
+    vec[0].iov_base   = (unsigned char *) &prio;
+    vec[0].iov_len    = 1;
+    vec[1].iov_base   = (void *) tag;
+    vec[1].iov_len    = strlen(tag) + 1;
+    vec[2].iov_base   = (void *) msg;
+    vec[2].iov_len    = strlen(msg) + 1;
+
+    return write_to_log(log_id, vec, 3);
+}
+
+int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+{
+    char buf[LOG_BUF_SIZE];    
+
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];    
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    return __android_log_write(prio, tag, buf);
+}
+
+void __android_log_assert(const char *cond, const char *tag,
+			  const char *fmt, ...)
+{
+    va_list ap;
+    char buf[LOG_BUF_SIZE];    
+
+    va_start(ap, fmt);
+    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+    va_end(ap);
+
+    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+
+    __builtin_trap(); /* trap so we have a chance to debug the situation */
+}
+
+int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+{
+    struct iovec vec[2];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = (void*)payload;
+    vec[1].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 2);
+}
+
+/*
+ * Like __android_log_bwrite, but takes the type as well.  Doesn't work
+ * for the general case where we're generating lists of stuff, but very
+ * handy if we just want to dump an integer into the log.
+ */
+int __android_log_btwrite(int32_t tag, char type, const void *payload,
+    size_t len)
+{
+    struct iovec vec[3];
+
+    vec[0].iov_base = &tag;
+    vec[0].iov_len = sizeof(tag);
+    vec[1].iov_base = &type;
+    vec[1].iov_len = sizeof(type);
+    vec[2].iov_base = (void*)payload;
+    vec[2].iov_len = len;
+
+    return write_to_log(LOG_ID_EVENTS, vec, 3);
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
new file mode 100644
index 0000000..2cf1254
--- /dev/null
+++ b/liblog/logprint.c
@@ -0,0 +1,972 @@
+/* //device/libs/cutils/logprint.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <alloca.h>
+#include <assert.h>
+#include <arpa/inet.h>
+
+#include <cutils/logd.h>
+#include <cutils/logprint.h>
+
+typedef struct FilterInfo_t {
+    char *mTag;
+    android_LogPriority mPri;
+    struct FilterInfo_t *p_next;
+} FilterInfo;
+
+struct AndroidLogFormat_t {
+    android_LogPriority global_pri;
+    FilterInfo *filters;
+    AndroidLogPrintFormat format;
+};
+
+static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri)
+{
+    FilterInfo *p_ret;
+
+    p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo));
+    p_ret->mTag = strdup(tag);
+    p_ret->mPri = pri;
+
+    return p_ret;
+}
+
+static void filterinfo_free(FilterInfo *p_info)
+{
+    if (p_info == NULL) {
+        return;
+    }
+
+    free(p_info->mTag);
+    p_info->mTag = NULL;
+}
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+    android_LogPriority pri;
+
+    c = tolower(c);
+
+    if (c >= '0' && c <= '9') {
+        if (c >= ('0'+ANDROID_LOG_SILENT)) {
+            pri = ANDROID_LOG_VERBOSE;
+        } else {
+            pri = (android_LogPriority)(c - '0');
+        }
+    } else if (c == 'v') {
+        pri = ANDROID_LOG_VERBOSE;
+    } else if (c == 'd') {
+        pri = ANDROID_LOG_DEBUG;
+    } else if (c == 'i') {
+        pri = ANDROID_LOG_INFO;
+    } else if (c == 'w') {
+        pri = ANDROID_LOG_WARN;
+    } else if (c == 'e') {
+        pri = ANDROID_LOG_ERROR;
+    } else if (c == 'f') {
+        pri = ANDROID_LOG_FATAL;
+    } else if (c == 's') {
+        pri = ANDROID_LOG_SILENT;
+    } else if (c == '*') {
+        pri = ANDROID_LOG_DEFAULT;
+    } else {
+        pri = ANDROID_LOG_UNKNOWN;
+    }
+
+    return pri;
+}
+
+static char filterPriToChar (android_LogPriority pri)
+{
+    switch (pri) {
+        case ANDROID_LOG_VERBOSE:       return 'V';
+        case ANDROID_LOG_DEBUG:         return 'D';
+        case ANDROID_LOG_INFO:          return 'I';
+        case ANDROID_LOG_WARN:          return 'W';
+        case ANDROID_LOG_ERROR:         return 'E';
+        case ANDROID_LOG_FATAL:         return 'F';
+        case ANDROID_LOG_SILENT:        return 'S';
+
+        case ANDROID_LOG_DEFAULT:
+        case ANDROID_LOG_UNKNOWN:
+        default:                        return '?';
+    }
+}
+
+static android_LogPriority filterPriForTag(
+        AndroidLogFormat *p_format, const char *tag)
+{
+    FilterInfo *p_curFilter;
+
+    for (p_curFilter = p_format->filters
+            ; p_curFilter != NULL
+            ; p_curFilter = p_curFilter->p_next
+    ) {
+        if (0 == strcmp(tag, p_curFilter->mTag)) {
+            if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
+                return p_format->global_pri;
+            } else {
+                return p_curFilter->mPri;
+            }
+        }
+    }
+
+    return p_format->global_pri;
+}
+
+/** for debugging */
+static void dumpFilters(AndroidLogFormat *p_format)
+{
+    FilterInfo *p_fi;
+
+    for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) {
+        char cPri = filterPriToChar(p_fi->mPri);
+        if (p_fi->mPri == ANDROID_LOG_DEFAULT) {
+            cPri = filterPriToChar(p_format->global_pri);
+        }
+        fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri);
+    }
+
+    fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri));
+
+}
+
+/**
+ * returns 1 if this log line should be printed based on its priority
+ * and tag, and 0 if it should not
+ */
+int android_log_shouldPrintLine (
+        AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+{
+    return pri >= filterPriForTag(p_format, tag);
+}
+
+AndroidLogFormat *android_log_format_new()
+{
+    AndroidLogFormat *p_ret;
+
+    p_ret = calloc(1, sizeof(AndroidLogFormat));
+
+    p_ret->global_pri = ANDROID_LOG_VERBOSE;
+    p_ret->format = FORMAT_BRIEF;
+
+    return p_ret;
+}
+
+void android_log_format_free(AndroidLogFormat *p_format)
+{
+    FilterInfo *p_info, *p_info_old;
+
+    p_info = p_format->filters;
+
+    while (p_info != NULL) {
+        p_info_old = p_info;
+        p_info = p_info->p_next;
+
+        free(p_info_old);
+    }
+
+    free(p_format);
+}
+
+
+
+void android_log_setPrintFormat(AndroidLogFormat *p_format,
+        AndroidLogPrintFormat format)
+{
+    p_format->format=format;
+}
+
+/**
+ * Returns FORMAT_OFF on invalid string
+ */
+AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+{
+    static AndroidLogPrintFormat format;
+
+    if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
+    else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
+    else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
+    else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
+    else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
+    else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
+    else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
+    else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
+    else format = FORMAT_OFF;
+
+    return format;
+}
+
+/**
+ * filterExpression: a single filter expression
+ * eg "AT:d"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ */
+
+int android_log_addFilterRule(AndroidLogFormat *p_format,
+        const char *filterExpression)
+{
+    size_t i=0;
+    size_t tagNameLength;
+    android_LogPriority pri = ANDROID_LOG_DEFAULT;
+
+    tagNameLength = strcspn(filterExpression, ":");
+
+    if (tagNameLength == 0) {
+        goto error;
+    }
+
+    if(filterExpression[tagNameLength] == ':') {
+        pri = filterCharToPri(filterExpression[tagNameLength+1]);
+
+        if (pri == ANDROID_LOG_UNKNOWN) {
+            goto error;
+        }
+    }
+
+    if(0 == strncmp("*", filterExpression, tagNameLength)) {
+        // This filter expression refers to the global filter
+        // The default level for this is DEBUG if the priority
+        // is unspecified
+        if (pri == ANDROID_LOG_DEFAULT) {
+            pri = ANDROID_LOG_DEBUG;
+        }
+
+        p_format->global_pri = pri;
+    } else {
+        // for filter expressions that don't refer to the global
+        // filter, the default is verbose if the priority is unspecified
+        if (pri == ANDROID_LOG_DEFAULT) {
+            pri = ANDROID_LOG_VERBOSE;
+        }
+
+        char *tagName;
+
+// Presently HAVE_STRNDUP is never defined, so the second case is always taken
+// Darwin doesn't have strnup, everything else does
+#ifdef HAVE_STRNDUP
+        tagName = strndup(filterExpression, tagNameLength);
+#else
+        //a few extra bytes copied...
+        tagName = strdup(filterExpression);
+        tagName[tagNameLength] = '\0';
+#endif /*HAVE_STRNDUP*/
+
+        FilterInfo *p_fi = filterinfo_new(tagName, pri);
+        free(tagName);
+
+        p_fi->p_next = p_format->filters;
+        p_format->filters = p_fi;
+    }
+
+    return 0;
+error:
+    return -1;
+}
+
+
+/**
+ * filterString: a comma/whitespace-separated set of filter expressions
+ *
+ * eg "AT:d *:i"
+ *
+ * returns 0 on success and -1 on invalid expression
+ *
+ * Assumes single threaded execution
+ *
+ */
+
+int android_log_addFilterString(AndroidLogFormat *p_format,
+        const char *filterString)
+{
+    char *filterStringCopy = strdup (filterString);
+    char *p_cur = filterStringCopy;
+    char *p_ret;
+    int err;
+
+    // Yes, I'm using strsep
+    while (NULL != (p_ret = strsep(&p_cur, " \t,"))) {
+        // ignore whitespace-only entries
+        if(p_ret[0] != '\0') {
+            err = android_log_addFilterRule(p_format, p_ret);
+
+            if (err < 0) {
+                goto error;
+            }
+        }
+    }
+
+    free (filterStringCopy);
+    return 0;
+error:
+    free (filterStringCopy);
+    return -1;
+}
+
+static inline char * strip_end(char *str)
+{
+    char *end = str + strlen(str) - 1;
+
+    while (end >= str && isspace(*end))
+        *end-- = '\0';
+    return str;
+}
+
+/**
+ * Splits a wire-format buffer into an AndroidLogEntry
+ * entry allocated by caller. Pointers will point directly into buf
+ *
+ * Returns 0 on success and -1 on invalid wire format (entry will be
+ * in unspecified state)
+ */
+int android_log_processLogBuffer(struct logger_entry *buf,
+                                 AndroidLogEntry *entry)
+{
+    size_t tag_len;
+
+    entry->tv_sec = buf->sec;
+    entry->tv_nsec = buf->nsec;
+    entry->priority = buf->msg[0];
+    entry->pid = buf->pid;
+    entry->tid = buf->tid;
+    entry->tag = buf->msg + 1;
+    tag_len = strlen(entry->tag);
+    entry->messageLen = buf->len - tag_len - 3;
+    entry->message = entry->tag + tag_len + 1;
+
+    return 0;
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+    uint32_t low, high;
+
+    low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+    high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+    return ((long long) high << 32) | (long long) low;
+}
+
+
+/*
+ * Recursively convert binary log data to printable form.
+ *
+ * This needs to be recursive because you can have lists of lists.
+ *
+ * If we run out of room, we stop processing immediately.  It's important
+ * for us to check for space on every output element to avoid producing
+ * garbled output.
+ *
+ * Returns 0 on success, 1 on buffer full, -1 on failure.
+ */
+static int android_log_printBinaryEvent(const unsigned char** pEventData,
+    size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
+{
+    const unsigned char* eventData = *pEventData;
+    size_t eventDataLen = *pEventDataLen;
+    char* outBuf = *pOutBuf;
+    size_t outBufLen = *pOutBufLen;
+    unsigned char type;
+    size_t outCount;
+    int result = 0;
+
+    if (eventDataLen < 1)
+        return -1;
+    type = *eventData++;
+    eventDataLen--;
+
+    //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen);
+
+    switch (type) {
+    case EVENT_TYPE_INT:
+        /* 32-bit signed int */
+        {
+            int ival;
+
+            if (eventDataLen < 4)
+                return -1;
+            ival = get4LE(eventData);
+            eventData += 4;
+            eventDataLen -= 4;
+
+            outCount = snprintf(outBuf, outBufLen, "%d", ival);
+            if (outCount < outBufLen) {
+                outBuf += outCount;
+                outBufLen -= outCount;
+            } else {
+                /* halt output */
+                goto no_room;
+            }
+        }
+        break;
+    case EVENT_TYPE_LONG:
+        /* 64-bit signed long */
+        {
+            long long lval;
+
+            if (eventDataLen < 8)
+                return -1;
+            lval = get8LE(eventData);
+            eventData += 8;
+            eventDataLen -= 8;
+
+            outCount = snprintf(outBuf, outBufLen, "%lld", lval);
+            if (outCount < outBufLen) {
+                outBuf += outCount;
+                outBufLen -= outCount;
+            } else {
+                /* halt output */
+                goto no_room;
+            }
+        }
+        break;
+    case EVENT_TYPE_STRING:
+        /* UTF-8 chars, not NULL-terminated */
+        {
+            unsigned int strLen;
+
+            if (eventDataLen < 4)
+                return -1;
+            strLen = get4LE(eventData);
+            eventData += 4;
+            eventDataLen -= 4;
+
+            if (eventDataLen < strLen)
+                return -1;
+
+            if (strLen < outBufLen) {
+                memcpy(outBuf, eventData, strLen);
+                outBuf += strLen;
+                outBufLen -= strLen;
+            } else if (outBufLen > 0) {
+                /* copy what we can */
+                memcpy(outBuf, eventData, outBufLen);
+                outBuf += outBufLen;
+                outBufLen -= outBufLen;
+                goto no_room;
+            }
+            eventData += strLen;
+            eventDataLen -= strLen;
+            break;
+        }
+    case EVENT_TYPE_LIST:
+        /* N items, all different types */
+        {
+            unsigned char count;
+            int i;
+
+            if (eventDataLen < 1)
+                return -1;
+
+            count = *eventData++;
+            eventDataLen--;
+
+            if (outBufLen > 0) {
+                *outBuf++ = '[';
+                outBufLen--;
+            } else {
+                goto no_room;
+            }
+
+            for (i = 0; i < count; i++) {
+                result = android_log_printBinaryEvent(&eventData, &eventDataLen,
+                        &outBuf, &outBufLen);
+                if (result != 0)
+                    goto bail;
+
+                if (i < count-1) {
+                    if (outBufLen > 0) {
+                        *outBuf++ = ',';
+                        outBufLen--;
+                    } else {
+                        goto no_room;
+                    }
+                }
+            }
+
+            if (outBufLen > 0) {
+                *outBuf++ = ']';
+                outBufLen--;
+            } else {
+                goto no_room;
+            }
+        }
+        break;
+    default:
+        fprintf(stderr, "Unknown binary event type %d\n", type);
+        return -1;
+    }
+
+bail:
+    *pEventData = eventData;
+    *pEventDataLen = eventDataLen;
+    *pOutBuf = outBuf;
+    *pOutBufLen = outBufLen;
+    return result;
+
+no_room:
+    result = 1;
+    goto bail;
+}
+
+/**
+ * Convert a binary log entry to ASCII form.
+ *
+ * For convenience we mimic the processLogBuffer API.  There is no
+ * pre-defined output length for the binary data, since we're free to format
+ * it however we choose, which means we can't really use a fixed-size buffer
+ * here.
+ */
+int android_log_processBinaryLogBuffer(struct logger_entry *buf,
+    AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
+    int messageBufLen)
+{
+    size_t inCount;
+    unsigned int tagIndex;
+    const unsigned char* eventData;
+
+    entry->tv_sec = buf->sec;
+    entry->tv_nsec = buf->nsec;
+    entry->priority = ANDROID_LOG_INFO;
+    entry->pid = buf->pid;
+    entry->tid = buf->tid;
+
+    /*
+     * Pull the tag out.
+     */
+    eventData = (const unsigned char*) buf->msg;
+    inCount = buf->len;
+    if (inCount < 4)
+        return -1;
+    tagIndex = get4LE(eventData);
+    eventData += 4;
+    inCount -= 4;
+
+    if (map != NULL) {
+        entry->tag = android_lookupEventTag(map, tagIndex);
+    } else {
+        entry->tag = NULL;
+    }
+
+    /*
+     * If we don't have a map, or didn't find the tag number in the map,
+     * stuff a generated tag value into the start of the output buffer and
+     * shift the buffer pointers down.
+     */
+    if (entry->tag == NULL) {
+        int tagLen;
+
+        tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
+        entry->tag = messageBuf;
+        messageBuf += tagLen+1;
+        messageBufLen -= tagLen+1;
+    }
+
+    /*
+     * Format the event log data into the buffer.
+     */
+    char* outBuf = messageBuf;
+    size_t outRemaining = messageBufLen-1;      /* leave one for nul byte */
+    int result;
+    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+                &outRemaining);
+    if (result < 0) {
+        fprintf(stderr, "Binary log entry conversion failed\n");
+        return -1;
+    } else if (result == 1) {
+        if (outBuf > messageBuf) {
+            /* leave an indicator */
+            *(outBuf-1) = '!';
+        } else {
+            /* no room to output anything at all */
+            *outBuf++ = '!';
+            outRemaining--;
+        }
+        /* pretend we ate all the data */
+        inCount = 0;
+    }
+
+    /* eat the silly terminating '\n' */
+    if (inCount == 1 && *eventData == '\n') {
+        eventData++;
+        inCount--;
+    }
+
+    if (inCount != 0) {
+        fprintf(stderr,
+            "Warning: leftover binary log data (%d bytes)\n", inCount);
+    }
+
+    /*
+     * Terminate the buffer.  The NUL byte does not count as part of
+     * entry->messageLen.
+     */
+    *outBuf = '\0';
+    entry->messageLen = outBuf - messageBuf;
+    assert(entry->messageLen == (messageBufLen-1) - outRemaining);
+
+    entry->message = messageBuf;
+
+    return 0;
+}
+
+/**
+ * Formats a log message into a buffer
+ *
+ * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
+ * If return value != defaultBuffer, caller must call free()
+ * Returns NULL on malloc error
+ */
+
+char *android_log_formatLogLine (
+    AndroidLogFormat *p_format,
+    char *defaultBuffer,
+    size_t defaultBufferSize,
+    const AndroidLogEntry *entry,
+    size_t *p_outLength)
+{
+#if defined(HAVE_LOCALTIME_R)
+    struct tm tmBuf;
+#endif
+    struct tm* ptm;
+    char timeBuf[32];
+    char headerBuf[128];
+    char prefixBuf[128], suffixBuf[128];
+    char priChar;
+    int prefixSuffixIsHeaderFooter = 0;
+    char * ret = NULL;
+
+    priChar = filterPriToChar(entry->priority);
+
+    /*
+     * Get the current date/time in pretty form
+     *
+     * It's often useful when examining a log with "less" to jump to
+     * a specific point in the file by searching for the date/time stamp.
+     * For this reason it's very annoying to have regexp meta characters
+     * in the time stamp.  Don't use forward slashes, parenthesis,
+     * brackets, asterisks, or other special chars here.
+     */
+#if defined(HAVE_LOCALTIME_R)
+    ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+#else
+    ptm = localtime(&(entry->tv_sec));
+#endif
+    //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
+    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+    /*
+     * Construct a buffer containing the log header and log message.
+     */
+    size_t prefixLen, suffixLen;
+
+    switch (p_format->format) {
+        case FORMAT_TAG:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%c/%-8s: ", priChar, entry->tag);
+            strcpy(suffixBuf, "\n"); suffixLen = 1;
+            break;
+        case FORMAT_PROCESS:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%c(%5d) ", priChar, entry->pid);
+            suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
+                "  (%s)\n", entry->tag);
+            break;
+        case FORMAT_THREAD:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid);
+            strcpy(suffixBuf, "\n");
+            suffixLen = 1;
+            break;
+        case FORMAT_RAW:
+            prefixBuf[0] = 0;
+            prefixLen = 0;
+            strcpy(suffixBuf, "\n");
+            suffixLen = 1;
+            break;
+        case FORMAT_TIME:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000,
+                priChar, entry->tag, entry->pid);
+            strcpy(suffixBuf, "\n");
+            suffixLen = 1;
+            break;
+        case FORMAT_THREADTIME:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000,
+                (int)entry->pid, (int)entry->tid, priChar, entry->tag);
+            strcpy(suffixBuf, "\n");
+            suffixLen = 1;
+            break;
+        case FORMAT_LONG:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "[ %s.%03ld %5d:%p %c/%-8s ]\n",
+                timeBuf, entry->tv_nsec / 1000000, entry->pid,
+                (void*)entry->tid, priChar, entry->tag);
+            strcpy(suffixBuf, "\n\n");
+            suffixLen = 2;
+            prefixSuffixIsHeaderFooter = 1;
+            break;
+        case FORMAT_BRIEF:
+        default:
+            prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
+                "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid);
+            strcpy(suffixBuf, "\n");
+            suffixLen = 1;
+            break;
+    }
+
+    /* the following code is tragically unreadable */
+
+    size_t numLines;
+    size_t i;
+    char *p;
+    size_t bufferSize;
+    const char *pm;
+
+    if (prefixSuffixIsHeaderFooter) {
+        // we're just wrapping message with a header/footer
+        numLines = 1;
+    } else {
+        pm = entry->message;
+        numLines = 0;
+
+        // The line-end finding here must match the line-end finding
+        // in for ( ... numLines...) loop below
+        while (pm < (entry->message + entry->messageLen)) {
+            if (*pm++ == '\n') numLines++;
+        }
+        // plus one line for anything not newline-terminated at the end
+        if (pm > entry->message && *(pm-1) != '\n') numLines++;
+    }
+
+    // this is an upper bound--newlines in message may be counted
+    // extraneously
+    bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1;
+
+    if (defaultBufferSize >= bufferSize) {
+        ret = defaultBuffer;
+    } else {
+        ret = (char *)malloc(bufferSize);
+
+        if (ret == NULL) {
+            return ret;
+        }
+    }
+
+    ret[0] = '\0';       /* to start strcat off */
+
+    p = ret;
+    pm = entry->message;
+
+    if (prefixSuffixIsHeaderFooter) {
+        strcat(p, prefixBuf);
+        p += prefixLen;
+        strncat(p, entry->message, entry->messageLen);
+        p += entry->messageLen;
+        strcat(p, suffixBuf);
+        p += suffixLen;
+    } else {
+        while(pm < (entry->message + entry->messageLen)) {
+            const char *lineStart;
+            size_t lineLen;
+
+            lineStart = pm;
+
+            // Find the next end-of-line in message
+            while (pm < (entry->message + entry->messageLen)
+                    && *pm != '\n') pm++;
+            lineLen = pm - lineStart;
+
+            strcat(p, prefixBuf);
+            p += prefixLen;
+            strncat(p, lineStart, lineLen);
+            p += lineLen;
+            strcat(p, suffixBuf);
+            p += suffixLen;
+
+            if (*pm == '\n') pm++;
+        }
+    }
+
+    if (p_outLength != NULL) {
+        *p_outLength = p - ret;
+    }
+
+    return ret;
+}
+
+/**
+ * Either print or do not print log line, based on filter
+ *
+ * Returns count bytes written
+ */
+
+int android_log_filterAndPrintLogLine(
+    AndroidLogFormat *p_format,
+    int fd,
+    const AndroidLogEntry *entry)
+{
+    int ret;
+    char defaultBuffer[512];
+    char *outBuffer = NULL;
+    size_t totalLen;
+
+    if (0 == android_log_shouldPrintLine(p_format, entry->tag,
+            entry->priority)) {
+        return 0;
+    }
+
+    outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
+            sizeof(defaultBuffer), entry, &totalLen);
+
+    if (!outBuffer)
+        return -1;
+
+    do {
+        ret = write(fd, outBuffer, totalLen);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
+        ret = 0;
+        goto done;
+    }
+
+    if (((size_t)ret) < totalLen) {
+        fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret,
+                (int)totalLen);
+        goto done;
+    }
+
+done:
+    if (outBuffer != defaultBuffer) {
+        free(outBuffer);
+    }
+
+    return ret;
+}
+
+
+
+void logprint_run_tests()
+{
+#if 0
+
+    fprintf(stderr, "tests disabled\n");
+
+#else
+
+    int err;
+    const char *tag;
+    AndroidLogFormat *p_format;
+
+    p_format = android_log_format_new();
+
+    fprintf(stderr, "running tests\n");
+
+    tag = "random";
+
+    android_log_addFilterRule(p_format,"*:i");
+
+    assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+    android_log_addFilterRule(p_format, "*");
+    assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+    android_log_addFilterRule(p_format, "*:v");
+    assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+    android_log_addFilterRule(p_format, "*:i");
+    assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+    android_log_addFilterRule(p_format, "random");
+    assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+    android_log_addFilterRule(p_format, "random:v");
+    assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+    android_log_addFilterRule(p_format, "random:d");
+    assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0);
+    android_log_addFilterRule(p_format, "random:w");
+    assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+    android_log_addFilterRule(p_format, "crap:*");
+    assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap"));
+    assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0);
+
+    // invalid expression
+    err = android_log_addFilterRule(p_format, "random:z");
+    assert (err < 0);
+    assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+    assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0);
+
+    // Issue #550946
+    err = android_log_addFilterString(p_format, " ");
+    assert(err == 0);
+    assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random"));
+
+    // note trailing space
+    err = android_log_addFilterString(p_format, "*:s random:d ");
+    assert(err == 0);
+    assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random"));
+
+    err = android_log_addFilterString(p_format, "*:s random:z");
+    assert(err < 0);
+
+
+#if 0
+    char *ret;
+    char defaultBuffer[512];
+
+    ret = android_log_formatLogLine(p_format,
+        defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123,
+        123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL);
+#endif
+
+
+    fprintf(stderr, "tests complete\n");
+#endif
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
new file mode 100644
index 0000000..b09a941
--- /dev/null
+++ b/libmincrypt/Android.mk
@@ -0,0 +1,18 @@
+# Copyright 2008 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmincrypt
+LOCAL_SRC_FILES := rsa.c sha.c
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmincrypt
+LOCAL_SRC_FILES := rsa.c sha.c
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# TODO: drop the hyphen once these are checked in
+include $(LOCAL_PATH)/tools/Android.mk
diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c
new file mode 100644
index 0000000..d7124fb
--- /dev/null
+++ b/libmincrypt/rsa.c
@@ -0,0 +1,198 @@
+/* rsa.c
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of Google Inc. nor the names of its contributors may
+**       be used to endorse or promote products derived from this software
+**       without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR 
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "mincrypt/rsa.h"
+#include "mincrypt/sha.h"
+
+/* a[] -= mod */
+static void subM(const RSAPublicKey *key, uint32_t *a) {
+    int64_t A = 0;
+    int i;
+    for (i = 0; i < key->len; ++i) {
+        A += (uint64_t)a[i] - key->n[i];
+        a[i] = (uint32_t)A;
+        A >>= 32;
+    }
+}
+
+/* return a[] >= mod */
+static int geM(const RSAPublicKey *key, const uint32_t *a) {
+    int i;
+    for (i = key->len; i;) {
+        --i;
+        if (a[i] < key->n[i]) return 0;
+        if (a[i] > key->n[i]) return 1;
+    }
+    return 1;  /* equal */
+}
+
+/* montgomery c[] += a * b[] / R % mod */
+static void montMulAdd(const RSAPublicKey *key,
+                       uint32_t* c,
+                       const uint32_t a,
+                       const uint32_t* b) {
+    uint64_t A = (uint64_t)a * b[0] + c[0];
+    uint32_t d0 = (uint32_t)A * key->n0inv;
+    uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A;
+    int i;
+
+    for (i = 1; i < key->len; ++i) {
+        A = (A >> 32) + (uint64_t)a * b[i] + c[i];
+        B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A;
+        c[i - 1] = (uint32_t)B;
+    }
+
+    A = (A >> 32) + (B >> 32);
+
+    c[i - 1] = (uint32_t)A;
+
+    if (A >> 32) {
+        subM(key, c);
+    }
+}
+
+/* montgomery c[] = a[] * b[] / R % mod */
+static void montMul(const RSAPublicKey *key,
+                    uint32_t* c,
+                    const uint32_t* a,
+                    const uint32_t* b) {
+    int i;
+    for (i = 0; i < key->len; ++i) {
+        c[i] = 0;
+    }
+    for (i = 0; i < key->len; ++i) {
+        montMulAdd(key, c, a[i], b);
+    }
+}
+
+/* In-place public exponentiation.
+** Input and output big-endian byte array in inout.
+*/
+static void modpow3(const RSAPublicKey *key,
+                    uint8_t* inout) {
+    uint32_t a[RSANUMWORDS];
+    uint32_t aR[RSANUMWORDS];
+    uint32_t aaR[RSANUMWORDS];
+    uint32_t *aaa = aR;  /* Re-use location. */
+    int i;
+
+    /* Convert from big endian byte array to little endian word array. */
+    for (i = 0; i < key->len; ++i) {
+        uint32_t tmp =
+            (inout[((key->len - 1 - i) * 4) + 0] << 24) |
+            (inout[((key->len - 1 - i) * 4) + 1] << 16) |
+            (inout[((key->len - 1 - i) * 4) + 2] << 8) |
+            (inout[((key->len - 1 - i) * 4) + 3] << 0);
+        a[i] = tmp;
+    }
+
+    montMul(key, aR, a, key->rr);  /* aR = a * RR / R mod M   */
+    montMul(key, aaR, aR, aR);     /* aaR = aR * aR / R mod M */
+    montMul(key, aaa, aaR, a);     /* aaa = aaR * a / R mod M */
+
+    /* Make sure aaa < mod; aaa is at most 1x mod too large. */
+    if (geM(key, aaa)) {
+        subM(key, aaa);
+    }
+
+    /* Convert to bigendian byte array */
+    for (i = key->len - 1; i >= 0; --i) {
+        uint32_t tmp = aaa[i];
+        *inout++ = tmp >> 24;
+        *inout++ = tmp >> 16;
+        *inout++ = tmp >> 8;
+        *inout++ = tmp >> 0;
+    }
+}
+
+/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature.
+** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the
+** other flavor which omits the optional parameter entirely). This code does not
+** accept signatures without the optional parameter.
+*/
+static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = {
+    0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,
+    0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,
+    0x04,0x14
+};
+
+/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash.
+** Returns 0 on failure, 1 on success.
+*/
+int RSA_verify(const RSAPublicKey *key,
+               const uint8_t *signature,
+               const int len,
+               const uint8_t *sha) {
+    uint8_t buf[RSANUMBYTES];
+    int i;
+
+    if (key->len != RSANUMWORDS) {
+        return 0;  /* Wrong key passed in. */
+    }
+
+    if (len != sizeof(buf)) {
+        return 0;  /* Wrong input length. */
+    }
+
+    for (i = 0; i < len; ++i) {
+        buf[i] = signature[i];
+    }
+
+    modpow3(key, buf);
+
+    /* Check pkcs1.5 padding bytes. */
+    for (i = 0; i < (int) sizeof(padding); ++i) {
+        if (buf[i] != padding[i]) {
+            return 0;
+        }
+    }
+
+    /* Check sha digest matches. */
+    for (; i < len; ++i) {
+        if (buf[i] != *sha++) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c
new file mode 100644
index 0000000..d6120da
--- /dev/null
+++ b/libmincrypt/sha.c
@@ -0,0 +1,142 @@
+/* sha.c
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+**     * Redistributions of source code must retain the above copyright
+**       notice, this list of conditions and the following disclaimer.
+**     * Redistributions in binary form must reproduce the above copyright
+**       notice, this list of conditions and the following disclaimer in the
+**       documentation and/or other materials provided with the distribution.
+**     * Neither the name of Google Inc. nor the names of its contributors may
+**       be used to endorse or promote products derived from this software
+**       without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR 
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "mincrypt/sha.h"
+
+#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static void SHA1_transform(SHA_CTX *ctx) {
+    uint32_t W[80];
+    uint32_t A, B, C, D, E;
+    uint8_t *p = ctx->buf;
+    int t;
+
+    for(t = 0; t < 16; ++t) {
+        uint32_t tmp =  *p++ << 24;
+        tmp |= *p++ << 16;
+        tmp |= *p++ << 8;
+        tmp |= *p++;
+        W[t] = tmp;
+    }
+
+    for(; t < 80; t++) {
+        W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = ctx->state[0];
+    B = ctx->state[1];
+    C = ctx->state[2];
+    D = ctx->state[3];
+    E = ctx->state[4];
+
+    for(t = 0; t < 80; t++) {
+        uint32_t tmp = rol(5,A) + E + W[t];
+
+        if (t < 20)
+            tmp += (D^(B&(C^D))) + 0x5A827999;
+        else if ( t < 40)
+            tmp += (B^C^D) + 0x6ED9EBA1;
+        else if ( t < 60)
+            tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
+        else
+            tmp += (B^C^D) + 0xCA62C1D6;
+
+        E = D;
+        D = C;
+        C = rol(30,B);
+        B = A;
+        A = tmp;
+    }
+
+    ctx->state[0] += A;
+    ctx->state[1] += B;
+    ctx->state[2] += C;
+    ctx->state[3] += D;
+    ctx->state[4] += E;
+}
+
+void SHA_init(SHA_CTX *ctx) {
+    ctx->state[0] = 0x67452301;
+    ctx->state[1] = 0xEFCDAB89;
+    ctx->state[2] = 0x98BADCFE;
+    ctx->state[3] = 0x10325476;
+    ctx->state[4] = 0xC3D2E1F0;
+    ctx->count = 0;
+}
+
+void SHA_update(SHA_CTX *ctx, const void *data, int len) {
+    int i = ctx->count % sizeof(ctx->buf);
+    const uint8_t* p = (const uint8_t*)data;
+
+    ctx->count += len;
+
+    while (len--) {
+        ctx->buf[i++] = *p++;
+        if (i == sizeof(ctx->buf)) {
+            SHA1_transform(ctx);
+            i = 0;
+        }
+    }
+}
+const uint8_t *SHA_final(SHA_CTX *ctx) {
+    uint8_t *p = ctx->buf;
+    uint64_t cnt = ctx->count * 8;
+    int i;
+
+    SHA_update(ctx, (uint8_t*)"\x80", 1);
+    while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) {
+        SHA_update(ctx, (uint8_t*)"\0", 1);
+    }
+    for (i = 0; i < 8; ++i) {
+        uint8_t tmp = cnt >> ((7 - i) * 8);
+        SHA_update(ctx, &tmp, 1);
+    }
+
+    for (i = 0; i < 5; i++) {
+        uint32_t tmp = ctx->state[i];
+        *p++ = tmp >> 24;
+        *p++ = tmp >> 16;
+        *p++ = tmp >> 8;
+        *p++ = tmp >> 0;
+    }
+
+    return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA(const void *data, int len, uint8_t *digest) {
+    const uint8_t *p;
+    int i;
+    SHA_CTX ctx;
+    SHA_init(&ctx);
+    SHA_update(&ctx, data, len);
+    p = SHA_final(&ctx);
+    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+        digest[i] = *p++;
+    }
+    return digest;
+}
diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk
new file mode 100644
index 0000000..b61234a
--- /dev/null
+++ b/libmincrypt/tools/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpkey
+LOCAL_SRC_FILES := DumpPublicKey.java
+LOCAL_JAR_MANIFEST := DumpPublicKey.mf
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java
new file mode 100644
index 0000000..c9e7e4d
--- /dev/null
+++ b/libmincrypt/tools/DumpPublicKey.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumpkey;
+
+import java.io.FileInputStream;
+import java.math.BigInteger;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
+import java.security.KeyStore;
+import java.security.Key;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import sun.misc.BASE64Encoder;
+
+/**
+ * Command line tool to extract RSA public keys from X.509 certificates
+ * and output source code with data initializers for the keys.
+ * @hide
+ */
+class DumpPublicKey {
+    /**
+     * @param key to perform sanity checks on
+     * @throws Exception if the key has the wrong size or public exponent
+     */
+    static void check(RSAPublicKey key) throws Exception {
+        BigInteger pubexp = key.getPublicExponent();
+        BigInteger modulus = key.getModulus();
+
+        if (!pubexp.equals(BigInteger.valueOf(3)))
+                throw new Exception("Public exponent should be 3 but is " +
+                        pubexp.toString(10) + ".");
+
+        if (modulus.bitLength() != 2048)
+             throw new Exception("Modulus should be 2048 bits long but is " +
+                        modulus.bitLength() + " bits.");
+    }
+
+    /**
+     * @param key to output
+     * @return a C initializer representing this public key.
+     */
+    static String print(RSAPublicKey key) throws Exception {
+        check(key);
+
+        BigInteger N = key.getModulus();
+
+        StringBuilder result = new StringBuilder();
+
+        int nwords = N.bitLength() / 32;    // # of 32 bit integers in modulus
+
+        result.append("{");
+        result.append(nwords);
+
+        BigInteger B = BigInteger.valueOf(0x100000000L);  // 2^32
+        BigInteger N0inv = B.subtract(N.modInverse(B));   // -1 / N[0] mod 2^32
+
+        result.append(",0x");
+        result.append(N0inv.toString(16));
+
+        BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
+        BigInteger RR = R.multiply(R).mod(N);    // 2^4096 mod N
+
+        // Write out modulus as little endian array of integers.
+        result.append(",{");
+        for (int i = 0; i < nwords; ++i) {
+            int n = N.mod(B).intValue();
+            result.append(n);
+
+            if (i != nwords - 1) {
+                result.append(",");
+            }
+
+            N = N.divide(B);
+        }
+        result.append("}");
+
+        // Write R^2 as little endian array of integers.
+        result.append(",{");
+        for (int i = 0; i < nwords; ++i) {
+            int rr = RR.mod(B).intValue();
+            result.append(rr);
+
+            if (i != nwords - 1) {
+                result.append(",");
+            }
+
+            RR = RR.divide(B);
+        }
+        result.append("}");
+
+        result.append("}");
+        return result.toString();
+    }
+
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            System.err.println("Usage: DumpPublicKey certfile ... > source.c");
+            System.exit(1);
+        }
+        try {
+            for (int i = 0; i < args.length; i++) {
+                FileInputStream input = new FileInputStream(args[i]);
+                CertificateFactory cf = CertificateFactory.getInstance("X.509");
+                Certificate cert = cf.generateCertificate(input);
+                RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey());
+                check(key);
+                System.out.print(print(key));
+                System.out.println(i < args.length - 1 ? "," : "");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        System.exit(0);
+    }
+}
diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf
new file mode 100644
index 0000000..7bb3bc8
--- /dev/null
+++ b/libmincrypt/tools/DumpPublicKey.mf
@@ -0,0 +1 @@
+Main-Class: com.android.dumpkey.DumpPublicKey
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
new file mode 100644
index 0000000..46102d5
--- /dev/null
+++ b/libnetutils/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        dhcpclient.c \
+        dhcpmsg.c \
+        dhcp_utils.c \
+        ifc_utils.c \
+	packet.c
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils
+
+# need "-lrt" on Linux simulator to pick up clock_gettime
+ifeq ($(TARGET_SIMULATOR),true)
+	ifeq ($(HOST_OS),linux)
+		LOCAL_LDLIBS += -lrt -lpthread
+	endif
+endif
+
+LOCAL_MODULE:= libnetutils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
new file mode 100644
index 0000000..bad2e2f
--- /dev/null
+++ b/libnetutils/dhcp_utils.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+/* Utilities for managing the dhcpcd DHCP client daemon */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+
+static const char DAEMON_NAME[]       = "dhcpcd";
+static const char DAEMON_PROP_NAME[]  = "init.svc.dhcpcd";
+static const char DHCP_PROP_NAME_PREFIX[]  = "dhcp";
+static const int  NAP_TIME = 1;   /* wait for 1 second at a time */
+                                  /* when polling for property values */
+static char errmsg[100];
+
+/*
+ * Wait for a system property to be assigned a specified value.
+ * If desired_value is NULL, then just wait for the property to
+ * be created with any value. maxwait is the maximum amount of
+ * time in seconds to wait before giving up.
+ */
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = maxwait / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(1000000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || 
+                    strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+static void fill_ip_info(const char *interface,
+                     in_addr_t *ipaddr,
+                     in_addr_t *gateway,
+                     in_addr_t *mask,
+                     in_addr_t *dns1,
+                     in_addr_t *dns2,
+                     in_addr_t *server,
+                     uint32_t  *lease)
+{
+    char prop_name[PROPERTY_KEY_MAX];
+    char prop_value[PROPERTY_VALUE_MAX];
+    struct in_addr addr;
+    in_addr_t iaddr;
+
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *ipaddr = addr.s_addr;
+    } else {
+        *ipaddr = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *gateway = addr.s_addr;
+    } else {
+        *gateway = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *mask = addr.s_addr;
+    } else {
+        *mask = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *dns1 = addr.s_addr;
+    } else {
+        *dns1 = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *dns2 = addr.s_addr;
+    } else {
+        *dns2 = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+        *server = addr.s_addr;
+    } else {
+        *server = 0;
+    }
+    snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, interface);
+    if (property_get(prop_name, prop_value, NULL)) {
+        *lease = atol(prop_value);
+    }
+}
+
+/*
+ * Start the dhcp client daemon, and wait for it to finish
+ * configuring the interface.
+ */
+int dhcp_do_request(const char *interface,
+                    in_addr_t *ipaddr,
+                    in_addr_t *gateway,
+                    in_addr_t *mask,
+                    in_addr_t *dns1,
+                    in_addr_t *dns2,
+                    in_addr_t *server,
+                    uint32_t  *lease)
+{
+    char result_prop_name[PROPERTY_KEY_MAX];
+    char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
+    const char *ctrl_prop = "ctl.start";
+    const char *desired_status = "running";
+
+    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+            DHCP_PROP_NAME_PREFIX,
+            interface);
+    /* Erase any previous setting of the dhcp result property */
+    property_set(result_prop_name, "");
+
+    /* Start the daemon and wait until it's ready */
+    property_set(ctrl_prop, DAEMON_NAME);
+    if (wait_for_property(DAEMON_PROP_NAME, desired_status, 10) < 0) {
+        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
+        return -1;
+    }
+
+    /* Wait for the daemon to return a result */
+    if (wait_for_property(result_prop_name, NULL, 30) < 0) {
+        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
+        return -1;
+    }
+
+    if (!property_get(result_prop_name, prop_value, NULL)) {
+        /* shouldn't ever happen, given the success of wait_for_property() */
+        snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
+        return -1;
+    }
+    if (strcmp(prop_value, "ok") == 0) {
+        fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease);
+        return 0;
+    } else {
+        snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
+        return -1;
+    }
+}
+
+/**
+ * Stop the DHCP client daemon.
+ */
+int dhcp_stop(const char *interface)
+{
+    char result_prop_name[PROPERTY_KEY_MAX];
+    const char *ctrl_prop = "ctl.stop";
+    const char *desired_status = "stopped";
+
+    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+            DHCP_PROP_NAME_PREFIX,
+            interface);
+    /* Stop the daemon and wait until it's reported to be stopped */
+    property_set(ctrl_prop, DAEMON_NAME);
+    if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) {
+        return -1;
+    }
+    property_set(result_prop_name, "failed");
+    return 0;
+}
+
+/**
+ * Release the current DHCP client lease.
+ */
+int dhcp_release_lease(const char *interface)
+{
+    const char *ctrl_prop = "ctl.stop";
+    const char *desired_status = "stopped";
+
+    /* Stop the daemon and wait until it's reported to be stopped */
+    property_set(ctrl_prop, DAEMON_NAME);
+    if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+char *dhcp_get_errmsg() {
+    return errmsg;
+}
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
new file mode 100644
index 0000000..45e392a
--- /dev/null
+++ b/libnetutils/dhcpclient.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <time.h>
+#include <sys/time.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+
+#include <dirent.h>
+
+#include "dhcpmsg.h"
+#include "ifc_utils.h"
+#include "packet.h"
+
+#define VERBOSE 2
+
+static int verbose = 1;
+static char errmsg[2048];
+
+typedef unsigned long long msecs_t;
+#if VERBOSE
+void dump_dhcp_msg();
+#endif
+
+msecs_t get_msecs(void)
+{
+    struct timespec ts;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
+        return 0;
+    } else {
+        return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
+            (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
+    }
+}
+
+void printerr(char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
+    va_end(ap);
+
+    LOGD(errmsg);
+}
+
+const char *dhcp_lasterror()
+{
+    return errmsg;
+}
+
+int fatal(const char *reason)
+{
+    printerr("%s: %s\n", reason, strerror(errno));
+    return -1;
+//    exit(1);
+}
+
+const char *ipaddr(uint32_t addr)
+{
+    static char buf[32];
+
+    sprintf(buf,"%d.%d.%d.%d",
+            addr & 255,
+            ((addr >> 8) & 255),
+            ((addr >> 16) & 255),
+            (addr >> 24));
+    return buf;
+}
+
+typedef struct dhcp_info dhcp_info;
+
+struct dhcp_info {
+    uint32_t type;
+
+    uint32_t ipaddr;
+    uint32_t gateway;
+    uint32_t netmask;
+
+    uint32_t dns1;
+    uint32_t dns2;
+
+    uint32_t serveraddr;
+    uint32_t lease;
+};
+
+dhcp_info last_good_info;
+
+void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *mask,
+                   uint32_t *dns1, uint32_t *dns2, uint32_t *server,
+                   uint32_t *lease)
+{
+    *ipaddr = last_good_info.ipaddr;
+    *gateway = last_good_info.gateway;
+    *mask = last_good_info.netmask;
+    *dns1 = last_good_info.dns1;
+    *dns2 = last_good_info.dns2;
+    *server = last_good_info.serveraddr;
+    *lease = last_good_info.lease;
+}
+
+static int ifc_configure(const char *ifname, dhcp_info *info)
+{
+    char dns_prop_name[PROPERTY_KEY_MAX];
+
+    if (ifc_set_addr(ifname, info->ipaddr)) {
+        printerr("failed to set ipaddr %s: %s\n", ipaddr(info->ipaddr), strerror(errno));
+        return -1;
+    }
+    if (ifc_set_mask(ifname, info->netmask)) {
+        printerr("failed to set netmask %s: %s\n", ipaddr(info->netmask), strerror(errno));
+        return -1;
+    }
+    if (ifc_create_default_route(ifname, info->gateway)) {
+        printerr("failed to set default route %s: %s\n", ipaddr(info->gateway), strerror(errno));
+        return -1;
+    }
+
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
+    property_set(dns_prop_name, info->dns1 ? ipaddr(info->dns1) : "");
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
+    property_set(dns_prop_name, info->dns2 ? ipaddr(info->dns2) : "");
+
+    last_good_info = *info;
+
+    return 0;
+}
+
+static const char *dhcp_type_to_name(uint32_t type)
+{
+    switch(type) {
+    case DHCPDISCOVER: return "discover";
+    case DHCPOFFER:    return "offer";
+    case DHCPREQUEST:  return "request";
+    case DHCPDECLINE:  return "decline";
+    case DHCPACK:      return "ack";
+    case DHCPNAK:      return "nak";
+    case DHCPRELEASE:  return "release";
+    case DHCPINFORM:   return "inform";
+    default:           return "???";
+    }
+}
+
+void dump_dhcp_info(dhcp_info *info)
+{
+    char addr[20], gway[20], mask[20];
+    LOGD("--- dhcp %s (%d) ---",
+            dhcp_type_to_name(info->type), info->type);
+    strcpy(addr, ipaddr(info->ipaddr));
+    strcpy(gway, ipaddr(info->gateway));
+    strcpy(mask, ipaddr(info->netmask));
+    LOGD("ip %s gw %s mask %s", addr, gway, mask);
+    if (info->dns1) LOGD("dns1: %s", ipaddr(info->dns1));
+    if (info->dns2) LOGD("dns2: %s", ipaddr(info->dns2));
+    LOGD("server %s, lease %d seconds",
+            ipaddr(info->serveraddr), info->lease);
+}
+
+
+int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
+{
+    uint8_t *x;
+    unsigned int opt;
+    int optlen;
+
+    memset(info, 0, sizeof(dhcp_info));
+    if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
+
+    len -= (DHCP_MSG_FIXED_SIZE + 4);
+
+    if (msg->options[0] != OPT_COOKIE1) return -1;
+    if (msg->options[1] != OPT_COOKIE2) return -1;
+    if (msg->options[2] != OPT_COOKIE3) return -1;
+    if (msg->options[3] != OPT_COOKIE4) return -1;
+
+    x = msg->options + 4;
+
+    while (len > 2) {
+        opt = *x++;
+        if (opt == OPT_PAD) {
+            len--;
+            continue;
+        }
+        if (opt == OPT_END) {
+            break;
+        }
+        optlen = *x++;
+        len -= 2;
+        if (optlen > len) {
+            break;
+        }
+        switch(opt) {
+        case OPT_SUBNET_MASK:
+            if (optlen >= 4) memcpy(&info->netmask, x, 4);
+            break;
+        case OPT_GATEWAY:
+            if (optlen >= 4) memcpy(&info->gateway, x, 4);
+            break;
+        case OPT_DNS:
+            if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
+            if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
+            break;
+        case OPT_LEASE_TIME:
+            if (optlen >= 4) {
+                memcpy(&info->lease, x, 4);
+                info->lease = ntohl(info->lease);
+            }
+            break;
+        case OPT_SERVER_ID:
+            if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
+            break;
+        case OPT_MESSAGE_TYPE:
+            info->type = *x;
+            break;
+        default:
+            break;
+        }
+        x += optlen;
+        len -= optlen;
+    }
+
+    info->ipaddr = msg->yiaddr;
+
+    return 0;
+}
+
+#if VERBOSE
+
+static void hex2str(char *buf, const unsigned char *array, int len)
+{
+    int i;
+    char *cp = buf;
+
+    for (i = 0; i < len; i++) {
+        cp += sprintf(cp, " %02x ", array[i]);
+    }
+}
+
+void dump_dhcp_msg(dhcp_msg *msg, int len)
+{
+    unsigned char *x;
+    unsigned int n,c;
+    int optsz;
+    const char *name;
+    char buf[2048];
+
+    LOGD("===== DHCP message:");
+    if (len < DHCP_MSG_FIXED_SIZE) {
+        LOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
+        return;
+    }
+
+    len -= DHCP_MSG_FIXED_SIZE;
+
+    if (msg->op == OP_BOOTREQUEST)
+        name = "BOOTREQUEST";
+    else if (msg->op == OP_BOOTREPLY)
+        name = "BOOTREPLY";
+    else
+        name = "????";
+    LOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d",
+           name, msg->op, msg->htype, msg->hlen, msg->hops);
+    LOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d",
+           ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len);
+    LOGD("ciaddr = %s", ipaddr(msg->ciaddr));
+    LOGD("yiaddr = %s", ipaddr(msg->yiaddr));
+    LOGD("siaddr = %s", ipaddr(msg->siaddr));
+    LOGD("giaddr = %s", ipaddr(msg->giaddr));
+
+    c = msg->hlen > 16 ? 16 : msg->hlen;
+    hex2str(buf, msg->chaddr, c);
+    LOGD("chaddr = {%s}", buf);
+
+    for (n = 0; n < 64; n++) {
+        if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) {
+            if (msg->sname[n] == 0) break;
+            msg->sname[n] = '.';
+        }
+    }
+    msg->sname[63] = 0;
+
+    for (n = 0; n < 128; n++) {
+        if ((msg->file[n] < ' ') || (msg->file[n] > 127)) {
+            if (msg->file[n] == 0) break;
+            msg->file[n] = '.';
+        }
+    }
+    msg->file[127] = 0;
+
+    LOGD("sname = '%s'", msg->sname);
+    LOGD("file = '%s'", msg->file);
+
+    if (len < 4) return;
+    len -= 4;
+    x = msg->options + 4;
+
+    while (len > 2) {
+        if (*x == 0) {
+            x++;
+            len--;
+            continue;
+        }
+        if (*x == OPT_END) {
+            break;
+        }
+        len -= 2;
+        optsz = x[1];
+        if (optsz > len) break;
+        if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
+            if ((unsigned int)optsz < sizeof(buf) - 1) {
+                n = optsz;
+            } else {
+                n = sizeof(buf) - 1;
+            }
+            memcpy(buf, &x[2], n);
+            buf[n] = '\0';
+        } else {
+            hex2str(buf, &x[2], optsz);
+        }
+        if (x[0] == OPT_MESSAGE_TYPE)
+            name = dhcp_type_to_name(x[2]);
+        else
+            name = NULL;
+        LOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name);
+        len -= optsz;
+        x = x + optsz + 2;
+    }
+}
+
+#endif
+
+static int send_message(int sock, int if_index, dhcp_msg  *msg, int size)
+{
+#if VERBOSE > 1
+    dump_dhcp_msg(msg, size);
+#endif
+    return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
+                       PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
+}
+
+static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
+{
+    if (sz < DHCP_MSG_FIXED_SIZE) {
+        if (verbose) LOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
+        return 0;
+    }
+    if (reply->op != OP_BOOTREPLY) {
+        if (verbose) LOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
+        return 0;
+    }
+    if (reply->xid != msg->xid) {
+        if (verbose) LOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
+                          ntohl(msg->xid));
+        return 0;
+    }
+    if (reply->htype != msg->htype) {
+        if (verbose) LOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype);
+        return 0;
+    }
+    if (reply->hlen != msg->hlen) {
+        if (verbose) LOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
+        return 0;
+    }
+    if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
+        if (verbose) LOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
+        return 0;
+    }
+    return 1;
+}
+
+#define STATE_SELECTING  1
+#define STATE_REQUESTING 2
+
+#define TIMEOUT_INITIAL   4000
+#define TIMEOUT_MAX      32000
+
+int dhcp_init_ifc(const char *ifname)
+{
+    dhcp_msg discover_msg;
+    dhcp_msg request_msg;
+    dhcp_msg reply;
+    dhcp_msg *msg;
+    dhcp_info info;
+    int s, r, size;
+    int valid_reply;
+    uint32_t xid;
+    unsigned char hwaddr[6];
+    struct pollfd pfd;
+    unsigned int state;
+    unsigned int timeout;
+    int if_index;
+
+    xid = (uint32_t) get_msecs();
+
+    if (ifc_get_hwaddr(ifname, hwaddr)) {
+        return fatal("cannot obtain interface address");
+    }
+    if (ifc_get_ifindex(ifname, &if_index)) {
+        return fatal("cannot obtain interface index");
+    }
+
+    s = open_raw_socket(ifname, hwaddr, if_index);
+
+    timeout = TIMEOUT_INITIAL;
+    state = STATE_SELECTING;
+    info.type = 0;
+    goto transmit;
+
+    for (;;) {
+        pfd.fd = s;
+        pfd.events = POLLIN;
+        pfd.revents = 0;
+        r = poll(&pfd, 1, timeout);
+
+        if (r == 0) {
+#if VERBOSE
+            printerr("TIMEOUT\n");
+#endif
+            if (timeout >= TIMEOUT_MAX) {
+                printerr("timed out\n");
+                if ( info.type == DHCPOFFER ) {
+                    printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
+                    return ifc_configure(ifname, &info);
+                }
+                errno = ETIME;
+                close(s);
+                return -1;
+            }
+            timeout = timeout * 2;
+
+        transmit:
+            size = 0;
+            msg = NULL;
+            switch(state) {
+            case STATE_SELECTING:
+                msg = &discover_msg;
+                size = init_dhcp_discover_msg(msg, hwaddr, xid);
+                break;
+            case STATE_REQUESTING:
+                msg = &request_msg;
+                size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
+                break;
+            default:
+                r = 0;
+            }
+            if (size != 0) {
+                r = send_message(s, if_index, msg, size);
+                if (r < 0) {
+                    printerr("error sending dhcp msg: %s\n", strerror(errno));
+                }
+            }
+            continue;
+        }
+
+        if (r < 0) {
+            if ((errno == EAGAIN) || (errno == EINTR)) {
+                continue;
+            }
+            return fatal("poll failed");
+        }
+
+        errno = 0;
+        r = receive_packet(s, &reply);
+        if (r < 0) {
+            if (errno != 0) {
+                LOGD("receive_packet failed (%d): %s", r, strerror(errno));
+                if (errno == ENETDOWN || errno == ENXIO) {
+                    return -1;
+                }
+            }
+            continue;
+        }
+
+#if VERBOSE > 1
+        dump_dhcp_msg(&reply, r);
+#endif
+        decode_dhcp_msg(&reply, r, &info);
+
+        if (state == STATE_SELECTING) {
+            valid_reply = is_valid_reply(&discover_msg, &reply, r);
+        } else {
+            valid_reply = is_valid_reply(&request_msg, &reply, r);
+        }
+        if (!valid_reply) {
+            printerr("invalid reply\n");
+            continue;
+        }
+
+        if (verbose) dump_dhcp_info(&info);
+
+        switch(state) {
+        case STATE_SELECTING:
+            if (info.type == DHCPOFFER) {
+                state = STATE_REQUESTING;
+                timeout = TIMEOUT_INITIAL;
+                xid++;
+                goto transmit;
+            }
+            break;
+        case STATE_REQUESTING:
+            if (info.type == DHCPACK) {
+                printerr("configuring %s\n", ifname);
+                close(s);
+                return ifc_configure(ifname, &info);
+            } else if (info.type == DHCPNAK) {
+                printerr("configuration request denied\n");
+                close(s);
+                return -1;
+            } else {
+                printerr("ignoring %s message in state %d\n",
+                         dhcp_type_to_name(info.type), state);
+            }
+            break;
+        }
+    }
+    close(s);
+    return 0;
+}
+
+int do_dhcp(char *iname)
+{
+    if (ifc_set_addr(iname, 0)) {
+        printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
+        return -1;
+    }
+
+    if (ifc_up(iname)) {
+        printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
+        return -1;
+    }
+
+    return dhcp_init_ifc(iname);
+}
diff --git a/libnetutils/dhcpmsg.c b/libnetutils/dhcpmsg.c
new file mode 100644
index 0000000..1e0a233
--- /dev/null
+++ b/libnetutils/dhcpmsg.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "dhcpmsg.h"
+
+static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
+{
+    uint8_t *x;
+
+    memset(msg, 0, sizeof(dhcp_msg));
+
+    msg->op = OP_BOOTREQUEST;
+    msg->htype = HTYPE_ETHER;
+    msg->hlen = 6;
+    msg->hops = 0;
+
+    msg->flags = htons(FLAGS_BROADCAST);
+
+    msg->xid = xid;
+
+    memcpy(msg->chaddr, hwaddr, 6);
+
+    x = msg->options;
+
+    *x++ = OPT_COOKIE1;
+    *x++ = OPT_COOKIE2;
+    *x++ = OPT_COOKIE3;
+    *x++ = OPT_COOKIE4;
+
+    *x++ = OPT_MESSAGE_TYPE;
+    *x++ = 1;
+    *x++ = type;
+
+    return x;
+}
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
+{
+    uint8_t *x;
+
+    x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
+
+    *x++ = OPT_PARAMETER_LIST;
+    *x++ = 4;
+    *x++ = OPT_SUBNET_MASK;
+    *x++ = OPT_GATEWAY;
+    *x++ = OPT_DNS;
+    *x++ = OPT_BROADCAST_ADDR;
+
+    *x++ = OPT_END;
+
+    return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+                          uint32_t ipaddr, uint32_t serveraddr)
+{
+    uint8_t *x;
+
+    x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
+
+    *x++ = OPT_PARAMETER_LIST;
+    *x++ = 4;
+    *x++ = OPT_SUBNET_MASK;
+    *x++ = OPT_GATEWAY;
+    *x++ = OPT_DNS;
+    *x++ = OPT_BROADCAST_ADDR;
+
+    *x++ = OPT_REQUESTED_IP;
+    *x++ = 4;
+    memcpy(x, &ipaddr, 4);
+    x +=  4;
+
+    *x++ = OPT_SERVER_ID;
+    *x++ = 4;
+    memcpy(x, &serveraddr, 4);
+    x += 4;
+
+    *x++ = OPT_END;
+
+    return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
diff --git a/libnetutils/dhcpmsg.h b/libnetutils/dhcpmsg.h
new file mode 100644
index 0000000..c86e400
--- /dev/null
+++ b/libnetutils/dhcpmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#ifndef _WIFI_DHCP_H_
+#define _WIFI_DHCP_H_
+
+#include <sys/types.h>
+
+#define PORT_BOOTP_SERVER 67
+#define PORT_BOOTP_CLIENT 68
+
+/* RFC 2131 p 9 */
+typedef struct dhcp_msg dhcp_msg;
+
+#define OP_BOOTREQUEST 1
+#define OP_BOOTREPLY   2
+
+#define FLAGS_BROADCAST 0x8000
+
+#define HTYPE_ETHER    1
+
+struct dhcp_msg
+{
+    uint8_t op;           /* BOOTREQUEST / BOOTREPLY    */
+    uint8_t htype;        /* hw addr type               */
+    uint8_t hlen;         /* hw addr len                */
+    uint8_t hops;         /* client set to 0            */
+    
+    uint32_t xid;         /* transaction id             */
+
+    uint16_t secs;        /* seconds since start of acq */
+    uint16_t flags;
+
+    uint32_t ciaddr;      /* client IP addr             */
+    uint32_t yiaddr;      /* your (client) IP addr      */
+    uint32_t siaddr;      /* ip addr of next server     */
+                          /* (DHCPOFFER and DHCPACK)    */
+    uint32_t giaddr;      /* relay agent IP addr        */
+
+    uint8_t chaddr[16];  /* client hw addr             */
+    char sname[64];      /* asciiz server hostname     */
+    char file[128];      /* asciiz boot file name      */
+
+    uint8_t options[1024];  /* optional parameters        */
+};
+
+#define DHCP_MSG_FIXED_SIZE 236
+
+/* first four bytes of options are a cookie to indicate that
+** the payload are DHCP options as opposed to some other BOOTP
+** extension.
+*/
+#define OPT_COOKIE1          0x63
+#define OPT_COOKIE2          0x82
+#define OPT_COOKIE3          0x53
+#define OPT_COOKIE4          0x63
+
+/* BOOTP/DHCP options - see RFC 2132 */
+#define OPT_PAD              0
+
+#define OPT_SUBNET_MASK      1     /* 4 <ipaddr> */
+#define OPT_TIME_OFFSET      2     /* 4 <seconds> */
+#define OPT_GATEWAY          3     /* 4*n <ipaddr> * n */
+#define OPT_DNS              6     /* 4*n <ipaddr> * n */
+#define OPT_DOMAIN_NAME      15    /* n <domainnamestring> */
+#define OPT_BROADCAST_ADDR   28    /* 4 <ipaddr> */
+
+#define OPT_REQUESTED_IP     50    /* 4 <ipaddr> */
+#define OPT_LEASE_TIME       51    /* 4 <seconds> */
+#define OPT_MESSAGE_TYPE     53    /* 1 <msgtype> */
+#define OPT_SERVER_ID        54    /* 4 <ipaddr> */
+#define OPT_PARAMETER_LIST   55    /* n <optcode> * n */
+#define OPT_MESSAGE          56    /* n <errorstring> */
+#define OPT_CLASS_ID         60    /* n <opaque> */
+#define OPT_CLIENT_ID        61    /* n <opaque> */
+#define OPT_END              255
+
+/* DHCP message types */
+#define DHCPDISCOVER         1
+#define DHCPOFFER            2
+#define DHCPREQUEST          3
+#define DHCPDECLINE          4
+#define DHCPACK              5
+#define DHCPNAK              6
+#define DHCPRELEASE          7
+#define DHCPINFORM           8
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+                          uint32_t ipaddr, uint32_t serveraddr);
+
+#endif
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
new file mode 100644
index 0000000..88635d9
--- /dev/null
+++ b/libnetutils/ifc_utils.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <linux/route.h>
+#include <linux/wireless.h>
+
+#ifdef ANDROID
+#define LOG_TAG "NetUtils"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define LOGD printf
+#define LOGW printf
+#endif
+
+static int ifc_ctl_sock = -1;
+void printerr(char *fmt, ...);
+
+static const char *ipaddr_to_string(uint32_t addr)
+{
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
+}
+
+int ifc_init(void)
+{
+    if (ifc_ctl_sock == -1) {
+        ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);    
+        if (ifc_ctl_sock < 0) {
+            printerr("socket() failed: %s\n", strerror(errno));
+        }
+    }
+    return ifc_ctl_sock < 0 ? -1 : 0;
+}
+
+void ifc_close(void)
+{
+    if (ifc_ctl_sock != -1) {
+        (void)close(ifc_ctl_sock);
+        ifc_ctl_sock = -1;
+    }
+}
+
+static void ifc_init_ifr(const char *name, struct ifreq *ifr)
+{
+    memset(ifr, 0, sizeof(struct ifreq));
+    strncpy(ifr->ifr_name, name, IFNAMSIZ);
+    ifr->ifr_name[IFNAMSIZ - 1] = 0;
+}
+
+int ifc_get_hwaddr(const char *name, void *ptr)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
+    if(r < 0) return -1;
+
+    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, 6);
+    return 0;    
+}
+
+int ifc_get_ifindex(const char *name, int *if_indexp)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
+    if(r < 0) return -1;
+
+    *if_indexp = ifr.ifr_ifindex;
+    return 0;    
+}
+
+static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
+    ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+    return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
+}
+
+int ifc_up(const char *name)
+{
+    return ifc_set_flags(name, IFF_UP, 0);
+}
+
+int ifc_down(const char *name)
+{
+    return ifc_set_flags(name, 0, IFF_UP);
+}
+
+static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
+{
+    struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    sin->sin_addr.s_addr = addr;
+}
+
+int ifc_set_addr(const char *name, in_addr_t addr)
+{
+    struct ifreq ifr;
+
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, addr);
+    
+    return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
+}
+
+int ifc_set_mask(const char *name, in_addr_t mask)
+{
+    struct ifreq ifr;
+
+    ifc_init_ifr(name, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, mask);
+    
+    return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+}
+
+int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags)
+{
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    if (addr != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {
+            *addr = 0;
+        } else {
+            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+        }
+    }
+    
+    if (mask != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {
+            *mask = 0;
+        } else {
+            *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+        }
+    }
+
+    if (flags != NULL) {
+        if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {
+            *flags = 0;
+        } else {
+            *flags = ifr.ifr_flags;
+        }
+    }
+
+    return 0;
+}
+
+
+int ifc_create_default_route(const char *name, in_addr_t addr)
+{
+    struct rtentry rt;
+
+    memset(&rt, 0, sizeof(rt));
+    
+    rt.rt_dst.sa_family = AF_INET;
+    rt.rt_flags = RTF_UP | RTF_GATEWAY;
+    rt.rt_dev = (void*) name;
+    init_sockaddr_in(&rt.rt_genmask, 0);
+    init_sockaddr_in(&rt.rt_gateway, addr);
+    
+    return ioctl(ifc_ctl_sock, SIOCADDRT, &rt);
+}
+
+int ifc_add_host_route(const char *name, in_addr_t addr)
+{
+    struct rtentry rt;
+    int result;
+
+    memset(&rt, 0, sizeof(rt));
+    
+    rt.rt_dst.sa_family = AF_INET;
+    rt.rt_flags = RTF_UP | RTF_HOST;
+    rt.rt_dev = (void*) name;
+    init_sockaddr_in(&rt.rt_dst, addr);
+    init_sockaddr_in(&rt.rt_genmask, 0);
+    init_sockaddr_in(&rt.rt_gateway, 0);
+    
+    ifc_init();
+    result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt);
+    if (result < 0 && errno == EEXIST) {
+        result = 0;
+    }
+    ifc_close();
+    return result;
+}
+
+int ifc_disable(const char *ifname)
+{
+    int result;
+
+    ifc_init();
+    result = ifc_down(ifname);
+    ifc_set_addr(ifname, 0);
+    ifc_close();
+    return result;
+}
+
+int ifc_reset_connections(const char *ifname)
+{
+#ifdef HAVE_ANDROID_OS
+    int result;
+    in_addr_t myaddr;
+    struct ifreq ifr;
+
+    ifc_init();
+    ifc_get_info(ifname, &myaddr, NULL, NULL);
+    ifc_init_ifr(ifname, &ifr);
+    init_sockaddr_in(&ifr.ifr_addr, myaddr);
+    result = ioctl(ifc_ctl_sock, SIOCKILLADDR,  &ifr);
+    ifc_close();
+    
+    return result;
+#else
+    return 0;
+#endif
+}
+
+/*
+ * Remove the routes associated with the named interface.
+ */
+int ifc_remove_host_routes(const char *name)
+{
+    char ifname[64];
+    in_addr_t dest, gway, mask;
+    int flags, refcnt, use, metric, mtu, win, irtt;
+    struct rtentry rt;
+    FILE *fp;
+    struct in_addr addr;
+
+    fp = fopen("/proc/net/route", "r");
+    if (fp == NULL)
+        return -1;
+    /* Skip the header line */
+    if (fscanf(fp, "%*[^\n]\n") < 0) {
+        fclose(fp);
+        return -1;
+    }
+    ifc_init();
+    for (;;) {
+        int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
+                           ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
+                           &mtu, &win, &irtt);
+        if (nread != 11) {
+            break;
+        }
+        if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)
+                || strcmp(ifname, name) != 0) {
+            continue;
+        }
+        memset(&rt, 0, sizeof(rt));
+        rt.rt_dev = (void *)name;
+        init_sockaddr_in(&rt.rt_dst, dest);
+        init_sockaddr_in(&rt.rt_gateway, gway);
+        init_sockaddr_in(&rt.rt_genmask, mask);
+        addr.s_addr = dest;
+        if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) {
+            LOGD("failed to remove route for %s to %s: %s",
+                 ifname, inet_ntoa(addr), strerror(errno));
+        }
+    }
+    fclose(fp);
+    ifc_close();
+    return 0;
+}
+
+/*
+ * Return the address of the default gateway
+ *
+ * TODO: factor out common code from this and remove_host_routes()
+ * so that we only scan /proc/net/route in one place.
+ */
+int ifc_get_default_route(const char *ifname)
+{
+    char name[64];
+    in_addr_t dest, gway, mask;
+    int flags, refcnt, use, metric, mtu, win, irtt;
+    int result;
+    FILE *fp;
+
+    fp = fopen("/proc/net/route", "r");
+    if (fp == NULL)
+        return 0;
+    /* Skip the header line */
+    if (fscanf(fp, "%*[^\n]\n") < 0) {
+        fclose(fp);
+        return 0;
+    }
+    ifc_init();
+    result = 0;
+    for (;;) {
+        int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
+                           name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
+                           &mtu, &win, &irtt);
+        if (nread != 11) {
+            break;
+        }
+        if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY)
+                && dest == 0
+                && strcmp(ifname, name) == 0) {
+            result = gway;
+            break;
+        }
+    }
+    fclose(fp);
+    ifc_close();
+    return result;
+}
+
+/*
+ * Sets the specified gateway as the default route for the named interface.
+ */
+int ifc_set_default_route(const char *ifname, in_addr_t gateway)
+{
+    struct in_addr addr;
+    int result;
+
+    ifc_init();
+    addr.s_addr = gateway;
+    if ((result = ifc_create_default_route(ifname, gateway)) < 0) {
+        LOGD("failed to add %s as default route for %s: %s",
+             inet_ntoa(addr), ifname, strerror(errno));
+    }
+    ifc_close();
+    return result;
+}
+
+/*
+ * Removes the default route for the named interface.
+ */
+int ifc_remove_default_route(const char *ifname)
+{
+    struct rtentry rt;
+    int result;
+
+    ifc_init();
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dev = (void *)ifname;
+    rt.rt_flags = RTF_UP|RTF_GATEWAY;
+    init_sockaddr_in(&rt.rt_dst, 0);
+    if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {
+        LOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
+    }
+    ifc_close();
+    return result;
+}
+
+int
+ifc_configure(const char *ifname,
+        in_addr_t address,
+        in_addr_t netmask,
+        in_addr_t gateway,
+        in_addr_t dns1,
+        in_addr_t dns2) {
+
+    char dns_prop_name[PROPERTY_KEY_MAX];
+
+    ifc_init();
+
+    if (ifc_up(ifname)) {
+        printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
+        ifc_close();
+        return -1;
+    }
+    if (ifc_set_addr(ifname, address)) {
+        printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
+        ifc_close();
+        return -1;
+    }
+    if (ifc_set_mask(ifname, netmask)) {
+        printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno));
+        ifc_close();
+        return -1;
+    }
+    if (ifc_create_default_route(ifname, gateway)) {
+        printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
+        ifc_close();
+        return -1;
+    }
+
+    ifc_close();
+
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns1", ifname);
+    property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns2", ifname);
+    property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
+
+    return 0;
+}
diff --git a/libnetutils/ifc_utils.h b/libnetutils/ifc_utils.h
new file mode 100644
index 0000000..49b8747
--- /dev/null
+++ b/libnetutils/ifc_utils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#ifndef _IFC_UTILS_H_
+#define _IFC_UTILS_H_
+
+int ifc_init(void);
+
+int ifc_get_ifindex(const char *name, int *if_indexp);
+int ifc_get_hwaddr(const char *name, void *ptr);
+
+int ifc_up(const char *name);
+int ifc_down(const char *name);
+
+int ifc_set_addr(const char *name, unsigned addr);
+int ifc_set_mask(const char *name, unsigned mask);
+
+int ifc_create_default_route(const char *name, unsigned addr);
+
+int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags);
+
+#endif
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
new file mode 100644
index 0000000..9388345
--- /dev/null
+++ b/libnetutils/packet.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <errno.h>
+
+#ifdef ANDROID
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define LOGD printf
+#define LOGW printf
+#endif
+
+#include "dhcpmsg.h"
+
+int fatal();
+
+int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index)
+{
+    int s, flag;
+    struct sockaddr_ll bindaddr;
+
+    if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+        return fatal("socket(PF_PACKET)");
+    }
+
+    memset(&bindaddr, 0, sizeof(bindaddr));
+    bindaddr.sll_family = AF_PACKET;
+    bindaddr.sll_protocol = htons(ETH_P_IP);
+    bindaddr.sll_halen = ETH_ALEN;
+    memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
+    bindaddr.sll_ifindex = if_index;
+
+    if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+        return fatal("Cannot bind raw socket to interface");
+    }
+
+    return s;
+}
+
+static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
+{
+    uint16_t *up = (uint16_t *)buffer;
+    uint32_t sum = startsum;
+    uint32_t upper16;
+
+    while (count > 1) {
+        sum += *up++;
+        count -= 2;
+    }
+    if (count > 0) {
+        sum += (uint16_t) *(uint8_t *)up;
+    }
+    while ((upper16 = (sum >> 16)) != 0) {
+        sum = (sum & 0xffff) + upper16;
+    }
+    return sum;
+}
+
+static uint32_t finish_sum(uint32_t sum)
+{
+    return ~sum & 0xffff;
+}
+
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+                uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
+{
+    struct iphdr ip;
+    struct udphdr udp;
+    struct iovec iov[3];
+    uint32_t udpsum;
+    uint16_t temp;
+    struct msghdr msghdr;
+    struct sockaddr_ll destaddr;
+
+    ip.version = IPVERSION;
+    ip.ihl = sizeof(ip) >> 2;
+    ip.tos = 0;
+    ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
+    ip.id = 0;
+    ip.frag_off = 0;
+    ip.ttl = IPDEFTTL;
+    ip.protocol = IPPROTO_UDP;
+    ip.check = 0;
+    ip.saddr = saddr;
+    ip.daddr = daddr;
+    ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
+
+    udp.source = htons(sport);
+    udp.dest = htons(dport);
+    udp.len = htons(sizeof(udp) + size);
+    udp.check = 0;
+
+    /* Calculate checksum for pseudo header */
+    udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
+    udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
+    temp = htons(IPPROTO_UDP);
+    udpsum = checksum(&temp, sizeof(temp), udpsum);
+    temp = udp.len;
+    udpsum = checksum(&temp, sizeof(temp), udpsum);
+
+    /* Add in the checksum for the udp header */
+    udpsum = checksum(&udp, sizeof(udp), udpsum);
+
+    /* Add in the checksum for the data */
+    udpsum = checksum(msg, size, udpsum);
+    udp.check = finish_sum(udpsum);
+
+    iov[0].iov_base = (char *)&ip;
+    iov[0].iov_len = sizeof(ip);
+    iov[1].iov_base = (char *)&udp;
+    iov[1].iov_len = sizeof(udp);
+    iov[2].iov_base = (char *)msg;
+    iov[2].iov_len = size;
+    memset(&destaddr, 0, sizeof(destaddr));
+    destaddr.sll_family = AF_PACKET;
+    destaddr.sll_protocol = htons(ETH_P_IP);
+    destaddr.sll_ifindex = if_index;
+    destaddr.sll_halen = ETH_ALEN;
+    memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
+
+    msghdr.msg_name = &destaddr;
+    msghdr.msg_namelen = sizeof(destaddr);
+    msghdr.msg_iov = iov;
+    msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
+    msghdr.msg_flags = 0;
+    msghdr.msg_control = 0;
+    msghdr.msg_controllen = 0;
+    return sendmsg(s, &msghdr, 0);
+}
+
+int receive_packet(int s, struct dhcp_msg *msg)
+{
+    int nread;
+    int is_valid;
+    struct dhcp_packet {
+        struct iphdr ip;
+        struct udphdr udp;
+        struct dhcp_msg dhcp;
+    } packet;
+    int dhcp_size;
+    uint32_t sum;
+    uint16_t temp;
+    uint32_t saddr, daddr;
+
+    nread = read(s, &packet, sizeof(packet));
+    if (nread < 0) {
+        return -1;
+    }
+    /*
+     * The raw packet interface gives us all packets received by the
+     * network interface. We need to filter out all packets that are
+     * not meant for us.
+     */
+    is_valid = 0;
+    if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
+#if VERBOSE
+        LOGD("Packet is too small (%d) to be a UDP datagram", nread);
+#endif
+    } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
+#if VERBOSE
+        LOGD("Not a valid IP packet");
+#endif
+    } else if (nread < ntohs(packet.ip.tot_len)) {
+#if VERBOSE
+        LOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
+#endif
+    } else if (packet.ip.protocol != IPPROTO_UDP) {
+#if VERBOSE
+        LOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
+#endif
+    } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
+#if VERBOSE
+        LOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
+#endif
+    } else {
+        is_valid = 1;
+    }
+
+    if (!is_valid) {
+        return -1;
+    }
+
+    /* Seems like it's probably a valid DHCP packet */
+    /* validate IP header checksum */
+    sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
+    if (sum != 0) {
+        LOGW("IP header checksum failure (0x%x)", packet.ip.check);
+        return -1;
+    }
+    /*
+     * Validate the UDP checksum.
+     * Since we don't need the IP header anymore, we "borrow" it
+     * to construct the pseudo header used in the checksum calculation.
+     */
+    dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+    saddr = packet.ip.saddr;
+    daddr = packet.ip.daddr;
+    nread = ntohs(packet.ip.tot_len);
+    memset(&packet.ip, 0, sizeof(packet.ip));
+    packet.ip.saddr = saddr;
+    packet.ip.daddr = daddr;
+    packet.ip.protocol = IPPROTO_UDP;
+    packet.ip.tot_len = packet.udp.len;
+    temp = packet.udp.check;
+    packet.udp.check = 0;
+    sum = finish_sum(checksum(&packet, nread, 0));
+    packet.udp.check = temp;
+    if (temp != sum) {
+        LOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp);
+        return -1;
+    }
+    memcpy(msg, &packet.dhcp, dhcp_size);
+    return dhcp_size;
+}
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
new file mode 100644
index 0000000..aade392
--- /dev/null
+++ b/libnetutils/packet.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+#ifndef _WIFI_PACKET_H_
+#define _WIFI_PACKET_H_
+
+int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+                uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
+int receive_packet(int s, struct dhcp_msg *msg);
+
+#endif
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
new file mode 100644
index 0000000..50eb5f5
--- /dev/null
+++ b/libpixelflinger/Android.mk
@@ -0,0 +1,92 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+#
+# ARMv6 specific objects
+#
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_ASFLAGS := -march=armv6
+LOCAL_SRC_FILES := rotate90CW_4x4_16v6.S
+LOCAL_MODULE := libpixelflinger_armv6
+include $(BUILD_STATIC_LIBRARY)
+endif
+
+#
+# C/C++ and ARMv5 objects
+#
+
+include $(CLEAR_VARS)
+PIXELFLINGER_SRC_FILES:= \
+    codeflinger/ARMAssemblerInterface.cpp \
+    codeflinger/ARMAssemblerProxy.cpp \
+    codeflinger/ARMAssembler.cpp \
+    codeflinger/CodeCache.cpp \
+    codeflinger/GGLAssembler.cpp \
+    codeflinger/load_store.cpp \
+    codeflinger/blending.cpp \
+    codeflinger/texturing.cpp \
+    codeflinger/disassem.c \
+	tinyutils/SharedBuffer.cpp \
+	tinyutils/VectorImpl.cpp \
+	fixed.cpp.arm \
+	picker.cpp.arm \
+	pixelflinger.cpp.arm \
+	trap.cpp.arm \
+	scanline.cpp.arm \
+	format.cpp \
+	clear.cpp \
+	raster.cpp \
+	buffer.cpp
+
+ifeq ($(TARGET_ARCH),arm)
+PIXELFLINGER_SRC_FILES += t32cb16blend.S
+endif
+
+ifeq ($(TARGET_ARCH),arm)
+# special optimization flags for pixelflinger
+PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer
+endif
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+ifneq ($(TARGET_ARCH),arm)
+# Required to define logging functions on the simulator.
+# TODO: move the simulator logging functions into libcutils with
+# the rest of the basic log stuff.
+LOCAL_SHARED_LIBRARIES += libutils
+endif
+
+#
+# Shared library
+#
+
+LOCAL_MODULE:= libpixelflinger
+LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
+ifneq ($(BUILD_TINY_ANDROID),true)
+# Really this should go away entirely or at least not depend on
+# libhardware, but this at least gets us built.
+LOCAL_SHARED_LIBRARIES += libhardware_legacy
+LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
+endif
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# Static library version
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libpixelflinger_static
+LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
+LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) 
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_WHOLE_STATIC_LIBRARIES := libpixelflinger_armv6
+endif
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/MODULE_LICENSE_APACHE2 b/libpixelflinger/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libpixelflinger/MODULE_LICENSE_APACHE2
diff --git a/libpixelflinger/NOTICE b/libpixelflinger/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libpixelflinger/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
new file mode 100644
index 0000000..af7356b
--- /dev/null
+++ b/libpixelflinger/buffer.cpp
@@ -0,0 +1,384 @@
+/* libs/pixelflinger/buffer.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#include <assert.h>
+
+#include "buffer.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+static void read_pixel(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel);
+static void write_pixel(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, const pixel_t* pixel);
+static void readRGB565(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel);
+static void readABGR8888(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel);
+
+static uint32_t logic_op(int op, uint32_t s, uint32_t d);
+static uint32_t extract(uint32_t v, int h, int l, int bits);
+static uint32_t expand(uint32_t v, int sbits, int dbits);
+static uint32_t downshift_component(uint32_t in, uint32_t v,
+        int sh, int sl, int dh, int dl, int ch, int cl, int dither);
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_texture(context_t* c)
+{
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        texture_t& t = c->state.texture[i];
+        t.s_coord = GGL_ONE_TO_ONE;
+        t.t_coord = GGL_ONE_TO_ONE;
+        t.s_wrap = GGL_REPEAT;
+        t.t_wrap = GGL_REPEAT;
+        t.min_filter = GGL_NEAREST;
+        t.mag_filter = GGL_NEAREST;
+        t.env = GGL_MODULATE;
+    }
+    c->activeTMU = &(c->state.texture[0]);
+}
+
+void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src)
+{
+    dst->width = src->width;
+    dst->height = src->height;
+    dst->stride = src->stride;
+    dst->data = src->data;
+    dst->format = src->format;
+    dst->dirty = 1;
+    if (__builtin_expect(dst->stride < 0, false)) {
+        const GGLFormat& pixelFormat(c->formats[dst->format]);
+        const int32_t bpr = -dst->stride * pixelFormat.size;
+        dst->data += bpr * (dst->height-1);
+    }
+}
+
+static void pick_read_write(surface_t* s)
+{
+    // Choose best reader/writers.
+    switch (s->format) {
+        case GGL_PIXEL_FORMAT_RGBA_8888:    s->read = readABGR8888;  break;
+        case GGL_PIXEL_FORMAT_RGB_565:      s->read = readRGB565;    break;
+        default:                            s->read = read_pixel;    break;
+    }
+    s->write = write_pixel;
+}
+
+void ggl_pick_texture(context_t* c)
+{
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+        surface_t& s = c->state.texture[i].surface;
+        if ((!c->state.texture[i].enable) || (!s.dirty))
+            continue;
+        s.dirty = 0;
+        pick_read_write(&s);
+        generated_tex_vars_t& gen = c->generated_vars.texture[i];
+        gen.width   = s.width;
+        gen.height  = s.height;
+        gen.stride  = s.stride;
+        gen.data    = int32_t(s.data);
+    }
+}
+
+void ggl_pick_cb(context_t* c)
+{
+    surface_t& s = c->state.buffers.color;
+    if (s.dirty) {
+        s.dirty = 0;
+        pick_read_write(&s);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void read_pixel(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel)
+{
+    assert((x < s->width) && (y < s->height));
+
+    const GGLFormat* f = &(c->formats[s->format]);
+    int32_t index = x + (s->stride * y);
+    uint8_t* const data = s->data + index * f->size;
+    uint32_t v = 0;
+    switch (f->size) {
+        case 1:		v = *data;									break;
+        case 2:		v = *(uint16_t*)data;						break;
+        case 3:		v = (data[2]<<16)|(data[1]<<8)|data[0];     break;
+        case 4:		v = GGL_RGBA_TO_HOST(*(uint32_t*)data);		break;
+    }
+    for (int i=0 ; i<4 ; i++) {
+        pixel->s[i] = f->c[i].h - f->c[i].l;
+        if (pixel->s[i])
+            pixel->c[i] = extract(v,  f->c[i].h,  f->c[i].l, f->size*8);
+    }
+}
+
+void readRGB565(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel)
+{
+    uint16_t v = *(reinterpret_cast<uint16_t*>(s->data) + (x + (s->stride * y)));
+    pixel->c[0] = 0;
+    pixel->c[1] = v>>11;
+    pixel->c[2] = (v>>5)&0x3F;
+    pixel->c[3] = v&0x1F;
+    pixel->s[0] = 0;
+    pixel->s[1] = 5;
+    pixel->s[2] = 6;
+    pixel->s[3] = 5;
+}
+
+void readABGR8888(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, pixel_t* pixel)
+{
+    uint32_t v = *(reinterpret_cast<uint32_t*>(s->data) + (x + (s->stride * y)));
+    v = GGL_RGBA_TO_HOST(v);
+    pixel->c[0] = v>>24;        // A
+    pixel->c[1] = v&0xFF;       // R
+    pixel->c[2] = (v>>8)&0xFF;  // G
+    pixel->c[3] = (v>>16)&0xFF; // B
+    pixel->s[0] = 
+    pixel->s[1] = 
+    pixel->s[2] = 
+    pixel->s[3] = 8;
+}
+
+void write_pixel(const surface_t* s, context_t* c,
+        uint32_t x, uint32_t y, const pixel_t* pixel)
+{
+    assert((x < s->width) && (y < s->height));
+
+    int dither = -1;
+    if (c->state.enables & GGL_ENABLE_DITHER) {
+        dither = c->ditherMatrix[ (x & GGL_DITHER_MASK) +
+                ((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ];
+    }
+
+    const GGLFormat* f = &(c->formats[s->format]);
+    int32_t index = x + (s->stride * y);
+    uint8_t* const data = s->data + index * f->size;
+        
+    uint32_t mask = 0;
+    uint32_t v = 0;
+    for (int i=0 ; i<4 ; i++) {
+        const int component_mask = 1 << i;
+        if (f->components>=GGL_LUMINANCE &&
+                (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) {
+            // destinations L formats don't have G or B
+            continue;
+        }
+        const int l = f->c[i].l;
+        const int h = f->c[i].h;
+        if (h && (c->state.mask.color & component_mask)) {
+            mask |= (((1<<(h-l))-1)<<l);
+            uint32_t u = pixel->c[i];
+            int32_t pixelSize = pixel->s[i];
+            if (pixelSize < (h-l)) {
+                u = expand(u, pixelSize, h-l);
+                pixelSize = h-l;
+            }
+            v = downshift_component(v, u, pixelSize, 0, h, l, 0, 0, dither);
+        }
+    }
+
+    if ((c->state.mask.color != 0xF) || 
+        (c->state.enables & GGL_ENABLE_LOGIC_OP)) {
+        uint32_t d = 0;
+        switch (f->size) {
+            case 1:	d = *data;									break;
+            case 2:	d = *(uint16_t*)data;						break;
+            case 3:	d = (data[2]<<16)|(data[1]<<8)|data[0];     break;
+            case 4:	d = GGL_RGBA_TO_HOST(*(uint32_t*)data);		break;
+        }
+        if (c->state.enables & GGL_ENABLE_LOGIC_OP) {
+            v = logic_op(c->state.logic_op.opcode, v, d);            
+            v &= mask;
+        }
+        v |= (d & ~mask);
+    }
+
+    switch (f->size) {
+        case 1:		*data = v;									break;
+        case 2:		*(uint16_t*)data = v;						break;
+        case 3:
+            data[0] = v;
+            data[1] = v>>8;
+            data[2] = v>>16;
+            break;
+        case 4:		*(uint32_t*)data = GGL_HOST_TO_RGBA(v);     break;
+    }
+}
+
+static uint32_t logic_op(int op, uint32_t s, uint32_t d)
+{
+    switch(op) {
+    case GGL_CLEAR:         return 0;
+    case GGL_AND:           return s & d;
+    case GGL_AND_REVERSE:   return s & ~d;
+    case GGL_COPY:          return s;
+    case GGL_AND_INVERTED:  return ~s & d;
+    case GGL_NOOP:          return d;
+    case GGL_XOR:           return s ^ d;
+    case GGL_OR:            return s | d;
+    case GGL_NOR:           return ~(s | d);
+    case GGL_EQUIV:         return ~(s ^ d);
+    case GGL_INVERT:        return ~d;
+    case GGL_OR_REVERSE:    return s | ~d;
+    case GGL_COPY_INVERTED: return ~s;
+    case GGL_OR_INVERTED:   return ~s | d;
+    case GGL_NAND:          return ~(s & d);
+    case GGL_SET:           return ~0;
+    };
+    return s;
+}            
+
+
+uint32_t ggl_expand(uint32_t v, int sbits, int dbits)
+{
+    return expand(v, sbits, dbits);
+}
+
+uint32_t ggl_pack_color(context_t* c, int32_t format,
+        GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a)
+{
+    const GGLFormat* f = &(c->formats[format]);
+    uint32_t p = 0;
+    const int32_t hbits = GGL_COLOR_BITS;
+    const int32_t lbits = GGL_COLOR_BITS - 8;
+    p = downshift_component(p, r,   hbits, lbits,  f->rh, f->rl, 0, 1, -1);
+    p = downshift_component(p, g,   hbits, lbits,  f->gh, f->gl, 0, 1, -1);
+    p = downshift_component(p, b,   hbits, lbits,  f->bh, f->bl, 0, 1, -1);
+    p = downshift_component(p, a,   hbits, lbits,  f->ah, f->al, 0, 1, -1);
+    switch (f->size) {
+    case 1: p |= p << 8;    // fallthrough
+    case 2: p |= p << 16;
+    }
+    return p;
+}
+
+// ----------------------------------------------------------------------------
+
+// extract a component from a word
+uint32_t extract(uint32_t v, int h, int l, int bits)
+{
+	assert(h);
+	if (l) {
+		v >>= l;
+	}
+	if (h != bits) {
+		v &= (1<<(h-l))-1;
+	}
+	return v;
+}
+
+// expand a component from sbits to dbits
+uint32_t expand(uint32_t v, int sbits, int dbits)
+{
+    if (dbits > sbits) {
+        assert(sbits);
+        if (sbits==1) {
+            v = (v<<dbits) - v;
+        } else {
+            if (dbits % sbits) {
+                v <<= (dbits-sbits);
+                dbits -= sbits;
+                do {
+                    v |= v>>sbits;
+                    dbits -= sbits;
+                    sbits *= 2;
+                } while (dbits>0);
+            } else {
+                dbits -= sbits;
+                do {
+                    v |= v<<sbits;
+                    dbits -= sbits;
+                    if (sbits*2 < dbits) {
+                        sbits *= 2;
+                    }
+                } while (dbits > 0);
+            }
+        }
+    }
+	return v;
+}
+
+// downsample a component from sbits to dbits
+// and shift / construct the pixel
+uint32_t downshift_component(	uint32_t in, uint32_t v,
+                                int sh, int sl,		// src
+                                int dh, int dl,		// dst
+                                int ch, int cl,		// clear
+                                int dither)
+{
+	const int sbits = sh-sl;
+	const int dbits = dh-dl;
+    
+	assert(sbits>=dbits);
+
+
+    if (sbits>dbits) {
+        if (dither>=0) {
+            v -= (v>>dbits);				// fix up
+            const int shift = (GGL_DITHER_BITS - (sbits-dbits));
+            if (shift >= 0)   v += (dither >> shift) << sl;
+            else              v += (dither << (-shift)) << sl;
+        } else {
+            // don't do that right now, so we can reproduce the same
+            // artifacts we get on ARM (Where we don't do this)
+            // -> this is not really needed if we don't dither
+            //if (dBits > 1) { // result already OK if dBits==1
+            //    v -= (v>>dbits);				// fix up
+            //    v += 1 << ((sbits-dbits)-1);	// rounding
+            //}
+        }
+    }
+
+
+	// we need to clear the high bits of the source
+	if (ch) {
+		v <<= 32-sh;
+		sl += 32-sh;
+        sh = 32;
+	}
+	
+	if (dl) {
+		if (cl || (sbits>dbits)) {
+			v >>= sh-dbits;
+			sl = 0;
+			sh = dbits;
+            in |= v<<dl;
+		} else {
+			// sbits==dbits and we don't need to clean the lower bits
+			// so we just have to shift the component to the right location
+            int shift = dh-sh;
+            in |= v<<shift;
+		}
+	} else {
+		// destination starts at bit 0
+		// ie: sh-dh == sh-dbits
+		int shift = sh-dh;
+		if (shift > 0)      in |= v>>shift;
+		else if (shift < 0) in |= v<<shift;
+		else                in |= v;
+	}
+	return in;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libpixelflinger/buffer.h b/libpixelflinger/buffer.h
new file mode 100644
index 0000000..9c9e4bc
--- /dev/null
+++ b/libpixelflinger/buffer.h
@@ -0,0 +1,39 @@
+/* libs/pixelflinger/buffer.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGL_TEXTURE_H
+#define ANDROID_GGL_TEXTURE_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_texture(context_t* c);
+
+void ggl_set_surface(context_t* c, surface_t* dst, const GGLSurface* src);
+
+void ggl_pick_texture(context_t* c);
+void ggl_pick_cb(context_t* c);
+
+uint32_t ggl_expand(uint32_t v, int sbits, int dbits);
+uint32_t ggl_pack_color(context_t* c, int32_t format,
+            GGLcolor r, GGLcolor g, GGLcolor b, GGLcolor a);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_TEXTURE_H
diff --git a/libpixelflinger/clear.cpp b/libpixelflinger/clear.cpp
new file mode 100644
index 0000000..b962456
--- /dev/null
+++ b/libpixelflinger/clear.cpp
@@ -0,0 +1,171 @@
+/* libs/pixelflinger/clear.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/memory.h>
+
+#include "clear.h"
+#include "buffer.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static void ggl_clear(void* c, GGLbitfield mask);
+static void ggl_clearColorx(void* c,
+        GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a);        
+static void ggl_clearDepthx(void* c, GGLclampx depth);
+static void ggl_clearStencil(void* c, GGLint s);
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_clear(context_t* c)
+{
+    GGLContext& procs = *(GGLContext*)c;
+    GGL_INIT_PROC(procs, clear);
+    GGL_INIT_PROC(procs, clearColorx);
+    GGL_INIT_PROC(procs, clearDepthx);
+    GGL_INIT_PROC(procs, clearStencil);
+    c->state.clear.dirty =  GGL_STENCIL_BUFFER_BIT |
+                            GGL_COLOR_BUFFER_BIT |
+                            GGL_DEPTH_BUFFER_BIT;
+    c->state.clear.depth = FIXED_ONE;
+}
+
+// ----------------------------------------------------------------------------
+
+static void memset2d(context_t* c, const surface_t& s, uint32_t packed,
+        uint32_t l, uint32_t t, uint32_t w, uint32_t h)
+{
+    const uint32_t size = c->formats[s.format].size;
+    const int32_t stride = s.stride * size;
+    uint8_t* dst = (uint8_t*)s.data + (l + t*s.stride)*size;
+    w *= size;
+
+    if (ggl_likely(int32_t(w) == stride)) {
+        // clear the whole thing in one call
+        w *= h;
+        h = 1;
+    }
+
+    switch (size) {
+    case 1:
+        do {
+            memset(dst, packed, w);
+            dst += stride;
+        } while(--h);
+        break;
+    case 2:
+        do {
+            android_memset16((uint16_t*)dst, packed, w);
+            dst += stride;
+        } while(--h);
+        break;
+    case 3: // XXX: 24-bit clear.
+        break;
+    case 4:
+        do {
+            android_memset32((uint32_t*)dst, packed, w);
+            dst += stride;
+        } while(--h);
+        break;
+    }    
+}
+
+static inline GGLfixed fixedToZ(GGLfixed z) {
+    return GGLfixed(((int64_t(z) << 16) - z) >> 16);
+}
+
+static void ggl_clear(void* con, GGLbitfield mask)
+{
+    GGL_CONTEXT(c, con);
+
+    // XXX: rgba-dithering, rgba-masking
+    // XXX: handle all formats of Z and S
+
+    const uint32_t l = c->state.scissor.left;
+    const uint32_t t = c->state.scissor.top;
+    uint32_t w = c->state.scissor.right - l;
+    uint32_t h = c->state.scissor.bottom - t;
+
+    if (!w || !h)
+        return;
+
+    // unexsiting buffers have no effect...
+    if (c->state.buffers.color.format == 0)
+        mask &= ~GGL_COLOR_BUFFER_BIT;
+
+    if (c->state.buffers.depth.format == 0)
+        mask &= ~GGL_DEPTH_BUFFER_BIT;
+
+    if (c->state.buffers.stencil.format == 0)
+        mask &= ~GGL_STENCIL_BUFFER_BIT;
+
+    if (mask & GGL_COLOR_BUFFER_BIT) {
+        if (c->state.clear.dirty & GGL_COLOR_BUFFER_BIT) {
+            c->state.clear.dirty &= ~GGL_COLOR_BUFFER_BIT;
+
+            uint32_t colorPacked = ggl_pack_color(c,
+                    c->state.buffers.color.format,
+                    gglFixedToIteratedColor(c->state.clear.r),
+                    gglFixedToIteratedColor(c->state.clear.g),
+                    gglFixedToIteratedColor(c->state.clear.b),
+                    gglFixedToIteratedColor(c->state.clear.a));
+
+            c->state.clear.colorPacked = GGL_HOST_TO_RGBA(colorPacked);
+        }
+        const uint32_t packed = c->state.clear.colorPacked;
+        memset2d(c, c->state.buffers.color, packed, l, t, w, h);
+    }
+    if (mask & GGL_DEPTH_BUFFER_BIT) {
+        if (c->state.clear.dirty & GGL_DEPTH_BUFFER_BIT) {
+            c->state.clear.dirty &= ~GGL_DEPTH_BUFFER_BIT;
+            uint32_t depth = fixedToZ(c->state.clear.depth);
+            c->state.clear.depthPacked = (depth<<16)|depth;
+        }
+        const uint32_t packed = c->state.clear.depthPacked;
+        memset2d(c, c->state.buffers.depth, packed, l, t, w, h);
+    }
+
+    // XXX: do stencil buffer
+}
+
+static void ggl_clearColorx(void* con,
+        GGLclampx r, GGLclampx g, GGLclampx b, GGLclampx a)
+{
+    GGL_CONTEXT(c, con);
+    c->state.clear.r = gglClampx(r);
+    c->state.clear.g = gglClampx(g);
+    c->state.clear.b = gglClampx(b);
+    c->state.clear.a = gglClampx(a);
+    c->state.clear.dirty |= GGL_COLOR_BUFFER_BIT;
+}
+
+static void ggl_clearDepthx(void* con, GGLclampx depth)
+{
+    GGL_CONTEXT(c, con);
+    c->state.clear.depth = gglClampx(depth);
+    c->state.clear.dirty |= GGL_DEPTH_BUFFER_BIT;
+}
+
+static void ggl_clearStencil(void* con, GGLint s)
+{
+    GGL_CONTEXT(c, con);
+    c->state.clear.stencil = s;
+    c->state.clear.dirty |= GGL_STENCIL_BUFFER_BIT;
+}
+
+}; // namespace android
diff --git a/libpixelflinger/clear.h b/libpixelflinger/clear.h
new file mode 100644
index 0000000..b071df0
--- /dev/null
+++ b/libpixelflinger/clear.h
@@ -0,0 +1,30 @@
+/* libs/pixelflinger/clear.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#ifndef ANDROID_GGL_CLEAR_H
+#define ANDROID_GGL_CLEAR_H
+
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_clear(context_t* c);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_CLEAR_H
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
new file mode 100644
index 0000000..ff7b0b3
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -0,0 +1,428 @@
+/* libs/pixelflinger/codeflinger/ARMAssembler.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#define LOG_TAG "ARMAssembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "codeflinger/ARMAssembler.h"
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/disassem.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ARMAssembler...
+#endif
+
+ARMAssembler::ARMAssembler(const sp<Assembly>& assembly)
+    :   ARMAssemblerInterface(),
+        mAssembly(assembly)
+{
+    mBase = mPC = (uint32_t *)assembly->base();
+    mDuration = ggl_system_time();
+#if defined(WITH_LIB_HARDWARE)
+    mQemuTracing = true;
+#endif
+}
+
+ARMAssembler::~ARMAssembler()
+{
+}
+
+uint32_t* ARMAssembler::pc() const
+{
+    return mPC;
+}
+
+uint32_t* ARMAssembler::base() const
+{
+    return mBase;
+}
+
+void ARMAssembler::reset()
+{
+    mBase = mPC = (uint32_t *)mAssembly->base();
+    mBranchTargets.clear();
+    mLabels.clear();
+    mLabelsInverseMapping.clear();
+    mComments.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+void ARMAssembler::disassemble(const char* name)
+{
+    if (name) {
+        printf("%s:\n", name);
+    }
+    size_t count = pc()-base();
+    uint32_t* i = base();
+    while (count--) {
+        ssize_t label = mLabelsInverseMapping.indexOfKey(i);
+        if (label >= 0) {
+            printf("%s:\n", mLabelsInverseMapping.valueAt(label));
+        }
+        ssize_t comment = mComments.indexOfKey(i);
+        if (comment >= 0) {
+            printf("; %s\n", mComments.valueAt(comment));
+        }
+        printf("%08x:    %08x    ", int(i), int(i[0]));
+        ::disassemble((u_int)i);
+        i++;
+    }
+}
+
+void ARMAssembler::comment(const char* string)
+{
+    mComments.add(mPC, string);
+}
+
+void ARMAssembler::label(const char* theLabel)
+{
+    mLabels.add(theLabel, mPC);
+    mLabelsInverseMapping.add(mPC, theLabel);
+}
+
+void ARMAssembler::B(int cc, const char* label)
+{
+    mBranchTargets.add(branch_target_t(label, mPC));
+    *mPC++ = (cc<<28) | (0xA<<24) | 0;
+}
+
+void ARMAssembler::BL(int cc, const char* label)
+{
+    mBranchTargets.add(branch_target_t(label, mPC));
+    *mPC++ = (cc<<28) | (0xB<<24) | 0;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+
+void ARMAssembler::prolog()
+{
+    // write dummy prolog code
+    mPrologPC = mPC;
+    STM(AL, FD, SP, 1, LSAVED);
+}
+
+void ARMAssembler::epilog(uint32_t touched)
+{
+    touched &= LSAVED;
+    if (touched) {
+        // write prolog code
+        uint32_t* pc = mPC;
+        mPC = mPrologPC;
+        STM(AL, FD, SP, 1, touched | LLR);
+        mPC = pc;
+        // write epilog code
+        LDM(AL, FD, SP, 1, touched | LLR);
+        BX(AL, LR);
+    } else {   // heh, no registers to save!
+        // write prolog code
+        uint32_t* pc = mPC;
+        mPC = mPrologPC;
+        MOV(AL, 0, R0, R0); // NOP
+        mPC = pc;
+        // write epilog code
+        BX(AL, LR);
+    }
+}
+
+int ARMAssembler::generate(const char* name)
+{
+    // fixup all the branches
+    size_t count = mBranchTargets.size();
+    while (count--) {
+        const branch_target_t& bt = mBranchTargets[count];
+        uint32_t* target_pc = mLabels.valueFor(bt.label);
+        LOG_ALWAYS_FATAL_IF(!target_pc,
+                "error resolving branch targets, target_pc is null");
+        int32_t offset = int32_t(target_pc - (bt.pc+2));
+        *bt.pc |= offset & 0xFFFFFF;
+    }
+
+    mAssembly->resize( int(pc()-base())*4 );
+    
+    // the instruction cache is flushed by CodeCache
+    const int64_t duration = ggl_system_time() - mDuration;
+    const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n";
+    LOGI(format, name, int(pc()-base()), base(), pc(), duration);
+
+#if defined(WITH_LIB_HARDWARE)
+    if (__builtin_expect(mQemuTracing, 0)) {
+        int err = qemu_add_mapping(int(base()), name);
+        mQemuTracing = (err >= 0);
+    }
+#endif
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.pf.disasm", value, "0");
+    if (atoi(value) != 0) {
+        printf(format, name, int(pc()-base()), base(), pc(), duration);
+        disassemble(name);
+    }
+    
+    return NO_ERROR;
+}
+
+uint32_t* ARMAssembler::pcForLabel(const char* label)
+{
+    return mLabels.valueFor(label);
+}
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+void ARMAssembler::dataProcessing(int opcode, int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    *mPC++ = (cc<<28) | (opcode<<21) | (s<<20) | (Rn<<16) | (Rd<<12) | Op2;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply...
+void ARMAssembler::MLA(int cc, int s,
+        int Rd, int Rm, int Rs, int Rn) {
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    LOG_FATAL_IF(Rd==Rm, "MLA(r%u,r%u,r%u,r%u)", Rd,Rm,Rs,Rn);
+    *mPC++ =    (cc<<28) | (1<<21) | (s<<20) |
+                (Rd<<16) | (Rn<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::MUL(int cc, int s,
+        int Rd, int Rm, int Rs) {
+    if (Rd == Rm) { int t = Rm; Rm=Rs; Rs=t; } 
+    LOG_FATAL_IF(Rd==Rm, "MUL(r%u,r%u,r%u)", Rd,Rm,Rs);
+    *mPC++ = (cc<<28) | (s<<20) | (Rd<<16) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::UMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "UMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    *mPC++ =    (cc<<28) | (1<<23) | (s<<20) |
+                (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::UMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+                (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::SMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+                (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+void ARMAssembler::SMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+                (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+void ARMAssembler::B(int cc, uint32_t* pc)
+{
+    int32_t offset = int32_t(pc - (mPC+2));
+    *mPC++ = (cc<<28) | (0xA<<24) | (offset & 0xFFFFFF);
+}
+
+void ARMAssembler::BL(int cc, uint32_t* pc)
+{
+    int32_t offset = int32_t(pc - (mPC+2));
+    *mPC++ = (cc<<28) | (0xB<<24) | (offset & 0xFFFFFF);
+}
+
+void ARMAssembler::BX(int cc, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x12FFF10 | Rn;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfert...
+void ARMAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<26) | (1<<20) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (1<<20) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::STR(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<26) | (Rn<<16) | (Rd<<12) | offset;
+}
+void ARMAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<26) | (1<<22) | (Rn<<16) | (Rd<<12) | offset;
+}
+
+void ARMAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xB0 | offset;
+}
+void ARMAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xD0 | offset;
+}
+void ARMAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (1<<20) | (Rn<<16) | (Rd<<12) | 0xF0 | offset;
+}
+void ARMAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset) {
+    *mPC++ = (cc<<28) | (Rn<<16) | (Rd<<12) | 0xB0 | offset;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ARMAssembler::LDM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                    ED FD EA FA      IB IA DB DA
+    const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
+    const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
+    *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+            (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+}
+
+void ARMAssembler::STM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                    FA EA FD ED      IB IA DB DA
+    const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
+    const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
+    *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+            (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+}
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ARMAssembler::SWP(int cc, int Rn, int Rd, int Rm) {
+    *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+}
+void ARMAssembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+    *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+}
+void ARMAssembler::SWI(int cc, uint32_t comment) {
+    *mPC++ = (cc<<28) | (0xF<<24) | comment;
+}
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ARMAssembler::PLD(int Rn, uint32_t offset) {
+    LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+                        "PLD only P=1, W=0");
+    *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+}
+
+void ARMAssembler::CLZ(int cc, int Rd, int Rm)
+{
+    *mPC++ = (cc<<28) | 0x16F0F10| (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QADD(int cc,  int Rd, int Rm, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+}
+
+void ARMAssembler::SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs)
+{
+    *mPC++ = (cc<<28) | 0x1600080 | (Rd<<16) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMULW(int cc, int y,
+                int Rd, int Rm, int Rs)
+{
+    *mPC++ = (cc<<28) | 0x12000A0 | (Rd<<16) | (Rs<<8) | (y<<4) | Rm;
+}
+
+void ARMAssembler::SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1000080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm)
+{
+    *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+}
+
+void ARMAssembler::SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+}
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
new file mode 100644
index 0000000..8837e07
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -0,0 +1,155 @@
+/* libs/pixelflinger/codeflinger/ARMAssembler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#ifndef ANDROID_ARMASSEMBLER_H
+#define ANDROID_ARMASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+#include "tinyutils/smartpointer.h"
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssembler : public ARMAssemblerInterface
+{
+public:
+                ARMAssembler(const sp<Assembly>& assembly);
+    virtual     ~ARMAssembler();
+
+    uint32_t*   base() const;
+    uint32_t*   pc() const;
+
+
+    void        disassemble(const char* name);
+
+    // ------------------------------------------------------------------------
+    // ARMAssemblerInterface...
+    // ------------------------------------------------------------------------
+
+    virtual void    reset();
+
+    virtual int     generate(const char* name);
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+    virtual void    comment(const char* string);
+
+    virtual void    dataProcessing(int opcode, int cc, int s,
+                                int Rd, int Rn,
+                                uint32_t Op2);
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs);
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+
+    virtual void B(int cc, uint32_t* pc);
+    virtual void BL(int cc, uint32_t* pc);
+    virtual void BX(int cc, int Rn);
+    virtual void label(const char* theLabel);
+    virtual void B(int cc, const char* label);
+    virtual void BL(int cc, const char* label);
+
+    virtual uint32_t* pcForLabel(const char* label);
+
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDRSB(int cc, int Rd, 
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+
+    virtual void SWP(int cc, int Rn, int Rd, int Rm);
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+    virtual void SWI(int cc, uint32_t comment);
+
+    virtual void PLD(int Rn, uint32_t offset);
+    virtual void CLZ(int cc, int Rd, int Rm);
+    virtual void QADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs);
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs);
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm);
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn);
+
+private:
+                ARMAssembler(const ARMAssembler& rhs);
+                ARMAssembler& operator = (const ARMAssembler& rhs);
+
+    sp<Assembly>    mAssembly;
+    uint32_t*       mBase;
+    uint32_t*       mPC;
+    uint32_t*       mPrologPC;
+    int64_t         mDuration;
+#if defined(WITH_LIB_HARDWARE)
+    bool            mQemuTracing;
+#endif
+    
+    struct branch_target_t {
+        inline branch_target_t() : label(0), pc(0) { }
+        inline branch_target_t(const char* l, uint32_t* p)
+            : label(l), pc(p) { }
+        const char* label;
+        uint32_t*   pc;
+    };
+    
+    Vector<branch_target_t>                 mBranchTargets;
+    KeyedVector< const char*, uint32_t* >   mLabels;
+    KeyedVector< uint32_t*, const char* >   mLabelsInverseMapping;
+    KeyedVector< uint32_t*, const char* >   mComments;
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
new file mode 100644
index 0000000..7fa0de0
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp
@@ -0,0 +1,173 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+#include "codeflinger/ARMAssemblerInterface.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+ARMAssemblerInterface::~ARMAssemblerInterface()
+{
+}
+
+int ARMAssemblerInterface::buildImmediate(
+        uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+    rot = 0;
+    imm = immediate;
+    if (imm > 0x7F) { // skip the easy cases
+        while (!(imm&3)  || (imm&0xFC000000)) {
+            uint32_t newval;
+            newval = imm >> 2;
+            newval |= (imm&3) << 30;
+            imm = newval;
+            rot += 2;
+            if (rot == 32) {
+                rot = 0;
+                break;
+            }
+        }
+    }
+    rot = (16 - (rot>>1)) & 0xF;
+
+    if (imm>=0x100)
+        return -EINVAL;
+
+    if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate)
+        return -1;
+
+    return 0;
+}
+
+// shifters...
+
+bool ARMAssemblerInterface::isValidImmediate(uint32_t immediate)
+{
+    uint32_t rot, imm;
+    return buildImmediate(immediate, rot, imm) == 0;
+}
+
+uint32_t ARMAssemblerInterface::imm(uint32_t immediate)
+{
+    uint32_t rot, imm;
+    int err = buildImmediate(immediate, rot, imm);
+
+    LOG_ALWAYS_FATAL_IF(err==-EINVAL,
+                        "immediate %08x cannot be encoded",
+                        immediate);
+
+    LOG_ALWAYS_FATAL_IF(err,
+                        "immediate (%08x) encoding bogus!",
+                        immediate);
+
+    return (1<<25) | (rot<<8) | imm;
+}
+
+uint32_t ARMAssemblerInterface::reg_imm(int Rm, int type, uint32_t shift)
+{
+    return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF);
+}
+
+uint32_t ARMAssemblerInterface::reg_rrx(int Rm)
+{
+    return (ROR<<5) | (Rm&0xF);
+}
+
+uint32_t ARMAssemblerInterface::reg_reg(int Rm, int type, int Rs)
+{
+    return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF);
+}
+
+// addressing modes... 
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+    return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) |
+            ((W&1)<<21) | (abs(immed12)&0x7FF);
+}
+
+uint32_t ARMAssemblerInterface::immed12_post(int32_t immed12)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+
+    return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF);
+}
+
+uint32_t ARMAssemblerInterface::reg_scale_pre(int Rm, int type, 
+        uint32_t shift, int W)
+{
+    return  (1<<25) | (1<<24) | 
+            (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) |
+            reg_imm(abs(Rm), type, shift);
+}
+
+uint32_t ARMAssemblerInterface::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+    return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift);
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W)
+{
+    uint32_t offset = abs(immed8);
+
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+
+    return  (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) |
+            ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF));
+}
+
+uint32_t ARMAssemblerInterface::immed8_post(int32_t immed8)
+{
+    uint32_t offset = abs(immed8);
+
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+
+    return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) |
+            (((offset&0xF0)<<4) | (offset&0xF));
+}
+
+uint32_t ARMAssemblerInterface::reg_pre(int Rm, int W)
+{
+    return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF);
+}
+
+uint32_t ARMAssemblerInterface::reg_post(int Rm)
+{
+    return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF);
+}
+
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
new file mode 100644
index 0000000..465b3bd
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -0,0 +1,324 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerInterface.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_ARMASSEMBLER_INTERFACE_H
+#define ANDROID_ARMASSEMBLER_INTERFACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssemblerInterface
+{
+public:
+    virtual ~ARMAssemblerInterface();
+
+    enum {
+        EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+        HS = CS,
+        LO = CC
+    };
+    enum {
+        S = 1
+    };
+    enum {
+        LSL, LSR, ASR, ROR
+    };
+    enum {
+        ED, FD, EA, FA,
+        IB, IA, DB, DA
+    };
+    enum {
+        R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
+        SP = R13,
+        LR = R14,
+        PC = R15
+    };
+    enum {
+        #define LIST(rr) L##rr=1<<rr
+        LIST(R0), LIST(R1), LIST(R2), LIST(R3), LIST(R4), LIST(R5), LIST(R6),
+        LIST(R7), LIST(R8), LIST(R9), LIST(R10), LIST(R11), LIST(R12),
+        LIST(R13), LIST(R14), LIST(R15),
+        LIST(SP), LIST(LR), LIST(PC),
+        #undef LIST
+        LSAVED = LR4|LR5|LR6|LR7|LR8|LR9|LR10|LR11 | LLR
+    };
+
+    // -----------------------------------------------------------------------
+    // shifters and addressing modes
+    // -----------------------------------------------------------------------
+
+    // shifters...
+    static bool        isValidImmediate(uint32_t immed);
+    static int         buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+    static uint32_t    imm(uint32_t immediate);
+    static uint32_t    reg_imm(int Rm, int type, uint32_t shift);
+    static uint32_t    reg_rrx(int Rm);
+    static uint32_t    reg_reg(int Rm, int type, int Rs);
+
+    // addressing modes... 
+    // LDR(B)/STR(B)/PLD
+    // (immediate and Rm can be negative, which indicates U=0)
+    static uint32_t    immed12_pre(int32_t immed12, int W=0);
+    static uint32_t    immed12_post(int32_t immed12);
+    static uint32_t    reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+    static uint32_t    reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+    // LDRH/LDRSB/LDRSH/STRH
+    // (immediate and Rm can be negative, which indicates U=0)
+    static uint32_t    immed8_pre(int32_t immed8, int W=0);
+    static uint32_t    immed8_post(int32_t immed8);
+    static uint32_t    reg_pre(int Rm, int W=0);
+    static uint32_t    reg_post(int Rm);
+
+    // -----------------------------------------------------------------------
+    // basic instructions & code generation
+    // -----------------------------------------------------------------------
+
+    // generate the code
+    virtual void reset() = 0;
+    virtual int  generate(const char* name) = 0;
+    virtual void disassemble(const char* name) = 0;
+    
+    // construct prolog and epilog
+    virtual void prolog() = 0;
+    virtual void epilog(uint32_t touched) = 0;
+    virtual void comment(const char* string) = 0;
+
+    // data processing...
+    enum {
+        opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC, 
+        opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+    };
+
+    virtual void
+            dataProcessing( int opcode, int cc, int s,
+                            int Rd, int Rn,
+                            uint32_t Op2) = 0;
+    
+    // multiply...
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn) = 0;
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs) = 0;
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs) = 0;
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs) = 0;
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs) = 0;
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs) = 0;
+
+    // branches...
+    virtual void B(int cc, uint32_t* pc) = 0;
+    virtual void BL(int cc, uint32_t* pc) = 0;
+    virtual void BX(int cc, int Rn) = 0;
+
+    virtual void label(const char* theLabel) = 0;
+    virtual void B(int cc, const char* label) = 0;
+    virtual void BL(int cc, const char* label) = 0;
+
+    // valid only after generate() has been called
+    virtual uint32_t* pcForLabel(const char* label) = 0;
+
+    // data transfer...
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0)) = 0;
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0)) = 0;
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0)) = 0;
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0)) = 0;
+
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0)) = 0;
+    virtual void LDRSB(int cc, int Rd, 
+                int Rn, uint32_t offset = immed8_pre(0)) = 0;
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0)) = 0;
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0)) = 0;
+
+    // block data transfer...
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list) = 0;
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list) = 0;
+
+    // special...
+    virtual void SWP(int cc, int Rn, int Rd, int Rm) = 0;
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm) = 0;
+    virtual void SWI(int cc, uint32_t comment) = 0;
+
+    // DSP instructions...
+    enum {
+        // B=0, T=1
+        //     yx
+        xyBB = 0, // 0000,
+        xyTB = 2, // 0010,
+        xyBT = 4, // 0100,
+        xyTT = 6, // 0110,
+        yB   = 0, // 0000,
+        yT   = 4, // 0100
+    };
+
+    virtual void PLD(int Rn, uint32_t offset) = 0;
+
+    virtual void CLZ(int cc, int Rd, int Rm) = 0;
+    
+    virtual void QADD(int cc, int Rd, int Rm, int Rn) = 0;
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn) = 0;
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn) = 0;
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn) = 0;
+    
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs) = 0;
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs) = 0;
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn) = 0;
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm) = 0;
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn) = 0;
+
+    // -----------------------------------------------------------------------
+    // convenience...
+    // -----------------------------------------------------------------------
+    inline void
+    ADC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opADC, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    ADD(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opADD, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    AND(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opAND, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    BIC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opBIC, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    EOR(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opEOR, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    MOV(int cc, int s, int Rd, uint32_t Op2) {
+        dataProcessing(opMOV, cc, s, Rd, 0, Op2);
+    }
+    inline void
+    MVN(int cc, int s, int Rd, uint32_t Op2) {
+        dataProcessing(opMVN, cc, s, Rd, 0, Op2);
+    }
+    inline void
+    ORR(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opORR, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    RSB(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opRSB, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    RSC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opRSC, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    SBC(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opSBC, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    SUB(int cc, int s, int Rd, int Rn, uint32_t Op2) {
+        dataProcessing(opSUB, cc, s, Rd, Rn, Op2);
+    }
+    inline void
+    TEQ(int cc, int Rn, uint32_t Op2) {
+        dataProcessing(opTEQ, cc, 1, 0, Rn, Op2);
+    }
+    inline void
+    TST(int cc, int Rn, uint32_t Op2) {
+        dataProcessing(opTST, cc, 1, 0, Rn, Op2);
+    }
+    inline void
+    CMP(int cc, int Rn, uint32_t Op2) {
+        dataProcessing(opCMP, cc, 1, 0, Rn, Op2);
+    }
+    inline void
+    CMN(int cc, int Rn, uint32_t Op2) {
+        dataProcessing(opCMN, cc, 1, 0, Rn, Op2);
+    }
+
+    inline void SMULBB(int cc, int Rd, int Rm, int Rs) {
+        SMUL(cc, xyBB, Rd, Rm, Rs);    }
+    inline void SMULTB(int cc, int Rd, int Rm, int Rs) {
+        SMUL(cc, xyTB, Rd, Rm, Rs);    }
+    inline void SMULBT(int cc, int Rd, int Rm, int Rs) {
+        SMUL(cc, xyBT, Rd, Rm, Rs);    }
+    inline void SMULTT(int cc, int Rd, int Rm, int Rs) {
+        SMUL(cc, xyTT, Rd, Rm, Rs);    }
+
+    inline void SMULWB(int cc, int Rd, int Rm, int Rs) {
+        SMULW(cc, yB, Rd, Rm, Rs);    }
+    inline void SMULWT(int cc, int Rd, int Rm, int Rs) {
+        SMULW(cc, yT, Rd, Rm, Rs);    }
+
+    inline void
+    SMLABB(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLA(cc, xyBB, Rd, Rm, Rs, Rn);    }
+    inline void
+    SMLATB(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLA(cc, xyTB, Rd, Rm, Rs, Rn);    }
+    inline void
+    SMLABT(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLA(cc, xyBT, Rd, Rm, Rs, Rn);    }
+    inline void
+    SMLATT(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLA(cc, xyTT, Rd, Rm, Rs, Rn);    }
+
+    inline void
+    SMLALBB(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+        SMLAL(cc, xyBB, RdHi, RdLo, Rs, Rm);    }
+    inline void
+    SMLALTB(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+        SMLAL(cc, xyTB, RdHi, RdLo, Rs, Rm);    }
+    inline void
+    SMLALBT(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+        SMLAL(cc, xyBT, RdHi, RdLo, Rs, Rm);    }
+    inline void
+    SMLALTT(int cc, int RdHi, int RdLo, int Rs, int Rm) {
+        SMLAL(cc, xyTT, RdHi, RdLo, Rs, Rm);    }
+
+    inline void
+    SMLAWB(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLAW(cc, yB, Rd, Rm, Rs, Rn);    }
+    inline void
+    SMLAWT(int cc, int Rd, int Rm, int Rs, int Rn) {
+        SMLAW(cc, yT, Rd, Rm, Rs, Rn);    }
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_INTERFACE_H
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
new file mode 100644
index 0000000..18c4618
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
@@ -0,0 +1,200 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "codeflinger/ARMAssemblerProxy.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+ARMAssemblerProxy::ARMAssemblerProxy()
+    : mTarget(0)
+{
+}
+
+ARMAssemblerProxy::ARMAssemblerProxy(ARMAssemblerInterface* target)
+    : mTarget(target)
+{
+}
+
+ARMAssemblerProxy::~ARMAssemblerProxy()
+{
+    delete mTarget;
+}
+
+void ARMAssemblerProxy::setTarget(ARMAssemblerInterface* target)
+{
+    delete mTarget;
+    mTarget = target;
+}
+
+void ARMAssemblerProxy::reset() {
+    mTarget->reset();
+}
+int ARMAssemblerProxy::generate(const char* name) {
+    return mTarget->generate(name);
+}
+void ARMAssemblerProxy::disassemble(const char* name) {
+    return mTarget->disassemble(name);
+}
+void ARMAssemblerProxy::prolog() {
+    mTarget->prolog();
+}
+void ARMAssemblerProxy::epilog(uint32_t touched) {
+    mTarget->epilog(touched);
+}
+void ARMAssemblerProxy::comment(const char* string) {
+    mTarget->comment(string);
+}
+
+
+void ARMAssemblerProxy::dataProcessing( int opcode, int cc, int s,
+                                        int Rd, int Rn, uint32_t Op2)
+{
+    mTarget->dataProcessing(opcode, cc, s, Rd, Rn, Op2);
+}
+
+void ARMAssemblerProxy::MLA(int cc, int s, int Rd, int Rm, int Rs, int Rn) {
+    mTarget->MLA(cc, s, Rd, Rm, Rs, Rn);
+}
+void ARMAssemblerProxy::MUL(int cc, int s, int Rd, int Rm, int Rs) {
+    mTarget->MUL(cc, s, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::UMULL(int cc, int s,
+            int RdLo, int RdHi, int Rm, int Rs) {
+    mTarget->UMULL(cc, s, RdLo, RdHi, Rm, Rs); 
+}
+void ARMAssemblerProxy::UMUAL(int cc, int s,
+            int RdLo, int RdHi, int Rm, int Rs) {
+    mTarget->UMUAL(cc, s, RdLo, RdHi, Rm, Rs); 
+}
+void ARMAssemblerProxy::SMULL(int cc, int s,
+            int RdLo, int RdHi, int Rm, int Rs) {
+    mTarget->SMULL(cc, s, RdLo, RdHi, Rm, Rs); 
+}
+void ARMAssemblerProxy::SMUAL(int cc, int s,
+            int RdLo, int RdHi, int Rm, int Rs) {
+    mTarget->SMUAL(cc, s, RdLo, RdHi, Rm, Rs); 
+}
+
+void ARMAssemblerProxy::B(int cc, uint32_t* pc) {
+    mTarget->B(cc, pc); 
+}
+void ARMAssemblerProxy::BL(int cc, uint32_t* pc) {
+    mTarget->BL(cc, pc); 
+}
+void ARMAssemblerProxy::BX(int cc, int Rn) {
+    mTarget->BX(cc, Rn); 
+}
+void ARMAssemblerProxy::label(const char* theLabel) {
+    mTarget->label(theLabel);
+}
+void ARMAssemblerProxy::B(int cc, const char* label) {
+    mTarget->B(cc, label);
+}
+void ARMAssemblerProxy::BL(int cc, const char* label) {
+    mTarget->BL(cc, label);
+}
+
+uint32_t* ARMAssemblerProxy::pcForLabel(const char* label) {
+    return mTarget->pcForLabel(label);
+}
+
+void ARMAssemblerProxy::LDR(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->LDR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRB(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->LDRB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STR(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->STR(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STRB(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->STRB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRH(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->LDRH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRSB(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->LDRSB(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDRSH(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->LDRSH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::STRH(int cc, int Rd, int Rn, uint32_t offset) {
+    mTarget->STRH(cc, Rd, Rn, offset);
+}
+void ARMAssemblerProxy::LDM(int cc, int dir, int Rn, int W, uint32_t reg_list) {
+    mTarget->LDM(cc, dir, Rn, W, reg_list);
+}
+void ARMAssemblerProxy::STM(int cc, int dir, int Rn, int W, uint32_t reg_list) {
+    mTarget->STM(cc, dir, Rn, W, reg_list);
+}
+
+void ARMAssemblerProxy::SWP(int cc, int Rn, int Rd, int Rm) {
+    mTarget->SWP(cc, Rn, Rd, Rm);
+}
+void ARMAssemblerProxy::SWPB(int cc, int Rn, int Rd, int Rm) {
+    mTarget->SWPB(cc, Rn, Rd, Rm);
+}
+void ARMAssemblerProxy::SWI(int cc, uint32_t comment) {
+    mTarget->SWI(cc, comment);
+}
+
+
+void ARMAssemblerProxy::PLD(int Rn, uint32_t offset) {
+    mTarget->PLD(Rn, offset);
+}
+void ARMAssemblerProxy::CLZ(int cc, int Rd, int Rm) {
+    mTarget->CLZ(cc, Rd, Rm);
+}
+void ARMAssemblerProxy::QADD(int cc, int Rd, int Rm, int Rn) {
+    mTarget->QADD(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QDADD(int cc, int Rd, int Rm, int Rn) {
+    mTarget->QDADD(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QSUB(int cc, int Rd, int Rm, int Rn) {
+    mTarget->QSUB(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::QDSUB(int cc, int Rd, int Rm, int Rn) {
+    mTarget->QDSUB(cc, Rd, Rm, Rn);
+}
+void ARMAssemblerProxy::SMUL(int cc, int xy, int Rd, int Rm, int Rs) {
+    mTarget->SMUL(cc, xy, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::SMULW(int cc, int y, int Rd, int Rm, int Rs) {
+    mTarget->SMULW(cc, y, Rd, Rm, Rs);
+}
+void ARMAssemblerProxy::SMLA(int cc, int xy, int Rd, int Rm, int Rs, int Rn) {
+    mTarget->SMLA(cc, xy, Rd, Rm, Rs, Rn);
+}
+void ARMAssemblerProxy::SMLAL(  int cc, int xy,
+                                int RdHi, int RdLo, int Rs, int Rm) {
+    mTarget->SMLAL(cc, xy, RdHi, RdLo, Rs, Rm);
+}
+void ARMAssemblerProxy::SMLAW(int cc, int y, int Rd, int Rm, int Rs, int Rn) {
+    mTarget->SMLAW(cc, y, Rd, Rm, Rs, Rn);
+}
+
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
new file mode 100644
index 0000000..4bdca9c
--- /dev/null
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -0,0 +1,123 @@
+/* libs/pixelflinger/codeflinger/ARMAssemblerProxy.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_ARMASSEMBLER_PROXY_H
+#define ANDROID_ARMASSEMBLER_PROXY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class ARMAssemblerProxy : public ARMAssemblerInterface
+{
+public:
+    // ARMAssemblerProxy take ownership of the target
+
+                ARMAssemblerProxy();
+                ARMAssemblerProxy(ARMAssemblerInterface* target);
+    virtual     ~ARMAssemblerProxy();
+
+    void setTarget(ARMAssemblerInterface* target);
+
+    virtual void    reset();
+    virtual int     generate(const char* name);
+    virtual void    disassemble(const char* name);
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+    virtual void    comment(const char* string);
+
+    virtual void    dataProcessing(int opcode, int cc, int s,
+                                int Rd, int Rn,
+                                uint32_t Op2);
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs);
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+
+    virtual void B(int cc, uint32_t* pc);
+    virtual void BL(int cc, uint32_t* pc);
+    virtual void BX(int cc, int Rn);
+    virtual void label(const char* theLabel);
+    virtual void B(int cc, const char* label);
+    virtual void BL(int cc, const char* label);
+
+    uint32_t* pcForLabel(const char* label);
+
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = immed12_pre(0));
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDRSB(int cc, int Rd, 
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = immed8_pre(0));
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+
+    virtual void SWP(int cc, int Rn, int Rd, int Rm);
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+    virtual void SWI(int cc, uint32_t comment);
+
+    virtual void PLD(int Rn, uint32_t offset);
+    virtual void CLZ(int cc, int Rd, int Rm);
+    virtual void QADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs);
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs);
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm);
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn);
+
+private:
+    ARMAssemblerInterface*  mTarget;
+};
+
+}; // namespace android
+
+#endif //ANDROID_ARMASSEMBLER_PROXY_H
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
new file mode 100644
index 0000000..29410c8
--- /dev/null
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -0,0 +1,151 @@
+/* libs/pixelflinger/codeflinger/CodeCache.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+
+#include "codeflinger/CodeCache.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#if defined(__arm__)
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+// ----------------------------------------------------------------------------
+
+Assembly::Assembly(size_t size)
+    : mCount(1), mSize(0)
+{
+    mBase = (uint32_t*)malloc(size);
+    if (mBase) {
+        mSize = size;
+    }
+}
+
+Assembly::~Assembly()
+{
+    free(mBase);
+}
+
+void Assembly::incStrong(const void*) const
+{
+    android_atomic_inc(&mCount);
+}
+
+void Assembly::decStrong(const void*) const
+{
+    if (android_atomic_dec(&mCount) == 1) {
+        delete this;
+    }
+}
+
+ssize_t Assembly::size() const
+{
+    if (!mBase) return NO_MEMORY;
+    return mSize;
+}
+
+uint32_t* Assembly::base() const
+{
+    return mBase;
+}
+
+ssize_t Assembly::resize(size_t newSize)
+{
+    mBase = (uint32_t*)realloc(mBase, newSize);
+    mSize = newSize;
+    return size();
+}
+
+// ----------------------------------------------------------------------------
+
+CodeCache::CodeCache(size_t size)
+    : mCacheSize(size), mCacheInUse(0)
+{
+    pthread_mutex_init(&mLock, 0);
+}
+
+CodeCache::~CodeCache()
+{
+    pthread_mutex_destroy(&mLock);
+}
+
+sp<Assembly> CodeCache::lookup(const AssemblyKeyBase& keyBase) const
+{
+    pthread_mutex_lock(&mLock);
+    sp<Assembly> r;
+    ssize_t index = mCacheData.indexOfKey(key_t(keyBase));
+    if (index >= 0) {
+        const cache_entry_t& e = mCacheData.valueAt(index);
+        e.when = mWhen++;
+        r = e.entry;
+    }
+    pthread_mutex_unlock(&mLock);
+    return r;
+}
+
+int CodeCache::cache(  const AssemblyKeyBase& keyBase,
+                            const sp<Assembly>& assembly)
+{
+    pthread_mutex_lock(&mLock);
+
+    const ssize_t assemblySize = assembly->size();
+    while (mCacheInUse + assemblySize > mCacheSize) {
+        // evict the LRU
+        size_t lru = 0;
+        size_t count = mCacheData.size();
+        for (size_t i=0 ; i<count ; i++) {
+            const cache_entry_t& e = mCacheData.valueAt(i);
+            if (e.when < mCacheData.valueAt(lru).when) {
+                lru = i;
+            }
+        }
+        const cache_entry_t& e = mCacheData.valueAt(lru);
+        mCacheInUse -= e.entry->size();
+        mCacheData.removeItemsAt(lru);
+    }
+
+    ssize_t err = mCacheData.add(key_t(keyBase), cache_entry_t(assembly, mWhen));
+    if (err >= 0) {
+        mCacheInUse += assemblySize;
+        mWhen++;
+        // synchronize caches...
+#if defined(__arm__)
+        const long base = long(assembly->base());
+        const long curr = base + long(assembly->size());
+        err = cacheflush(base, curr, 0);
+        LOGE_IF(err, "__ARM_NR_cacheflush error %s\n",
+                strerror(errno));
+#endif
+    }
+
+    pthread_mutex_unlock(&mLock);
+    return err;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
new file mode 100644
index 0000000..370ce17
--- /dev/null
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -0,0 +1,134 @@
+/* libs/pixelflinger/codeflinger/CodeCache.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_CODECACHE_H
+#define ANDROID_CODECACHE_H
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include <utils/KeyedVector.h>
+
+#include "tinyutils/smartpointer.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class AssemblyKeyBase {
+public:
+    virtual ~AssemblyKeyBase() { }
+    virtual int compare_type(const AssemblyKeyBase& key) const = 0;
+};
+
+template  <typename T>
+class AssemblyKey : public AssemblyKeyBase
+{
+public:
+    AssemblyKey(const T& rhs) : mKey(rhs) { }
+    virtual int compare_type(const AssemblyKeyBase& key) const {
+        const T& rhs = static_cast<const AssemblyKey&>(key).mKey;
+        return android::compare_type(mKey, rhs);
+    }
+private:
+    T mKey;
+};
+
+// ----------------------------------------------------------------------------
+
+class Assembly
+{
+public:
+                Assembly(size_t size);
+    virtual     ~Assembly();
+
+    ssize_t     size() const;
+    uint32_t*   base() const;
+    ssize_t     resize(size_t size);
+
+    // protocol for sp<>
+            void    incStrong(const void* id) const;
+            void    decStrong(const void* id) const;
+    typedef void    weakref_type;
+
+private:
+    mutable int32_t     mCount;
+            uint32_t*   mBase;
+            ssize_t     mSize;
+};
+
+// ----------------------------------------------------------------------------
+
+class CodeCache
+{
+public:
+// pretty simple cache API...
+                CodeCache(size_t size);
+                ~CodeCache();
+    
+            sp<Assembly>        lookup(const AssemblyKeyBase& key) const;
+
+            int                 cache(  const AssemblyKeyBase& key,
+                                        const sp<Assembly>& assembly);
+
+private:
+    // nothing to see here...
+    struct cache_entry_t {
+        inline cache_entry_t() { }
+        inline cache_entry_t(const sp<Assembly>& a, int64_t w)
+                : entry(a), when(w) { }
+        sp<Assembly>            entry;
+        mutable int64_t         when;
+    };
+
+    class key_t {
+        friend int compare_type(
+            const key_value_pair_t<key_t, cache_entry_t>&,
+            const key_value_pair_t<key_t, cache_entry_t>&);
+        const AssemblyKeyBase* mKey;
+    public:
+        key_t() { };
+        key_t(const AssemblyKeyBase& k) : mKey(&k)  { }
+    };
+
+    mutable pthread_mutex_t             mLock;
+    mutable int64_t                     mWhen;
+    size_t                              mCacheSize;
+    size_t                              mCacheInUse;
+    KeyedVector<key_t, cache_entry_t>   mCacheData;
+
+    friend int compare_type(
+        const key_value_pair_t<key_t, cache_entry_t>&,
+        const key_value_pair_t<key_t, cache_entry_t>&);
+};
+
+// KeyedVector uses compare_type(), which is more efficient, than
+// just using operator < ()
+inline int compare_type(
+    const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& lhs,
+    const key_value_pair_t<CodeCache::key_t, CodeCache::cache_entry_t>& rhs)
+{
+    return lhs.key.mKey->compare_type(*(rhs.key.mKey));
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif //ANDROID_CODECACHE_H
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
new file mode 100644
index 0000000..1cd189c
--- /dev/null
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -0,0 +1,1150 @@
+/* libs/pixelflinger/codeflinger/GGLAssembler.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#define LOG_TAG "GGLAssembler"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "codeflinger/GGLAssembler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+GGLAssembler::GGLAssembler(ARMAssemblerInterface* target)
+    : ARMAssemblerProxy(target), RegisterAllocator(), mOptLevel(7)
+{
+}
+
+GGLAssembler::~GGLAssembler()
+{
+}
+
+void GGLAssembler::prolog()
+{
+    ARMAssemblerProxy::prolog();
+}
+
+void GGLAssembler::epilog(uint32_t touched)
+{
+    ARMAssemblerProxy::epilog(touched);
+}
+
+void GGLAssembler::reset(int opt_level)
+{
+    ARMAssemblerProxy::reset();
+    RegisterAllocator::reset();
+    mOptLevel = opt_level;
+}
+
+// ---------------------------------------------------------------------------
+
+int GGLAssembler::scanline(const needs_t& needs, context_t const* c)
+{
+    int err = 0;
+    int opt_level = mOptLevel;
+    while (opt_level >= 0) {
+        reset(opt_level);
+        err = scanline_core(needs, c);
+        if (err == 0)
+            break;
+        opt_level--;
+    }
+    
+    // XXX: in theory, pcForLabel is not valid before generate()
+    uint32_t* fragment_start_pc = pcForLabel("fragment_loop");
+    uint32_t* fragment_end_pc = pcForLabel("epilog");
+    const int per_fragment_ops = int(fragment_end_pc - fragment_start_pc);
+    
+    // build a name for our pipeline
+    char name[64];    
+    sprintf(name,
+            "scanline__%08X:%08X_%08X_%08X [%3d ipp]",
+            needs.p, needs.n, needs.t[0], needs.t[1], per_fragment_ops);
+
+    if (err) {
+        LOGE("Error while generating ""%s""\n", name);
+        disassemble(name);
+        return -1;
+    }
+
+    return generate(name);
+}
+
+int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c)
+{
+    int64_t duration = ggl_system_time();
+
+    mBlendFactorCached = 0;
+    mBlending = 0;
+    mMasking = 0;
+    mAA        = GGL_READ_NEEDS(P_AA, needs.p);
+    mDithering = GGL_READ_NEEDS(P_DITHER, needs.p);
+    mAlphaTest = GGL_READ_NEEDS(P_ALPHA_TEST, needs.p) + GGL_NEVER;
+    mDepthTest = GGL_READ_NEEDS(P_DEPTH_TEST, needs.p) + GGL_NEVER;
+    mFog       = GGL_READ_NEEDS(P_FOG, needs.p) != 0;
+    mSmooth    = GGL_READ_NEEDS(SHADE, needs.n) != 0;
+    mBuilderContext.needs = needs;
+    mBuilderContext.c = c;
+    mBuilderContext.Rctx = reserveReg(R0); // context always in R0
+    mCbFormat = c->formats[ GGL_READ_NEEDS(CB_FORMAT, needs.n) ];
+
+    // ------------------------------------------------------------------------
+
+    decodeLogicOpNeeds(needs);
+
+    decodeTMUNeeds(needs, c);
+
+    mBlendSrc  = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRC, needs.n));
+    mBlendDst  = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DST, needs.n));
+    mBlendSrcA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRCA, needs.n));
+    mBlendDstA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DSTA, needs.n));
+
+    if (!mCbFormat.c[GGLFormat::ALPHA].h) {
+        if ((mBlendSrc == GGL_ONE_MINUS_DST_ALPHA) ||
+            (mBlendSrc == GGL_DST_ALPHA)) {
+            mBlendSrc = GGL_ONE;
+        }
+        if ((mBlendSrcA == GGL_ONE_MINUS_DST_ALPHA) ||
+            (mBlendSrcA == GGL_DST_ALPHA)) {
+            mBlendSrcA = GGL_ONE;
+        }
+        if ((mBlendDst == GGL_ONE_MINUS_DST_ALPHA) ||
+            (mBlendDst == GGL_DST_ALPHA)) {
+            mBlendDst = GGL_ONE;
+        }
+        if ((mBlendDstA == GGL_ONE_MINUS_DST_ALPHA) ||
+            (mBlendDstA == GGL_DST_ALPHA)) {
+            mBlendDstA = GGL_ONE;
+        }
+    }
+
+    // if we need the framebuffer, read it now
+    const int blending =    blending_codes(mBlendSrc, mBlendDst) |
+                            blending_codes(mBlendSrcA, mBlendDstA);
+
+    // XXX: handle special cases, destination not modified...
+    if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) &&
+        (mBlendDst==GGL_ONE) && (mBlendDstA==GGL_ONE)) {
+        // Destination unmodified (beware of logic ops)
+    } else if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) &&
+        (mBlendDst==GGL_ZERO) && (mBlendDstA==GGL_ZERO)) {
+        // Destination is zero (beware of logic ops)
+    }
+    
+    int fbComponents = 0;
+    const int masking = GGL_READ_NEEDS(MASK_ARGB, needs.n);
+    for (int i=0 ; i<4 ; i++) {
+        const int mask = 1<<i;
+        component_info_t& info = mInfo[i];
+        int fs = i==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+        int fd = i==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+        if (fs==GGL_SRC_ALPHA_SATURATE && i==GGLFormat::ALPHA)
+            fs = GGL_ONE;
+        info.masked =   !!(masking & mask);
+        info.inDest =   !info.masked && mCbFormat.c[i].h && 
+                        ((mLogicOp & LOGIC_OP_SRC) || (!mLogicOp));
+        if (mCbFormat.components >= GGL_LUMINANCE &&
+                (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) {
+            info.inDest = false;
+        }
+        info.needed =   (i==GGLFormat::ALPHA) && 
+                        (isAlphaSourceNeeded() || mAlphaTest != GGL_ALWAYS);
+        info.replaced = !!(mTextureMachine.replaced & mask);
+        info.iterated = (!info.replaced && (info.inDest || info.needed)); 
+        info.smooth =   mSmooth && info.iterated;
+        info.fog =      mFog && info.inDest && (i != GGLFormat::ALPHA);
+        info.blend =    (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO));
+
+        mBlending |= (info.blend ? mask : 0);
+        mMasking |= (mCbFormat.c[i].h && info.masked) ? mask : 0;
+        fbComponents |= mCbFormat.c[i].h ? mask : 0;
+    }
+
+    mAllMasked = (mMasking == fbComponents);
+    if (mAllMasked) {
+        mDithering = 0;
+    }
+    
+    fragment_parts_t parts;
+
+    // ------------------------------------------------------------------------
+    prolog();
+    // ------------------------------------------------------------------------
+
+    build_scanline_prolog(parts, needs);
+
+    if (registerFile().status())
+        return registerFile().status();
+
+    // ------------------------------------------------------------------------
+    label("fragment_loop");
+    // ------------------------------------------------------------------------
+    {
+        Scratch regs(registerFile());
+
+        if (mDithering) {
+            // update the dither index.
+            MOV(AL, 0, parts.count.reg,
+                    reg_imm(parts.count.reg, ROR, GGL_DITHER_ORDER_SHIFT));
+            ADD(AL, 0, parts.count.reg, parts.count.reg,
+                    imm( 1 << (32 - GGL_DITHER_ORDER_SHIFT)));
+            MOV(AL, 0, parts.count.reg,
+                    reg_imm(parts.count.reg, ROR, 32 - GGL_DITHER_ORDER_SHIFT));
+        }
+
+        // XXX: could we do an early alpha-test here in some cases?
+        // It would probaly be used only with smooth-alpha and no texture
+        // (or no alpha component in the texture).
+
+        // Early z-test
+        if (mAlphaTest==GGL_ALWAYS) {
+            build_depth_test(parts, Z_TEST|Z_WRITE);
+        } else {
+            // we cannot do the z-write here, because
+            // it might be killed by the alpha-test later
+            build_depth_test(parts, Z_TEST);
+        }
+
+        { // texture coordinates
+            Scratch scratches(registerFile());
+
+            // texel generation
+            build_textures(parts, regs);
+        }        
+
+        if ((blending & (FACTOR_DST|BLEND_DST)) || 
+                (mMasking && !mAllMasked) ||
+                (mLogicOp & LOGIC_OP_DST)) 
+        {
+            // blending / logic_op / masking need the framebuffer
+            mDstPixel.setTo(regs.obtain(), &mCbFormat);
+
+            // load the framebuffer pixel
+            comment("fetch color-buffer");
+            load(parts.cbPtr, mDstPixel);
+        }
+
+        if (registerFile().status())
+            return registerFile().status();
+
+        pixel_t pixel;
+        int directTex = mTextureMachine.directTexture;
+        if (directTex | parts.packed) {
+            // note: we can't have both here
+            // iterated color or direct texture
+            pixel = directTex ? parts.texel[directTex-1] : parts.iterated;
+            pixel.flags &= ~CORRUPTIBLE;
+        } else {
+            if (mDithering) {
+                const int ctxtReg = mBuilderContext.Rctx;
+                const int mask = GGL_DITHER_SIZE-1;
+                parts.dither = reg_t(regs.obtain());
+                AND(AL, 0, parts.dither.reg, parts.count.reg, imm(mask));
+                ADD(AL, 0, parts.dither.reg, parts.dither.reg, ctxtReg);
+                LDRB(AL, parts.dither.reg, parts.dither.reg,
+                        immed12_pre(GGL_OFFSETOF(ditherMatrix)));
+            }
+        
+            // allocate a register for the resulting pixel
+            pixel.setTo(regs.obtain(), &mCbFormat, FIRST);
+
+            build_component(pixel, parts, GGLFormat::ALPHA,    regs);
+
+            if (mAlphaTest!=GGL_ALWAYS) {
+                // only handle the z-write part here. We know z-test
+                // was successful, as well as alpha-test.
+                build_depth_test(parts, Z_WRITE);
+            }
+
+            build_component(pixel, parts, GGLFormat::RED,      regs);
+            build_component(pixel, parts, GGLFormat::GREEN,    regs);
+            build_component(pixel, parts, GGLFormat::BLUE,     regs);
+
+            pixel.flags |= CORRUPTIBLE;
+        }
+
+        if (registerFile().status())
+            return registerFile().status();
+        
+        if (pixel.reg == -1) {
+            // be defensive here. if we're here it's probably
+            // that this whole fragment is a no-op.
+            pixel = mDstPixel;
+        }
+        
+        if (!mAllMasked) {
+            // logic operation
+            build_logic_op(pixel, regs);
+    
+            // masking
+            build_masking(pixel, regs); 
+    
+            comment("store");
+            store(parts.cbPtr, pixel, WRITE_BACK);
+        }
+    }
+
+    if (registerFile().status())
+        return registerFile().status();
+
+    // update the iterated color...
+    if (parts.reload != 3) {
+        build_smooth_shade(parts);
+    }
+
+    // update iterated z
+    build_iterate_z(parts);
+
+    // update iterated fog
+    build_iterate_f(parts);
+
+    SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16));
+    B(PL, "fragment_loop");
+    label("epilog");
+    epilog(registerFile().touched());
+
+    if ((mAlphaTest!=GGL_ALWAYS) || (mDepthTest!=GGL_ALWAYS)) {
+        if (mDepthTest!=GGL_ALWAYS) {
+            label("discard_before_textures");
+            build_iterate_texture_coordinates(parts);
+        }
+        label("discard_after_textures");
+        build_smooth_shade(parts);
+        build_iterate_z(parts);
+        build_iterate_f(parts);
+        if (!mAllMasked) {
+            ADD(AL, 0, parts.cbPtr.reg, parts.cbPtr.reg, imm(parts.cbPtr.size>>3));
+        }
+        SUB(AL, S, parts.count.reg, parts.count.reg, imm(1<<16));
+        B(PL, "fragment_loop");
+        epilog(registerFile().touched());
+    }
+
+    return registerFile().status();
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_scanline_prolog(
+    fragment_parts_t& parts, const needs_t& needs)
+{
+    Scratch scratches(registerFile());
+    int Rctx = mBuilderContext.Rctx;
+
+    // compute count
+    comment("compute ct (# of pixels to process)");
+    parts.count.setTo(obtainReg());
+    int Rx = scratches.obtain();    
+    int Ry = scratches.obtain();
+    CONTEXT_LOAD(Rx, iterators.xl);
+    CONTEXT_LOAD(parts.count.reg, iterators.xr);
+    CONTEXT_LOAD(Ry, iterators.y);
+
+    // parts.count = iterators.xr - Rx
+    SUB(AL, 0, parts.count.reg, parts.count.reg, Rx);
+    SUB(AL, 0, parts.count.reg, parts.count.reg, imm(1));
+
+    if (mDithering) {
+        // parts.count.reg = 0xNNNNXXDD
+        // NNNN = count-1
+        // DD   = dither offset
+        // XX   = 0xxxxxxx (x = garbage)
+        Scratch scratches(registerFile());
+        int tx = scratches.obtain();
+        int ty = scratches.obtain();
+        AND(AL, 0, tx, Rx, imm(GGL_DITHER_MASK));
+        AND(AL, 0, ty, Ry, imm(GGL_DITHER_MASK));
+        ADD(AL, 0, tx, tx, reg_imm(ty, LSL, GGL_DITHER_ORDER_SHIFT));
+        ORR(AL, 0, parts.count.reg, tx, reg_imm(parts.count.reg, LSL, 16));
+    } else {
+        // parts.count.reg = 0xNNNN0000
+        // NNNN = count-1
+        MOV(AL, 0, parts.count.reg, reg_imm(parts.count.reg, LSL, 16));
+    }
+
+    if (!mAllMasked) {
+        // compute dst ptr
+        comment("compute color-buffer pointer");
+        const int cb_bits = mCbFormat.size*8;
+        int Rs = scratches.obtain();
+        parts.cbPtr.setTo(obtainReg(), cb_bits);
+        CONTEXT_LOAD(Rs, state.buffers.color.stride);
+        CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data);
+        SMLABB(AL, Rs, Ry, Rs, Rx);  // Rs = Rx + Ry*Rs
+        base_offset(parts.cbPtr, parts.cbPtr, Rs);
+        scratches.recycle(Rs);
+    }
+    
+    // init fog
+    const int need_fog = GGL_READ_NEEDS(P_FOG, needs.p);
+    if (need_fog) {
+        comment("compute initial fog coordinate");
+        Scratch scratches(registerFile());
+        int dfdx = scratches.obtain();
+        int ydfdy = scratches.obtain();
+        int f = ydfdy;
+        CONTEXT_LOAD(dfdx,  generated_vars.dfdx);
+        CONTEXT_LOAD(ydfdy, iterators.ydfdy);
+        MLA(AL, 0, f, Rx, dfdx, ydfdy);
+        CONTEXT_STORE(f, generated_vars.f);
+    }
+
+    // init Z coordinate
+    if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) {
+        parts.z = reg_t(obtainReg());
+        comment("compute initial Z coordinate");
+        Scratch scratches(registerFile());
+        int dzdx = scratches.obtain();
+        int ydzdy = parts.z.reg;
+        CONTEXT_LOAD(dzdx,  generated_vars.dzdx);   // 1.31 fixed-point
+        CONTEXT_LOAD(ydzdy, iterators.ydzdy);       // 1.31 fixed-point
+        MLA(AL, 0, parts.z.reg, Rx, dzdx, ydzdy);
+
+        // we're going to index zbase of parts.count
+        // zbase = base + (xl-count + stride*y)*2
+        int Rs = dzdx;
+        int zbase = scratches.obtain();
+        CONTEXT_LOAD(Rs, state.buffers.depth.stride);
+        CONTEXT_LOAD(zbase, state.buffers.depth.data);
+        SMLABB(AL, Rs, Ry, Rs, Rx);
+        ADD(AL, 0, Rs, Rs, reg_imm(parts.count.reg, LSR, 16));
+        ADD(AL, 0, zbase, zbase, reg_imm(Rs, LSL, 1));
+        CONTEXT_STORE(zbase, generated_vars.zbase);
+    }
+
+    // init texture coordinates
+    init_textures(parts.coords, reg_t(Rx), reg_t(Ry));
+    scratches.recycle(Ry);
+
+    // iterated color
+    init_iterated_color(parts, reg_t(Rx));
+
+    // init coverage factor application (anti-aliasing)
+    if (mAA) {
+        parts.covPtr.setTo(obtainReg(), 16);
+        CONTEXT_LOAD(parts.covPtr.reg, state.buffers.coverage);
+        ADD(AL, 0, parts.covPtr.reg, parts.covPtr.reg, reg_imm(Rx, LSL, 1));
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_component( pixel_t& pixel,
+                                    const fragment_parts_t& parts,
+                                    int component,
+                                    Scratch& regs)
+{
+    static char const * comments[] = {"alpha", "red", "green", "blue"};
+    comment(comments[component]);
+
+    // local register file
+    Scratch scratches(registerFile());
+    const int dst_component_size = pixel.component_size(component);
+
+    component_t temp(-1);
+    build_incoming_component( temp, dst_component_size,
+            parts, component, scratches, regs);
+
+    if (mInfo[component].inDest) {
+
+        // blending...
+        build_blending( temp, mDstPixel, component, scratches );
+
+        // downshift component and rebuild pixel...
+        downshift(pixel, component, temp, parts.dither);
+    }
+}
+
+void GGLAssembler::build_incoming_component(
+                                    component_t& temp,
+                                    int dst_size,
+                                    const fragment_parts_t& parts,
+                                    int component,
+                                    Scratch& scratches,
+                                    Scratch& global_regs)
+{
+    const uint32_t component_mask = 1<<component;
+
+    // Figure out what we need for the blending stage...
+    int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+    int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+    if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) {
+        fs = GGL_ONE;
+    }
+
+    // Figure out what we need to extract and for what reason
+    const int blending = blending_codes(fs, fd);
+
+    // Are we actually going to blend?
+    const int need_blending = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO));
+    
+    // expand the source if the destination has more bits
+    int need_expander = false;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT-1 ; i++) {
+        texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if ((tmu.format_idx) &&
+            (parts.texel[i].component_size(component) < dst_size)) {
+            need_expander = true;
+        }
+    }
+
+    // do we need to extract this component?
+    const bool multiTexture = mTextureMachine.activeUnits > 1;
+    const int blend_needs_alpha_source = (component==GGLFormat::ALPHA) &&
+                                        (isAlphaSourceNeeded());
+    int need_extract = mInfo[component].needed;
+    if (mInfo[component].inDest)
+    {
+        need_extract |= ((need_blending ?
+                (blending & (BLEND_SRC|FACTOR_SRC)) : need_expander));
+        need_extract |= (mTextureMachine.mask != mTextureMachine.replaced);
+        need_extract |= mInfo[component].smooth;
+        need_extract |= mInfo[component].fog;
+        need_extract |= mDithering;
+        need_extract |= multiTexture;
+    }
+
+    if (need_extract) {
+        Scratch& regs = blend_needs_alpha_source ? global_regs : scratches;
+        component_t fragment;
+
+        // iterated color
+        build_iterated_color(fragment, parts, component, regs);
+
+        // texture environement (decal, modulate, replace)
+        build_texture_environment(fragment, parts, component, regs);
+
+        // expand the source if the destination has more bits
+        if (need_expander && (fragment.size() < dst_size)) {
+            // we're here only if we fetched a texel
+            // (so we know for sure fragment is CORRUPTIBLE)
+            expand(fragment, fragment, dst_size);
+        }
+
+        // We have a few specific things to do for the alpha-channel
+        if ((component==GGLFormat::ALPHA) &&
+            (mInfo[component].needed || fragment.size()<dst_size))
+        {
+            // convert to integer_t first and make sure
+            // we don't corrupt a needed register
+            if (fragment.l) {
+                component_t incoming(fragment);
+                modify(fragment, regs);
+                MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSR, incoming.l));
+                fragment.h -= fragment.l;
+                fragment.l = 0;
+            }
+
+            // coverage factor application
+            build_coverage_application(fragment, parts, regs);
+
+            // alpha-test
+            build_alpha_test(fragment, parts);
+
+            if (blend_needs_alpha_source) {
+                // We keep only 8 bits for the blending stage
+                const int shift = fragment.h <= 8 ? 0 : fragment.h-8;
+                if (fragment.flags & CORRUPTIBLE) {
+                    fragment.flags &= ~CORRUPTIBLE;
+                    mAlphaSource.setTo(fragment.reg,
+                            fragment.size(), fragment.flags);
+                    if (shift) {
+                        MOV(AL, 0, mAlphaSource.reg,
+                            reg_imm(mAlphaSource.reg, LSR, shift));
+                    }
+                } else {
+                    // XXX: it would better to do this in build_blend_factor()
+                    // so we can avoid the extra MOV below.
+                    mAlphaSource.setTo(regs.obtain(),
+                            fragment.size(), CORRUPTIBLE);
+                    if (shift) {
+                        MOV(AL, 0, mAlphaSource.reg,
+                            reg_imm(fragment.reg, LSR, shift));
+                    } else {
+                        MOV(AL, 0, mAlphaSource.reg, fragment.reg);
+                    }
+                }
+                mAlphaSource.s -= shift;
+            }
+        }
+
+        // fog...
+        build_fog( fragment, component, regs );
+
+        temp = fragment;
+    } else {
+        if (mInfo[component].inDest) {
+            // extraction not needed and replace
+            // we just select the right component
+            if ((mTextureMachine.replaced & component_mask) == 0) {
+                // component wasn't replaced, so use it!
+                temp = component_t(parts.iterated, component);
+            }
+            for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+                const texture_unit_t& tmu = mTextureMachine.tmu[i];
+                if ((tmu.mask & component_mask) &&
+                    ((tmu.replaced & component_mask) == 0)) {
+                    temp = component_t(parts.texel[i], component);
+                }
+            }
+        }
+    }
+}
+
+bool GGLAssembler::isAlphaSourceNeeded() const
+{
+    // XXX: also needed for alpha-test
+    const int bs = mBlendSrc;
+    const int bd = mBlendDst;
+    return  bs==GGL_SRC_ALPHA_SATURATE ||
+            bs==GGL_SRC_ALPHA || bs==GGL_ONE_MINUS_SRC_ALPHA ||
+            bd==GGL_SRC_ALPHA || bd==GGL_ONE_MINUS_SRC_ALPHA ; 
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_smooth_shade(const fragment_parts_t& parts)
+{
+    if (mSmooth && !parts.iterated_packed) {
+        // update the iterated color in a pipelined way...
+        comment("update iterated color");
+        Scratch scratches(registerFile());
+
+        const int reload = parts.reload;
+        for (int i=0 ; i<4 ; i++) {
+            if (!mInfo[i].iterated) 
+                continue;
+                
+            int c = parts.argb[i].reg;
+            int dx = parts.argb_dx[i].reg;
+            
+            if (reload & 1) {
+                c = scratches.obtain();
+                CONTEXT_LOAD(c, generated_vars.argb[i].c);
+            }
+            if (reload & 2) {
+                dx = scratches.obtain();
+                CONTEXT_LOAD(dx, generated_vars.argb[i].dx);
+            }
+            
+            if (mSmooth) {
+                ADD(AL, 0, c, c, dx);
+            }
+            
+            if (reload & 1) {
+                CONTEXT_STORE(c, generated_vars.argb[i].c);
+                scratches.recycle(c);
+            }
+            if (reload & 2) {
+                scratches.recycle(dx);
+            }
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_coverage_application(component_t& fragment,
+        const fragment_parts_t& parts, Scratch& regs)
+{
+    // here fragment.l is guarenteed to be 0
+    if (mAA) {
+        // coverages are 1.15 fixed-point numbers
+        comment("coverage application");
+
+        component_t incoming(fragment);
+        modify(fragment, regs);
+
+        Scratch scratches(registerFile());
+        int cf = scratches.obtain();
+        LDRH(AL, cf, parts.covPtr.reg, immed8_post(2));
+        if (fragment.h > 31) {
+            fragment.h--;
+            SMULWB(AL, fragment.reg, incoming.reg, cf);
+        } else {
+            MOV(AL, 0, fragment.reg, reg_imm(incoming.reg, LSL, 1));
+            SMULWB(AL, fragment.reg, fragment.reg, cf);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_alpha_test(component_t& fragment,
+                                    const fragment_parts_t& parts)
+{
+    if (mAlphaTest != GGL_ALWAYS) {
+        comment("Alpha Test");
+        Scratch scratches(registerFile());
+        int ref = scratches.obtain();
+        const int shift = GGL_COLOR_BITS-fragment.size();
+        CONTEXT_LOAD(ref, state.alpha_test.ref);
+        if (shift) CMP(AL, fragment.reg, reg_imm(ref, LSR, shift));
+        else       CMP(AL, fragment.reg, ref);
+        int cc = NV;
+        switch (mAlphaTest) {
+        case GGL_NEVER:     cc = NV;    break;
+        case GGL_LESS:      cc = LT;    break;
+        case GGL_EQUAL:     cc = EQ;    break;
+        case GGL_LEQUAL:    cc = LS;    break;
+        case GGL_GREATER:   cc = HI;    break;
+        case GGL_NOTEQUAL:  cc = NE;    break;
+        case GGL_GEQUAL:    cc = HS;    break;
+        }
+        B(cc^1, "discard_after_textures");
+    }
+}
+
+// ---------------------------------------------------------------------------
+            
+void GGLAssembler::build_depth_test(
+        const fragment_parts_t& parts, uint32_t mask)
+{
+    mask &= Z_TEST|Z_WRITE;
+    const needs_t& needs = mBuilderContext.needs;
+    const int zmask = GGL_READ_NEEDS(P_MASK_Z, needs.p);
+    Scratch scratches(registerFile());
+
+    if (mDepthTest != GGL_ALWAYS || zmask) {
+        int cc=AL, ic=AL;
+        switch (mDepthTest) {
+        case GGL_LESS:      ic = HI;    break;
+        case GGL_EQUAL:     ic = EQ;    break;
+        case GGL_LEQUAL:    ic = HS;    break;
+        case GGL_GREATER:   ic = LT;    break;
+        case GGL_NOTEQUAL:  ic = NE;    break;
+        case GGL_GEQUAL:    ic = LS;    break;
+        case GGL_NEVER:
+            // this never happens, because it's taken care of when 
+            // computing the needs. but we keep it for completness.
+            comment("Depth Test (NEVER)");
+            B(AL, "discard_before_textures");
+            return;
+        case GGL_ALWAYS:
+            // we're here because zmask is enabled
+            mask &= ~Z_TEST;    // test always passes.
+            break;
+        }
+        
+        // inverse the condition
+        cc = ic^1;
+        
+        if ((mask & Z_WRITE) && !zmask) {
+            mask &= ~Z_WRITE;
+        }
+        
+        if (!mask)
+            return;
+
+        comment("Depth Test");
+
+        int zbase = scratches.obtain();
+        int depth = scratches.obtain();
+        int z = parts.z.reg;
+        
+        CONTEXT_LOAD(zbase, generated_vars.zbase);  // stall
+        SUB(AL, 0, zbase, zbase, reg_imm(parts.count.reg, LSR, 15));
+            // above does zbase = zbase + ((count >> 16) << 1)
+
+        if (mask & Z_TEST) {
+            LDRH(AL, depth, zbase);  // stall
+            CMP(AL, depth, reg_imm(z, LSR, 16));
+            B(cc, "discard_before_textures");
+        }
+        if (mask & Z_WRITE) {
+            if (mask == Z_WRITE) {
+                // only z-write asked, cc is meaningless
+                ic = AL;
+            }
+            MOV(AL, 0, depth, reg_imm(z, LSR, 16));
+            STRH(ic, depth, zbase);
+        }
+    }
+}
+
+void GGLAssembler::build_iterate_z(const fragment_parts_t& parts)
+{
+    const needs_t& needs = mBuilderContext.needs;
+    if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) {
+        Scratch scratches(registerFile());
+        int dzdx = scratches.obtain();
+        CONTEXT_LOAD(dzdx, generated_vars.dzdx);    // stall
+        ADD(AL, 0, parts.z.reg, parts.z.reg, dzdx); 
+    }
+}
+
+void GGLAssembler::build_iterate_f(const fragment_parts_t& parts)
+{
+    const needs_t& needs = mBuilderContext.needs;
+    if (GGL_READ_NEEDS(P_FOG, needs.p)) {
+        Scratch scratches(registerFile());
+        int dfdx = scratches.obtain();
+        int f = scratches.obtain();
+        CONTEXT_LOAD(f,     generated_vars.f);
+        CONTEXT_LOAD(dfdx,  generated_vars.dfdx);   // stall
+        ADD(AL, 0, f, f, dfdx);
+        CONTEXT_STORE(f,    generated_vars.f);
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_logic_op(pixel_t& pixel, Scratch& regs)
+{
+    const needs_t& needs = mBuilderContext.needs;
+    const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+    if (opcode == GGL_COPY)
+        return;
+    
+    comment("logic operation");
+
+    pixel_t s(pixel);
+    if (!(pixel.flags & CORRUPTIBLE)) {
+        pixel.reg = regs.obtain();
+        pixel.flags |= CORRUPTIBLE;
+    }
+    
+    pixel_t d(mDstPixel);
+    switch(opcode) {
+    case GGL_CLEAR:         MOV(AL, 0, pixel.reg, imm(0));          break;
+    case GGL_AND:           AND(AL, 0, pixel.reg, s.reg, d.reg);    break;
+    case GGL_AND_REVERSE:   BIC(AL, 0, pixel.reg, s.reg, d.reg);    break;
+    case GGL_COPY:                                                  break;
+    case GGL_AND_INVERTED:  BIC(AL, 0, pixel.reg, d.reg, s.reg);    break;
+    case GGL_NOOP:          MOV(AL, 0, pixel.reg, d.reg);           break;
+    case GGL_XOR:           EOR(AL, 0, pixel.reg, s.reg, d.reg);    break;
+    case GGL_OR:            ORR(AL, 0, pixel.reg, s.reg, d.reg);    break;
+    case GGL_NOR:           ORR(AL, 0, pixel.reg, s.reg, d.reg);
+                            MVN(AL, 0, pixel.reg, pixel.reg);       break;
+    case GGL_EQUIV:         EOR(AL, 0, pixel.reg, s.reg, d.reg);
+                            MVN(AL, 0, pixel.reg, pixel.reg);       break;
+    case GGL_INVERT:        MVN(AL, 0, pixel.reg, d.reg);           break;
+    case GGL_OR_REVERSE:    // s | ~d == ~(~s & d)
+                            BIC(AL, 0, pixel.reg, d.reg, s.reg);
+                            MVN(AL, 0, pixel.reg, pixel.reg);       break;
+    case GGL_COPY_INVERTED: MVN(AL, 0, pixel.reg, s.reg);           break;
+    case GGL_OR_INVERTED:   // ~s | d == ~(s & ~d)
+                            BIC(AL, 0, pixel.reg, s.reg, d.reg);
+                            MVN(AL, 0, pixel.reg, pixel.reg);       break;
+    case GGL_NAND:          AND(AL, 0, pixel.reg, s.reg, d.reg);
+                            MVN(AL, 0, pixel.reg, pixel.reg);       break;
+    case GGL_SET:           MVN(AL, 0, pixel.reg, imm(0));          break;
+    };        
+}
+
+// ---------------------------------------------------------------------------
+
+static uint32_t find_bottom(uint32_t val)
+{
+    uint32_t i = 0;
+    while (!(val & (3<<i)))
+        i+= 2;
+    return i;
+}
+
+static void normalize(uint32_t& val, uint32_t& rot)
+{
+    rot = 0;
+    while (!(val&3)  || (val & 0xFC000000)) {
+        uint32_t newval;
+        newval = val >> 2;
+        newval |= (val&3) << 30;
+        val = newval;
+        rot += 2;
+        if (rot == 32) {
+            rot = 0;
+            break;
+        }
+    }
+}
+
+void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits)
+{
+    uint32_t rot;
+    uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1;
+    mask &= size;
+
+    if (mask == size) {
+        if (d != s)
+            MOV( AL, 0, d, s);
+        return;
+    }
+    
+    int negative_logic = !isValidImmediate(mask);
+    if (negative_logic) {
+        mask = ~mask & size;
+    }
+    normalize(mask, rot);
+
+    if (mask) {
+        while (mask) {
+            uint32_t bitpos = find_bottom(mask);
+            int shift = rot + bitpos;
+            uint32_t m = mask & (0xff << bitpos);
+            mask &= ~m;
+            m >>= bitpos;
+            int32_t newMask =  (m<<shift) | (m>>(32-shift));
+            if (!negative_logic) {
+                AND( AL, 0, d, s, imm(newMask) );
+            } else {
+                BIC( AL, 0, d, s, imm(newMask) );
+            }
+            s = d;
+        }
+    } else {
+        MOV( AL, 0, d, imm(0));
+    }
+}		
+
+void GGLAssembler::build_masking(pixel_t& pixel, Scratch& regs)
+{
+    if (!mMasking || mAllMasked) {
+        return;
+    }
+
+    comment("color mask");
+
+    pixel_t fb(mDstPixel);
+    pixel_t s(pixel);
+    if (!(pixel.flags & CORRUPTIBLE)) {
+        pixel.reg = regs.obtain();
+        pixel.flags |= CORRUPTIBLE;
+    }
+
+    int mask = 0;
+    for (int i=0 ; i<4 ; i++) {
+        const int component_mask = 1<<i;
+        const int h = fb.format.c[i].h;
+        const int l = fb.format.c[i].l;
+        if (h && (!(mMasking & component_mask))) {
+            mask |= ((1<<(h-l))-1) << l;
+        }
+    }
+
+    // There is no need to clear the masked components of the source
+    // (unless we applied a logic op), because they're already zeroed 
+    // by construction (masked components are not computed)
+
+    if (mLogicOp) {
+        const needs_t& needs = mBuilderContext.needs;
+        const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+        if (opcode != GGL_CLEAR) {
+            // clear masked component of source
+            build_and_immediate(pixel.reg, s.reg, mask, fb.size());
+            s = pixel;
+        }
+    }
+
+    // clear non masked components of destination
+    build_and_immediate(fb.reg, fb.reg, ~mask, fb.size()); 
+
+    // or back the channels that were masked
+    if (s.reg == fb.reg) {
+         // this is in fact a MOV
+        if (s.reg == pixel.reg) {
+            // ugh. this in in fact a nop
+        } else {
+            MOV(AL, 0, pixel.reg, fb.reg);
+        }
+    } else {
+        ORR(AL, 0, pixel.reg, s.reg, fb.reg);
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::base_offset(
+        const pointer_t& d, const pointer_t& b, const reg_t& o)
+{
+    switch (b.size) {
+    case 32:
+        ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 2));
+        break;
+    case 24:
+        if (d.reg == b.reg) {
+            ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+            ADD(AL, 0, d.reg, d.reg, o.reg);
+        } else {
+            ADD(AL, 0, d.reg, o.reg, reg_imm(o.reg, LSL, 1));
+            ADD(AL, 0, d.reg, d.reg, b.reg);
+        }
+        break;
+    case 16:
+        ADD(AL, 0, d.reg, b.reg, reg_imm(o.reg, LSL, 1));
+        break;
+    case 8:
+        ADD(AL, 0, d.reg, b.reg, o.reg);
+        break;
+    }
+}
+
+// ----------------------------------------------------------------------------
+// cheezy register allocator...
+// ----------------------------------------------------------------------------
+
+void RegisterAllocator::reset()
+{
+    mRegs.reset();
+}
+
+int RegisterAllocator::reserveReg(int reg)
+{
+    return mRegs.reserve(reg);
+}
+
+int RegisterAllocator::obtainReg()
+{
+    return mRegs.obtain();
+}
+
+void RegisterAllocator::recycleReg(int reg)
+{
+    mRegs.recycle(reg);
+}
+
+RegisterAllocator::RegisterFile& RegisterAllocator::registerFile()
+{
+    return mRegs;
+}
+
+// ----------------------------------------------------------------------------
+
+RegisterAllocator::RegisterFile::RegisterFile()
+    : mRegs(0), mTouched(0), mStatus(0)
+{
+    reserve(ARMAssemblerInterface::SP);
+    reserve(ARMAssemblerInterface::PC);
+}
+
+RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs)
+    : mRegs(rhs.mRegs), mTouched(rhs.mTouched)
+{
+}
+
+RegisterAllocator::RegisterFile::~RegisterFile()
+{
+}
+
+bool RegisterAllocator::RegisterFile::operator == (const RegisterFile& rhs) const
+{
+    return (mRegs == rhs.mRegs);
+}
+
+void RegisterAllocator::RegisterFile::reset()
+{
+    mRegs = mTouched = mStatus = 0;
+    reserve(ARMAssemblerInterface::SP);
+    reserve(ARMAssemblerInterface::PC);
+}
+
+int RegisterAllocator::RegisterFile::reserve(int reg)
+{
+    LOG_ALWAYS_FATAL_IF(isUsed(reg),
+                        "reserving register %d, but already in use",
+                        reg);
+    mRegs |= (1<<reg);
+    mTouched |= mRegs;
+    return reg;
+}
+
+void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask)
+{
+    mRegs |= regMask;
+    mTouched |= regMask;
+}
+
+int RegisterAllocator::RegisterFile::isUsed(int reg) const
+{
+    LOG_ALWAYS_FATAL_IF(reg>=16, "invalid register %d", reg);
+    return mRegs & (1<<reg);
+}
+
+int RegisterAllocator::RegisterFile::obtain()
+{
+    const char priorityList[14] = {  0,  1, 2, 3, 
+                                    12, 14, 4, 5, 
+                                     6,  7, 8, 9,
+                                    10, 11 };
+    const int nbreg = sizeof(priorityList);
+    int i, r;
+    for (i=0 ; i<nbreg ; i++) {
+        r = priorityList[i];
+        if (!isUsed(r)) {
+            break;
+        }
+    }
+    // this is not an error anymore because, we'll try again with
+    // a lower optimization level.
+    //LOGE_IF(i >= nbreg, "pixelflinger ran out of registers\n");
+    if (i >= nbreg) {
+        mStatus |= OUT_OF_REGISTERS;
+        // we return SP so we can more easily debug things
+        // the code will never be run anyway.
+        return ARMAssemblerInterface::SP; 
+    }
+    reserve(r);
+    return r;
+}
+
+bool RegisterAllocator::RegisterFile::hasFreeRegs() const
+{
+    return ((mRegs & 0xFFFF) == 0xFFFF) ? false : true;
+}
+
+int RegisterAllocator::RegisterFile::countFreeRegs() const
+{
+    int f = ~mRegs & 0xFFFF;
+    // now count number of 1
+   f = (f & 0x5555) + ((f>>1) & 0x5555);
+   f = (f & 0x3333) + ((f>>2) & 0x3333);
+   f = (f & 0x0F0F) + ((f>>4) & 0x0F0F);
+   f = (f & 0x00FF) + ((f>>8) & 0x00FF);
+   return f;
+}
+
+void RegisterAllocator::RegisterFile::recycle(int reg)
+{
+    LOG_FATAL_IF(!isUsed(reg),
+            "recycling unallocated register %d",
+            reg);
+    mRegs &= ~(1<<reg);
+}
+
+void RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask)
+{
+    LOG_FATAL_IF((mRegs & regMask)!=regMask,
+            "recycling unallocated registers "
+            "(recycle=%08x, allocated=%08x, unallocated=%08x)",
+            regMask, mRegs, mRegs&regMask);
+    mRegs &= ~regMask;
+}
+
+uint32_t RegisterAllocator::RegisterFile::touched() const
+{
+    return mTouched;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
new file mode 100644
index 0000000..d1d29f0
--- /dev/null
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -0,0 +1,554 @@
+/* libs/pixelflinger/codeflinger/GGLAssembler.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGLASSEMBLER_H
+#define ANDROID_GGLASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "codeflinger/ARMAssemblerProxy.h"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define CONTEXT_LOAD(REG, FIELD) \
+    LDR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+#define CONTEXT_STORE(REG, FIELD) \
+    STR(AL, REG, mBuilderContext.Rctx, immed12_pre(GGL_OFFSETOF(FIELD)))
+
+
+class RegisterAllocator
+{
+public:
+    class RegisterFile;
+    
+    RegisterFile&   registerFile();
+    int             reserveReg(int reg);
+    int             obtainReg();
+    void            recycleReg(int reg);
+    void            reset();
+
+    class RegisterFile
+    {
+    public:
+                            RegisterFile();
+                            RegisterFile(const RegisterFile& rhs);
+                            ~RegisterFile();
+
+                void        reset();
+
+                bool operator == (const RegisterFile& rhs) const;
+                bool operator != (const RegisterFile& rhs) const {
+                    return !operator == (rhs);
+                }
+
+                int         reserve(int reg);
+                void        reserveSeveral(uint32_t regMask);
+
+                void        recycle(int reg);
+                void        recycleSeveral(uint32_t regMask);
+
+                int         obtain();
+        inline  int         isUsed(int reg) const;
+
+                bool        hasFreeRegs() const;
+                int         countFreeRegs() const;                
+
+                uint32_t    touched() const;
+        inline  uint32_t    status() const { return mStatus; }
+        
+        enum {
+            OUT_OF_REGISTERS = 0x1
+        };
+
+    private:
+        uint32_t    mRegs;
+        uint32_t    mTouched;
+        uint32_t    mStatus;
+    };
+ 
+    class Scratch
+    {
+    public:
+            Scratch(RegisterFile& regFile)
+                : mRegFile(regFile), mScratch(0) { 
+            }
+            ~Scratch() {
+                mRegFile.recycleSeveral(mScratch);
+            }
+        int obtain() { 
+            int reg = mRegFile.obtain();
+            mScratch |= 1<<reg;
+            return reg;
+        }
+        void recycle(int reg) {
+            mRegFile.recycle(reg);
+            mScratch &= ~(1<<reg);
+        }
+        bool isUsed(int reg) {
+            return (mScratch & (1<<reg));
+        }
+        int countFreeRegs() {
+            return mRegFile.countFreeRegs();
+        }
+    private:
+        RegisterFile&   mRegFile;
+        uint32_t        mScratch;
+    };
+
+    class Spill
+    {
+    public:
+        Spill(RegisterFile& regFile, ARMAssemblerInterface& gen, uint32_t reglist)
+            : mRegFile(regFile), mGen(gen), mRegList(reglist), mCount(0)
+        {
+            if (reglist) {
+                int count = 0;
+                while (reglist) {
+                    count++;
+                    reglist &= ~(1 << (31 - __builtin_clz(reglist)));
+                }
+                if (count == 1) {
+                    int reg = 31 - __builtin_clz(mRegList);
+                    mGen.STR(mGen.AL, reg, mGen.SP, mGen.immed12_pre(-4, 1));
+                } else {
+                    mGen.STM(mGen.AL, mGen.DB, mGen.SP, 1, mRegList);
+                }
+                mRegFile.recycleSeveral(mRegList);
+                mCount = count;
+            }
+        }
+        ~Spill() {
+            if (mRegList) {
+                if (mCount == 1) {
+                    int reg = 31 - __builtin_clz(mRegList);
+                    mGen.LDR(mGen.AL, reg, mGen.SP, mGen.immed12_post(4));
+                } else {
+                    mGen.LDM(mGen.AL, mGen.IA, mGen.SP, 1, mRegList);
+                }
+                mRegFile.reserveSeveral(mRegList);
+            }
+        }
+    private:
+        RegisterFile&           mRegFile;
+        ARMAssemblerInterface&  mGen;
+        uint32_t                mRegList;
+        int                     mCount;
+    };
+    
+private:
+    RegisterFile    mRegs;
+};
+
+// ----------------------------------------------------------------------------
+
+class GGLAssembler : public ARMAssemblerProxy, public RegisterAllocator
+{
+public:
+
+                    GGLAssembler(ARMAssemblerInterface* target);
+        virtual     ~GGLAssembler();
+
+    uint32_t*   base() const { return 0; } // XXX
+    uint32_t*   pc() const { return 0; } // XXX
+
+    void        reset(int opt_level);
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+
+        // generate scanline code for given needs
+    int         scanline(const needs_t& needs, context_t const* c);
+    int         scanline_core(const needs_t& needs, context_t const* c);
+
+        enum {
+            CLEAR_LO    = 0x0001,
+            CLEAR_HI    = 0x0002,
+            CORRUPTIBLE = 0x0004,
+            FIRST       = 0x0008
+        };
+
+        enum { //load/store flags
+            WRITE_BACK  = 0x0001
+        };
+
+        struct reg_t {
+            reg_t() : reg(-1), flags(0) {
+            }
+            reg_t(int r, int f=0)
+                : reg(r), flags(f) {
+            }
+            void setTo(int r, int f=0) {
+                reg=r; flags=f;
+            }
+            int         reg;
+            uint16_t    flags;
+        };
+
+        struct integer_t : public reg_t {
+            integer_t() : reg_t(), s(0) {
+            }
+            integer_t(int r, int sz=32, int f=0)
+                : reg_t(r, f), s(sz) {
+            }
+            void setTo(int r, int sz=32, int f=0) {
+                reg_t::setTo(r, f); s=sz;
+            }
+            int8_t s;
+            inline int size() const { return s; }
+        };
+        
+        struct pixel_t : public reg_t {
+            pixel_t() : reg_t() {
+                memset(&format, 0, sizeof(GGLFormat));
+            }
+            pixel_t(int r, const GGLFormat* fmt, int f=0)
+                : reg_t(r, f), format(*fmt) {
+            }
+            void setTo(int r, const GGLFormat* fmt, int f=0) {
+                reg_t::setTo(r, f); format = *fmt;
+            }
+            GGLFormat format;
+            inline int hi(int c) const { return format.c[c].h; }
+            inline int low(int c) const { return format.c[c].l; }
+            inline int mask(int c) const { return ((1<<size(c))-1) << low(c); }
+            inline int size() const { return format.size*8; }
+            inline int size(int c) const { return component_size(c); }
+            inline int component_size(int c) const { return hi(c) - low(c); }
+        };
+
+        struct component_t : public reg_t {
+            component_t() : reg_t(), h(0), l(0) {
+            }
+            component_t(int r, int f=0)
+                : reg_t(r, f), h(0), l(0) {
+            }
+            component_t(int r, int lo, int hi, int f=0)
+                : reg_t(r, f), h(hi), l(lo) {
+            }
+            explicit component_t(const integer_t& rhs)
+                : reg_t(rhs.reg, rhs.flags), h(rhs.s), l(0) {
+            }
+            explicit component_t(const pixel_t& rhs, int component) {
+                setTo(  rhs.reg, 
+                        rhs.format.c[component].l,
+                        rhs.format.c[component].h,
+                        rhs.flags|CLEAR_LO|CLEAR_HI);
+            }
+            void setTo(int r, int lo=0, int hi=0, int f=0) {
+                reg_t::setTo(r, f); h=hi; l=lo;
+            }
+            int8_t h;
+            int8_t l;
+            inline int size() const { return h-l; }
+        };
+
+        struct pointer_t : public reg_t {
+            pointer_t() : reg_t(), size(0) {
+            }
+            pointer_t(int r, int s, int f=0)
+                : reg_t(r, f), size(s) {
+            }
+            void setTo(int r, int s, int f=0) {
+                reg_t::setTo(r, f); size=s;
+            }
+            int8_t size;
+        };
+
+
+private:
+    struct tex_coord_t {
+        reg_t       s;
+        reg_t       t;
+        pointer_t   ptr;
+    };
+
+    struct fragment_parts_t {
+        uint32_t    packed  : 1;
+        uint32_t    reload  : 2;
+        uint32_t    iterated_packed  : 1;
+        pixel_t     iterated;
+        pointer_t   cbPtr;
+        pointer_t   covPtr;
+        reg_t       count;
+        reg_t       argb[4];
+        reg_t       argb_dx[4];
+        reg_t       z;
+        reg_t       dither;
+        pixel_t     texel[GGL_TEXTURE_UNIT_COUNT];
+        tex_coord_t coords[GGL_TEXTURE_UNIT_COUNT];
+    };
+    
+    struct texture_unit_t {
+        int         format_idx;
+        GGLFormat   format;
+        int         bits;
+        int         swrap;
+        int         twrap;
+        int         env;
+        int         pot;
+        int         linear;
+        uint8_t     mask;
+        uint8_t     replaced;
+    };
+
+    struct texture_machine_t {
+        texture_unit_t  tmu[GGL_TEXTURE_UNIT_COUNT];
+        uint8_t         mask;
+        uint8_t         replaced;
+        uint8_t         directTexture;
+        uint8_t         activeUnits;
+    };
+
+    struct component_info_t {
+        bool    masked      : 1;
+        bool    inDest      : 1;
+        bool    needed      : 1;
+        bool    replaced    : 1;
+        bool    iterated    : 1;
+        bool    smooth      : 1;
+        bool    blend       : 1;
+        bool    fog         : 1;
+    };
+
+    struct builder_context_t {
+        context_t const*    c;
+        needs_t             needs;
+        int                 Rctx;
+    };
+
+    template <typename T>
+    void modify(T& r, Scratch& regs)
+    {
+        if (!(r.flags & CORRUPTIBLE)) {
+            r.reg = regs.obtain();
+            r.flags |= CORRUPTIBLE;
+        }
+    }
+
+    // helpers
+    void    base_offset(const pointer_t& d, const pointer_t& b, const reg_t& o);
+
+    // texture environement
+    void    modulate(   component_t& dest,
+                        const component_t& incoming,
+                        const pixel_t& texel, int component);
+
+    void    decal(  component_t& dest,
+                    const component_t& incoming,
+                    const pixel_t& texel, int component);
+
+    void    blend(  component_t& dest,
+                    const component_t& incoming,
+                    const pixel_t& texel, int component, int tmu);
+
+    void    add(  component_t& dest,
+                    const component_t& incoming,
+                    const pixel_t& texel, int component);
+
+    // load/store stuff
+    void    store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0);
+    void    load(const pointer_t& addr, const pixel_t& dest, uint32_t flags=0);
+    void    extract(integer_t& d, const pixel_t& s, int component);    
+    void    extract(component_t& d, const pixel_t& s, int component);    
+    void    extract(integer_t& d, int s, int h, int l, int bits=32);
+    void    expand(integer_t& d, const integer_t& s, int dbits);
+    void    expand(integer_t& d, const component_t& s, int dbits);
+    void    expand(component_t& d, const component_t& s, int dbits);
+    void    downshift(pixel_t& d, int component, component_t s, const reg_t& dither);
+
+
+    void    mul_factor( component_t& d,
+                        const integer_t& v,
+                        const integer_t& f);
+
+    void    mul_factor_add( component_t& d,
+                            const integer_t& v,
+                            const integer_t& f,
+                            const component_t& a);
+
+    void    component_add(  component_t& d,
+                            const integer_t& dst,
+                            const integer_t& src);
+
+    void    component_sat(  const component_t& v);
+
+
+    void    build_scanline_prolog(  fragment_parts_t& parts,
+                                    const needs_t& needs);
+
+    void    build_smooth_shade(const fragment_parts_t& parts);
+
+    void    build_component(    pixel_t& pixel,
+                                const fragment_parts_t& parts,
+                                int component,
+                                Scratch& global_scratches);
+                                
+    void    build_incoming_component(
+                                component_t& temp,
+                                int dst_size,
+                                const fragment_parts_t& parts,
+                                int component,
+                                Scratch& scratches,
+                                Scratch& global_scratches);
+
+    void    init_iterated_color(fragment_parts_t& parts, const reg_t& x);
+
+    void    build_iterated_color(   component_t& fragment,
+                                    const fragment_parts_t& parts,
+                                    int component,
+                                    Scratch& regs);
+
+    void    decodeLogicOpNeeds(const needs_t& needs);
+    
+    void    decodeTMUNeeds(const needs_t& needs, context_t const* c);
+
+    void    init_textures(  tex_coord_t* coords,
+                            const reg_t& x,
+                            const reg_t& y);
+
+    void    build_textures( fragment_parts_t& parts,
+                            Scratch& regs);
+
+    void    filter8(   const fragment_parts_t& parts,
+                        pixel_t& texel, const texture_unit_t& tmu,
+                        int U, int V, pointer_t& txPtr,
+                        int FRAC_BITS);
+
+    void    filter16(   const fragment_parts_t& parts,
+                        pixel_t& texel, const texture_unit_t& tmu,
+                        int U, int V, pointer_t& txPtr,
+                        int FRAC_BITS);
+
+    void    filter24(   const fragment_parts_t& parts,
+                        pixel_t& texel, const texture_unit_t& tmu,
+                        int U, int V, pointer_t& txPtr,
+                        int FRAC_BITS);
+
+    void    filter32(   const fragment_parts_t& parts,
+                        pixel_t& texel, const texture_unit_t& tmu,
+                        int U, int V, pointer_t& txPtr,
+                        int FRAC_BITS);
+
+    void    build_texture_environment(  component_t& fragment,
+                                        const fragment_parts_t& parts,
+                                        int component,
+                                        Scratch& regs);
+
+    void    wrapping(   int d,
+                        int coord, int size,
+                        int tx_wrap, int tx_linear);
+
+    void    build_fog(  component_t& temp,
+                        int component,
+                        Scratch& parent_scratches);
+
+    void    build_blending(     component_t& in_out,
+                                const pixel_t& pixel,
+                                int component,
+                                Scratch& parent_scratches);
+
+    void    build_blend_factor(
+                integer_t& factor, int f, int component,
+                const pixel_t& dst_pixel,
+                integer_t& fragment,
+                integer_t& fb,
+                Scratch& scratches);
+
+    void    build_blendFOneMinusF(  component_t& temp,
+                                    const integer_t& factor, 
+                                    const integer_t& fragment,
+                                    const integer_t& fb);
+
+    void    build_blendOneMinusFF(  component_t& temp,
+                                    const integer_t& factor, 
+                                    const integer_t& fragment,
+                                    const integer_t& fb);
+
+    void build_coverage_application(component_t& fragment,
+                                    const fragment_parts_t& parts,
+                                    Scratch& regs);
+
+    void build_alpha_test(component_t& fragment, const fragment_parts_t& parts);
+
+    enum { Z_TEST=1, Z_WRITE=2 }; 
+    void build_depth_test(const fragment_parts_t& parts, uint32_t mask);
+    void build_iterate_z(const fragment_parts_t& parts);
+    void build_iterate_f(const fragment_parts_t& parts);
+    void build_iterate_texture_coordinates(const fragment_parts_t& parts);
+
+    void build_logic_op(pixel_t& pixel, Scratch& regs);
+
+    void build_masking(pixel_t& pixel, Scratch& regs);
+
+    void build_and_immediate(int d, int s, uint32_t mask, int bits);
+
+    bool    isAlphaSourceNeeded() const;
+
+    enum {
+        FACTOR_SRC=1, FACTOR_DST=2, BLEND_SRC=4, BLEND_DST=8 
+    };
+    
+    enum {
+        LOGIC_OP=1, LOGIC_OP_SRC=2, LOGIC_OP_DST=4
+    };
+
+    static int blending_codes(int fs, int fd);
+
+    builder_context_t   mBuilderContext;
+    texture_machine_t   mTextureMachine;
+    component_info_t    mInfo[4];
+    int                 mBlending;
+    int                 mMasking;
+    int                 mAllMasked;
+    int                 mLogicOp;
+    int                 mAlphaTest;
+    int                 mAA;
+    int                 mDithering;
+    int                 mDepthTest;
+
+    int             mSmooth;
+    int             mFog;
+    pixel_t         mDstPixel;
+    
+    GGLFormat       mCbFormat;
+    
+    int             mBlendFactorCached;
+    integer_t       mAlphaSource;
+    
+    int             mBaseRegister;
+    
+    int             mBlendSrc;
+    int             mBlendDst;
+    int             mBlendSrcA;
+    int             mBlendDstA;
+    
+    int             mOptLevel;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_GGLASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/armreg.h b/libpixelflinger/codeflinger/armreg.h
new file mode 100644
index 0000000..fde81ba
--- /dev/null
+++ b/libpixelflinger/codeflinger/armreg.h
@@ -0,0 +1,300 @@
+/*	$NetBSD: armreg.h,v 1.28 2003/10/31 16:30:15 scw Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2001 Ben Harris
+ * Copyright (c) 1994-1996 Mark Brinicombe.
+ * Copyright (c) 1994 Brini.
+ * All rights reserved.
+ *
+ * This code is derived from software written for Brini by Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ *    endorse or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/armreg.h,v 1.3 2005/11/21 19:06:25 cognet Exp $
+ */
+
+#ifndef MACHINE_ARMREG_H
+#define MACHINE_ARMREG_H
+#define INSN_SIZE	4
+#define INSN_COND_MASK	0xf0000000	/* Condition mask */
+#define PSR_MODE        0x0000001f      /* mode mask */
+#define PSR_USR26_MODE  0x00000000
+#define PSR_FIQ26_MODE  0x00000001
+#define PSR_IRQ26_MODE  0x00000002
+#define PSR_SVC26_MODE  0x00000003
+#define PSR_USR32_MODE  0x00000010
+#define PSR_FIQ32_MODE  0x00000011
+#define PSR_IRQ32_MODE  0x00000012
+#define PSR_SVC32_MODE  0x00000013
+#define PSR_ABT32_MODE  0x00000017
+#define PSR_UND32_MODE  0x0000001b
+#define PSR_SYS32_MODE  0x0000001f
+#define PSR_32_MODE     0x00000010
+#define PSR_FLAGS	0xf0000000    /* flags */
+
+#define PSR_C_bit (1 << 29)       /* carry */
+
+/* The high-order byte is always the implementor */
+#define CPU_ID_IMPLEMENTOR_MASK	0xff000000
+#define CPU_ID_ARM_LTD		0x41000000 /* 'A' */
+#define CPU_ID_DEC		0x44000000 /* 'D' */
+#define CPU_ID_INTEL		0x69000000 /* 'i' */
+#define	CPU_ID_TI		0x54000000 /* 'T' */
+
+/* How to decide what format the CPUID is in. */
+#define CPU_ID_ISOLD(x)		(((x) & 0x0000f000) == 0x00000000)
+#define CPU_ID_IS7(x)		(((x) & 0x0000f000) == 0x00007000)
+#define CPU_ID_ISNEW(x)		(!CPU_ID_ISOLD(x) && !CPU_ID_IS7(x))
+
+/* On ARM3 and ARM6, this byte holds the foundry ID. */
+#define CPU_ID_FOUNDRY_MASK	0x00ff0000
+#define CPU_ID_FOUNDRY_VLSI	0x00560000
+
+/* On ARM7 it holds the architecture and variant (sub-model) */
+#define CPU_ID_7ARCH_MASK	0x00800000
+#define CPU_ID_7ARCH_V3		0x00000000
+#define CPU_ID_7ARCH_V4T	0x00800000
+#define CPU_ID_7VARIANT_MASK	0x007f0000
+
+/* On more recent ARMs, it does the same, but in a different format */
+#define CPU_ID_ARCH_MASK	0x000f0000
+#define CPU_ID_ARCH_V3		0x00000000
+#define CPU_ID_ARCH_V4		0x00010000
+#define CPU_ID_ARCH_V4T		0x00020000
+#define CPU_ID_ARCH_V5		0x00030000
+#define CPU_ID_ARCH_V5T		0x00040000
+#define CPU_ID_ARCH_V5TE	0x00050000
+#define CPU_ID_VARIANT_MASK	0x00f00000
+
+/* Next three nybbles are part number */
+#define CPU_ID_PARTNO_MASK	0x0000fff0
+
+/* Intel XScale has sub fields in part number */
+#define CPU_ID_XSCALE_COREGEN_MASK	0x0000e000 /* core generation */
+#define CPU_ID_XSCALE_COREREV_MASK	0x00001c00 /* core revision */
+#define CPU_ID_XSCALE_PRODUCT_MASK	0x000003f0 /* product number */
+
+/* And finally, the revision number. */
+#define CPU_ID_REVISION_MASK	0x0000000f
+
+/* Individual CPUs are probably best IDed by everything but the revision. */
+#define CPU_ID_CPU_MASK		0xfffffff0
+
+/* Fake CPU IDs for ARMs without CP15 */
+#define CPU_ID_ARM2		0x41560200
+#define CPU_ID_ARM250		0x41560250
+
+/* Pre-ARM7 CPUs -- [15:12] == 0 */
+#define CPU_ID_ARM3		0x41560300
+#define CPU_ID_ARM600		0x41560600
+#define CPU_ID_ARM610		0x41560610
+#define CPU_ID_ARM620		0x41560620
+
+/* ARM7 CPUs -- [15:12] == 7 */
+#define CPU_ID_ARM700		0x41007000 /* XXX This is a guess. */
+#define CPU_ID_ARM710		0x41007100
+#define CPU_ID_ARM7500		0x41027100 /* XXX This is a guess. */
+#define CPU_ID_ARM710A		0x41047100 /* inc ARM7100 */
+#define CPU_ID_ARM7500FE	0x41077100
+#define CPU_ID_ARM710T		0x41807100
+#define CPU_ID_ARM720T		0x41807200
+#define CPU_ID_ARM740T8K	0x41807400 /* XXX no MMU, 8KB cache */
+#define CPU_ID_ARM740T4K	0x41817400 /* XXX no MMU, 4KB cache */
+
+/* Post-ARM7 CPUs */
+#define CPU_ID_ARM810		0x41018100
+#define CPU_ID_ARM920T		0x41129200
+#define CPU_ID_ARM920T_ALT	0x41009200
+#define CPU_ID_ARM922T		0x41029220
+#define CPU_ID_ARM940T		0x41029400 /* XXX no MMU */
+#define CPU_ID_ARM946ES		0x41049460 /* XXX no MMU */
+#define	CPU_ID_ARM966ES		0x41049660 /* XXX no MMU */
+#define	CPU_ID_ARM966ESR1	0x41059660 /* XXX no MMU */
+#define CPU_ID_ARM1020E		0x4115a200 /* (AKA arm10 rev 1) */
+#define CPU_ID_ARM1022ES	0x4105a220
+#define CPU_ID_SA110		0x4401a100
+#define CPU_ID_SA1100		0x4401a110
+#define	CPU_ID_TI925T		0x54029250
+#define CPU_ID_SA1110		0x6901b110
+#define CPU_ID_IXP1200		0x6901c120
+#define CPU_ID_80200		0x69052000
+#define CPU_ID_PXA250    	0x69052100 /* sans core revision */
+#define CPU_ID_PXA210    	0x69052120
+#define CPU_ID_PXA250A		0x69052100 /* 1st version Core */
+#define CPU_ID_PXA210A		0x69052120 /* 1st version Core */
+#define CPU_ID_PXA250B		0x69052900 /* 3rd version Core */
+#define CPU_ID_PXA210B		0x69052920 /* 3rd version Core */
+#define CPU_ID_PXA250C		0x69052d00 /* 4th version Core */
+#define CPU_ID_PXA210C		0x69052d20 /* 4th version Core */
+#define	CPU_ID_80321_400	0x69052420
+#define	CPU_ID_80321_600	0x69052430
+#define	CPU_ID_80321_400_B0	0x69052c20
+#define	CPU_ID_80321_600_B0	0x69052c30
+#define	CPU_ID_IXP425_533	0x690541c0
+#define	CPU_ID_IXP425_400	0x690541d0
+#define	CPU_ID_IXP425_266	0x690541f0
+
+/* ARM3-specific coprocessor 15 registers */
+#define ARM3_CP15_FLUSH		1
+#define ARM3_CP15_CONTROL	2
+#define ARM3_CP15_CACHEABLE	3
+#define ARM3_CP15_UPDATEABLE	4
+#define ARM3_CP15_DISRUPTIVE	5	
+
+/* ARM3 Control register bits */
+#define ARM3_CTL_CACHE_ON	0x00000001
+#define ARM3_CTL_SHARED		0x00000002
+#define ARM3_CTL_MONITOR	0x00000004
+
+/*
+ * Post-ARM3 CP15 registers:
+ *
+ *	1	Control register
+ *
+ *	2	Translation Table Base
+ *
+ *	3	Domain Access Control
+ *
+ *	4	Reserved
+ *
+ *	5	Fault Status
+ *
+ *	6	Fault Address
+ *
+ *	7	Cache/write-buffer Control
+ *
+ *	8	TLB Control
+ *
+ *	9	Cache Lockdown
+ *
+ *	10	TLB Lockdown
+ *
+ *	11	Reserved
+ *
+ *	12	Reserved
+ *
+ *	13	Process ID (for FCSE)
+ *
+ *	14	Reserved
+ *
+ *	15	Implementation Dependent
+ */
+
+/* Some of the definitions below need cleaning up for V3/V4 architectures */
+
+/* CPU control register (CP15 register 1) */
+#define CPU_CONTROL_MMU_ENABLE	0x00000001 /* M: MMU/Protection unit enable */
+#define CPU_CONTROL_AFLT_ENABLE	0x00000002 /* A: Alignment fault enable */
+#define CPU_CONTROL_DC_ENABLE	0x00000004 /* C: IDC/DC enable */
+#define CPU_CONTROL_WBUF_ENABLE 0x00000008 /* W: Write buffer enable */
+#define CPU_CONTROL_32BP_ENABLE 0x00000010 /* P: 32-bit exception handlers */
+#define CPU_CONTROL_32BD_ENABLE 0x00000020 /* D: 32-bit addressing */
+#define CPU_CONTROL_LABT_ENABLE 0x00000040 /* L: Late abort enable */
+#define CPU_CONTROL_BEND_ENABLE 0x00000080 /* B: Big-endian mode */
+#define CPU_CONTROL_SYST_ENABLE 0x00000100 /* S: System protection bit */
+#define CPU_CONTROL_ROM_ENABLE	0x00000200 /* R: ROM protection bit */
+#define CPU_CONTROL_CPCLK	0x00000400 /* F: Implementation defined */
+#define CPU_CONTROL_BPRD_ENABLE 0x00000800 /* Z: Branch prediction enable */
+#define CPU_CONTROL_IC_ENABLE   0x00001000 /* I: IC enable */
+#define CPU_CONTROL_VECRELOC	0x00002000 /* V: Vector relocation */
+#define CPU_CONTROL_ROUNDROBIN	0x00004000 /* RR: Predictable replacement */
+#define CPU_CONTROL_V4COMPAT	0x00008000 /* L4: ARMv4 compat LDR R15 etc */
+
+#define CPU_CONTROL_IDC_ENABLE	CPU_CONTROL_DC_ENABLE
+
+/* XScale Auxillary Control Register (CP15 register 1, opcode2 1) */
+#define	XSCALE_AUXCTL_K		0x00000001 /* dis. write buffer coalescing */
+#define	XSCALE_AUXCTL_P		0x00000002 /* ECC protect page table access */
+#define	XSCALE_AUXCTL_MD_WB_RA	0x00000000 /* mini-D$ wb, read-allocate */
+#define	XSCALE_AUXCTL_MD_WB_RWA	0x00000010 /* mini-D$ wb, read/write-allocate */
+#define	XSCALE_AUXCTL_MD_WT	0x00000020 /* mini-D$ wt, read-allocate */
+#define	XSCALE_AUXCTL_MD_MASK	0x00000030
+
+/* Cache type register definitions */
+#define	CPU_CT_ISIZE(x)		((x) & 0xfff)		/* I$ info */
+#define	CPU_CT_DSIZE(x)		(((x) >> 12) & 0xfff)	/* D$ info */
+#define	CPU_CT_S		(1U << 24)		/* split cache */
+#define	CPU_CT_CTYPE(x)		(((x) >> 25) & 0xf)	/* cache type */
+
+#define	CPU_CT_CTYPE_WT		0	/* write-through */
+#define	CPU_CT_CTYPE_WB1	1	/* write-back, clean w/ read */
+#define	CPU_CT_CTYPE_WB2	2	/* w/b, clean w/ cp15,7 */
+#define	CPU_CT_CTYPE_WB6	6	/* w/b, cp15,7, lockdown fmt A */
+#define	CPU_CT_CTYPE_WB7	7	/* w/b, cp15,7, lockdown fmt B */
+
+#define	CPU_CT_xSIZE_LEN(x)	((x) & 0x3)		/* line size */
+#define	CPU_CT_xSIZE_M		(1U << 2)		/* multiplier */
+#define	CPU_CT_xSIZE_ASSOC(x)	(((x) >> 3) & 0x7)	/* associativity */
+#define	CPU_CT_xSIZE_SIZE(x)	(((x) >> 6) & 0x7)	/* size */
+
+/* Fault status register definitions */
+
+#define FAULT_TYPE_MASK 0x0f
+#define FAULT_USER      0x10
+
+#define FAULT_WRTBUF_0  0x00 /* Vector Exception */
+#define FAULT_WRTBUF_1  0x02 /* Terminal Exception */
+#define FAULT_BUSERR_0  0x04 /* External Abort on Linefetch -- Section */
+#define FAULT_BUSERR_1  0x06 /* External Abort on Linefetch -- Page */
+#define FAULT_BUSERR_2  0x08 /* External Abort on Non-linefetch -- Section */
+#define FAULT_BUSERR_3  0x0a /* External Abort on Non-linefetch -- Page */
+#define FAULT_BUSTRNL1  0x0c /* External abort on Translation -- Level 1 */
+#define FAULT_BUSTRNL2  0x0e /* External abort on Translation -- Level 2 */
+#define FAULT_ALIGN_0   0x01 /* Alignment */
+#define FAULT_ALIGN_1   0x03 /* Alignment */
+#define FAULT_TRANS_S   0x05 /* Translation -- Section */
+#define FAULT_TRANS_P   0x07 /* Translation -- Page */
+#define FAULT_DOMAIN_S  0x09 /* Domain -- Section */
+#define FAULT_DOMAIN_P  0x0b /* Domain -- Page */
+#define FAULT_PERM_S    0x0d /* Permission -- Section */
+#define FAULT_PERM_P    0x0f /* Permission -- Page */
+
+#define	FAULT_IMPRECISE	0x400	/* Imprecise exception (XSCALE) */
+
+/*
+ * Address of the vector page, low and high versions.
+ */
+#define	ARM_VECTORS_LOW		0x00000000U
+#define	ARM_VECTORS_HIGH	0xffff0000U
+
+/*
+ * ARM Instructions
+ *
+ *       3 3 2 2 2                              
+ *       1 0 9 8 7                                                     0
+ *      +-------+-------------------------------------------------------+
+ *      | cond  |              instruction dependant                    |
+ *      |c c c c|                                                       |
+ *      +-------+-------------------------------------------------------+
+ */
+
+#define INSN_SIZE		4		/* Always 4 bytes */
+#define INSN_COND_MASK		0xf0000000	/* Condition mask */
+#define INSN_COND_AL		0xe0000000	/* Always condition */
+
+#endif /* !MACHINE_ARMREG_H */
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
new file mode 100644
index 0000000..f10217b
--- /dev/null
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -0,0 +1,682 @@
+/* libs/pixelflinger/codeflinger/blending.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "codeflinger/GGLAssembler.h"
+
+
+namespace android {
+
+void GGLAssembler::build_fog(
+                        component_t& temp,      // incomming fragment / output
+                        int component,
+                        Scratch& regs)
+{
+   if (mInfo[component].fog) {
+        Scratch scratches(registerFile());
+        comment("fog");
+
+        integer_t fragment(temp.reg, temp.h, temp.flags);
+        if (!(temp.flags & CORRUPTIBLE)) {
+            temp.reg = regs.obtain();
+            temp.flags |= CORRUPTIBLE;
+        }
+
+        integer_t fogColor(scratches.obtain(), 8, CORRUPTIBLE); 
+        LDRB(AL, fogColor.reg, mBuilderContext.Rctx,
+                immed12_pre(GGL_OFFSETOF(state.fog.color[component])));
+
+        integer_t factor(scratches.obtain(), 16, CORRUPTIBLE);
+        CONTEXT_LOAD(factor.reg, generated_vars.f);
+
+        // clamp fog factor (TODO: see if there is a way to guarantee
+        // we won't overflow, when setting the iterators)
+        BIC(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, ASR, 31));
+        CMP(AL, factor.reg, imm( 0x10000 ));
+        MOV(HS, 0, factor.reg, imm( 0x10000 ));
+
+        build_blendFOneMinusF(temp, factor, fragment, fogColor);
+    }
+}
+
+void GGLAssembler::build_blending(
+                        component_t& temp,      // incomming fragment / output
+                        const pixel_t& pixel,   // framebuffer
+                        int component,
+                        Scratch& regs)
+{
+   if (!mInfo[component].blend)
+        return;
+        
+    int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc;
+    int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst;
+    if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA)
+        fs = GGL_ONE;
+    const int blending = blending_codes(fs, fd);
+    if (!temp.size()) {
+        // here, blending will produce something which doesn't depend on
+        // that component (eg: GL_ZERO:GL_*), so the register has not been
+        // allocated yet. Will never be used as a source.
+        temp = component_t(regs.obtain(), CORRUPTIBLE);
+    }
+
+    // we are doing real blending...
+    // fb:          extracted dst
+    // fragment:    extracted src
+    // temp:        component_t(fragment) and result
+
+    // scoped register allocator
+    Scratch scratches(registerFile());
+    comment("blending");
+
+    // we can optimize these cases a bit...
+    // (1) saturation is not needed
+    // (2) we can use only one multiply instead of 2
+    // (3) we can reduce the register pressure
+    //      R = S*f + D*(1-f) = (S-D)*f + D
+    //      R = S*(1-f) + D*f = (D-S)*f + S
+
+    const bool same_factor_opt1 =
+        (fs==GGL_DST_COLOR && fd==GGL_ONE_MINUS_DST_COLOR) ||
+        (fs==GGL_SRC_COLOR && fd==GGL_ONE_MINUS_SRC_COLOR) ||
+        (fs==GGL_DST_ALPHA && fd==GGL_ONE_MINUS_DST_ALPHA) ||
+        (fs==GGL_SRC_ALPHA && fd==GGL_ONE_MINUS_SRC_ALPHA);
+
+    const bool same_factor_opt2 =
+        (fs==GGL_ONE_MINUS_DST_COLOR && fd==GGL_DST_COLOR) ||
+        (fs==GGL_ONE_MINUS_SRC_COLOR && fd==GGL_SRC_COLOR) || 
+        (fs==GGL_ONE_MINUS_DST_ALPHA && fd==GGL_DST_ALPHA) ||
+        (fs==GGL_ONE_MINUS_SRC_ALPHA && fd==GGL_SRC_ALPHA);
+
+
+    // XXX: we could also optimize these cases:
+    // R = S*f + D*f = (S+D)*f
+    // R = S*(1-f) + D*(1-f) = (S+D)*(1-f)
+    // R = S*D + D*S = 2*S*D
+
+
+    // see if we need to extract 'component' from the destination (fb)
+    integer_t fb;
+    if (blending & (BLEND_DST|FACTOR_DST)) { 
+        fb.setTo(scratches.obtain(), 32); 
+        extract(fb, pixel, component);
+        if (mDithering) {
+            // XXX: maybe what we should do instead, is simply
+            // expand fb -or- fragment to the larger of the two
+            if (fb.size() < temp.size()) {
+                // for now we expand 'fb' to min(fragment, 8)
+                int new_size = temp.size() < 8 ? temp.size() : 8;
+                expand(fb, fb, new_size);
+            }
+        }
+    }
+
+
+    // convert input fragment to integer_t
+    if (temp.l && (temp.flags & CORRUPTIBLE)) {
+        MOV(AL, 0, temp.reg, reg_imm(temp.reg, LSR, temp.l));
+        temp.h -= temp.l;
+        temp.l = 0;
+    }
+    integer_t fragment(temp.reg, temp.size(), temp.flags);
+
+    // if not done yet, convert input fragment to integer_t
+    if (temp.l) {
+        // here we know temp is not CORRUPTIBLE
+        fragment.reg = scratches.obtain();
+        MOV(AL, 0, fragment.reg, reg_imm(temp.reg, LSR, temp.l));
+        fragment.flags |= CORRUPTIBLE;
+    }
+
+    if (!(temp.flags & CORRUPTIBLE)) {
+        // temp is not corruptible, but since it's the destination it
+        // will be modified, so we need to allocate a new register.
+        temp.reg = regs.obtain();
+        temp.flags &= ~CORRUPTIBLE;
+        fragment.flags &= ~CORRUPTIBLE;
+    }
+
+    if ((blending & BLEND_SRC) && !same_factor_opt1) {
+        // source (fragment) is needed for the blending stage
+        // so it's not CORRUPTIBLE (unless we're doing same_factor_opt1)
+        fragment.flags &= ~CORRUPTIBLE;
+    }
+
+
+    if (same_factor_opt1) {
+        //  R = S*f + D*(1-f) = (S-D)*f + D
+        integer_t factor;
+        build_blend_factor(factor, fs, 
+                component, pixel, fragment, fb, scratches);
+        // fb is always corruptible from this point
+        fb.flags |= CORRUPTIBLE;
+        build_blendFOneMinusF(temp, factor, fragment, fb);
+    } else if (same_factor_opt2) {
+        //  R = S*(1-f) + D*f = (D-S)*f + S
+        integer_t factor;
+        // fb is always corrruptible here
+        fb.flags |= CORRUPTIBLE;
+        build_blend_factor(factor, fd,
+                component, pixel, fragment, fb, scratches);
+        build_blendOneMinusFF(temp, factor, fragment, fb);
+    } else {
+        integer_t src_factor;
+        integer_t dst_factor;
+
+        // if destination (fb) is not needed for the blending stage, 
+        // then it can be marked as CORRUPTIBLE
+        if (!(blending & BLEND_DST)) {
+            fb.flags |= CORRUPTIBLE;
+        }
+
+        // XXX: try to mark some registers as CORRUPTIBLE
+        // in most case we could make those corruptible
+        // when we're processing the last component
+        // but not always, for instance
+        //    when fragment is constant and not reloaded
+        //    when fb is needed for logic-ops or masking
+        //    when a register is aliased (for instance with mAlphaSource)
+
+        // blend away...
+        if (fs==GGL_ZERO) {
+            if (fd==GGL_ZERO) {         // R = 0
+                // already taken care of
+            } else if (fd==GGL_ONE) {   // R = D
+                // already taken care of
+            } else {                    // R = D*fd
+                // compute fd
+                build_blend_factor(dst_factor, fd,
+                        component, pixel, fragment, fb, scratches);
+                mul_factor(temp, fb, dst_factor);
+            }
+        } else if (fs==GGL_ONE) {
+            if (fd==GGL_ZERO) {         // R = S
+                // NOP, taken care of
+            } else if (fd==GGL_ONE) {   // R = S + D
+                component_add(temp, fb, fragment); // args order matters
+                component_sat(temp);
+            } else {                    // R = S + D*fd
+                // compute fd
+                build_blend_factor(dst_factor, fd,
+                        component, pixel, fragment, fb, scratches);
+                mul_factor_add(temp, fb, dst_factor, component_t(fragment));
+                if (fd==GGL_ONE_MINUS_SRC_ALPHA) {
+                    // XXX: in theory this is not correct, we should
+                    // saturate here. However, this mode is often
+                    // used for displaying alpha-premultiplied graphics,
+                    // in which case, saturation is not necessary.
+                    // unfortunatelly, we have no way to know.
+                    // This is a case, where we sacrifice correctness for
+                    // performance. we should probably have some heuristics.
+                } else {
+                    component_sat(temp);
+                }
+            }
+        } else {
+            // compute fs
+            build_blend_factor(src_factor, fs, 
+                    component, pixel, fragment, fb, scratches);
+            if (fd==GGL_ZERO) {         // R = S*fs
+                mul_factor(temp, fragment, src_factor);
+            } else if (fd==GGL_ONE) {   // R = S*fs + D
+                mul_factor_add(temp, fragment, src_factor, component_t(fb));
+                component_sat(temp);
+            } else {                    // R = S*fs + D*fd
+                mul_factor(temp, fragment, src_factor);
+                if (scratches.isUsed(src_factor.reg))
+                    scratches.recycle(src_factor.reg);
+                // compute fd
+                build_blend_factor(dst_factor, fd,
+                        component, pixel, fragment, fb, scratches);
+                mul_factor_add(temp, fb, dst_factor, temp);
+                if (!same_factor_opt1 && !same_factor_opt2) {
+                    component_sat(temp);
+                }
+            }
+        }
+    }
+
+    // now we can be corrupted (it's the dest)
+    temp.flags |= CORRUPTIBLE;
+}
+
+void GGLAssembler::build_blend_factor(
+        integer_t& factor, int f, int component,
+        const pixel_t& dst_pixel,
+        integer_t& fragment,
+        integer_t& fb,
+        Scratch& scratches)
+{
+    integer_t src_alpha(fragment);
+
+    // src_factor/dst_factor won't be used after blending,
+    // so it's fine to mark them as CORRUPTIBLE (if not aliased)
+    factor.flags |= CORRUPTIBLE;
+
+    switch(f) {
+    case GGL_ONE_MINUS_SRC_ALPHA:
+    case GGL_SRC_ALPHA:
+        if (component==GGLFormat::ALPHA && !isAlphaSourceNeeded()) {
+            // we're processing alpha, so we already have
+            // src-alpha in fragment, and we need src-alpha just this time.
+        } else {
+           // alpha-src will be needed for other components
+            if (!mBlendFactorCached || mBlendFactorCached==f) {
+                src_alpha = mAlphaSource;
+                factor = mAlphaSource;
+                factor.flags &= ~CORRUPTIBLE;           
+                // we already computed the blend factor before, nothing to do.
+                if (mBlendFactorCached)
+                    return;
+                // this is the first time, make sure to compute the blend
+                // factor properly.
+                mBlendFactorCached = f;
+                break;
+            } else {
+                // we have a cached alpha blend factor, but we want another one,
+                // this should really not happen because by construction,
+                // we cannot have BOTH source and destination
+                // blend factors use ALPHA *and* ONE_MINUS_ALPHA (because
+                // the blending stage uses the f/(1-f) optimization
+                
+                // for completeness, we handle this case though. Since there
+                // are only 2 choices, this meens we want "the other one"
+                // (1-factor)
+                factor = mAlphaSource;
+                factor.flags &= ~CORRUPTIBLE;           
+                RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s)));
+                mBlendFactorCached = f;
+                return;
+            }                
+        }
+        // fall-through...
+    case GGL_ONE_MINUS_DST_COLOR:
+    case GGL_DST_COLOR:
+    case GGL_ONE_MINUS_SRC_COLOR:
+    case GGL_SRC_COLOR:
+    case GGL_ONE_MINUS_DST_ALPHA:
+    case GGL_DST_ALPHA:
+    case GGL_SRC_ALPHA_SATURATE:
+        // help us find out what register we can use for the blend-factor
+        // CORRUPTIBLE registers are chosen first, or a new one is allocated.
+        if (fragment.flags & CORRUPTIBLE) {
+            factor.setTo(fragment.reg, 32, CORRUPTIBLE);
+            fragment.flags &= ~CORRUPTIBLE;
+        } else if (fb.flags & CORRUPTIBLE) {
+            factor.setTo(fb.reg, 32, CORRUPTIBLE);
+            fb.flags &= ~CORRUPTIBLE;
+        } else {
+            factor.setTo(scratches.obtain(), 32, CORRUPTIBLE);
+        } 
+        break;
+    }
+
+    // XXX: doesn't work if size==1
+
+    switch(f) {
+    case GGL_ONE_MINUS_DST_COLOR:
+    case GGL_DST_COLOR:
+        factor.s = fb.s;
+        ADD(AL, 0, factor.reg, fb.reg, reg_imm(fb.reg, LSR, fb.s-1));
+        break;
+    case GGL_ONE_MINUS_SRC_COLOR:
+    case GGL_SRC_COLOR:
+        factor.s = fragment.s;
+        ADD(AL, 0, factor.reg, fragment.reg,
+            reg_imm(fragment.reg, LSR, fragment.s-1));
+        break;
+    case GGL_ONE_MINUS_SRC_ALPHA:
+    case GGL_SRC_ALPHA:
+        factor.s = src_alpha.s;
+        ADD(AL, 0, factor.reg, src_alpha.reg,
+                reg_imm(src_alpha.reg, LSR, src_alpha.s-1));
+        break;
+    case GGL_ONE_MINUS_DST_ALPHA:
+    case GGL_DST_ALPHA:
+        // XXX: should be precomputed
+        extract(factor, dst_pixel, GGLFormat::ALPHA);
+        ADD(AL, 0, factor.reg, factor.reg,
+                reg_imm(factor.reg, LSR, factor.s-1));
+        break;
+    case GGL_SRC_ALPHA_SATURATE:
+        // XXX: should be precomputed
+        // XXX: f = min(As, 1-Ad)
+        // btw, we're guaranteed that Ad's size is <= 8, because
+        // it's extracted from the framebuffer
+        break;
+    }
+
+    switch(f) {
+    case GGL_ONE_MINUS_DST_COLOR:
+    case GGL_ONE_MINUS_SRC_COLOR:
+    case GGL_ONE_MINUS_DST_ALPHA:
+    case GGL_ONE_MINUS_SRC_ALPHA:
+        RSB(AL, 0, factor.reg, factor.reg, imm((1<<factor.s)));
+    }
+    
+    // don't need more than 8-bits for the blend factor
+    // and this will prevent overflows in the multiplies later
+    if (factor.s > 8) {
+        MOV(AL, 0, factor.reg, reg_imm(factor.reg, LSR, factor.s-8));
+        factor.s = 8;
+    }
+}
+
+int GGLAssembler::blending_codes(int fs, int fd)
+{
+    int blending = 0;
+    switch(fs) {
+    case GGL_ONE:
+        blending |= BLEND_SRC;
+        break;
+
+    case GGL_ONE_MINUS_DST_COLOR:
+    case GGL_DST_COLOR:
+        blending |= FACTOR_DST|BLEND_SRC;
+        break;
+    case GGL_ONE_MINUS_DST_ALPHA:
+    case GGL_DST_ALPHA:
+        // no need to extract 'component' from the destination
+        // for the blend factor, because we need ALPHA only.
+        blending |= BLEND_SRC;
+        break;
+
+    case GGL_ONE_MINUS_SRC_COLOR:
+    case GGL_SRC_COLOR:    
+        blending |= FACTOR_SRC|BLEND_SRC;
+        break;
+    case GGL_ONE_MINUS_SRC_ALPHA:
+    case GGL_SRC_ALPHA:
+    case GGL_SRC_ALPHA_SATURATE:
+        blending |= FACTOR_SRC|BLEND_SRC;
+        break;
+    }
+    switch(fd) {
+    case GGL_ONE:
+        blending |= BLEND_DST;
+        break;
+
+    case GGL_ONE_MINUS_DST_COLOR:
+    case GGL_DST_COLOR:
+        blending |= FACTOR_DST|BLEND_DST;
+        break;
+    case GGL_ONE_MINUS_DST_ALPHA:
+    case GGL_DST_ALPHA:
+        blending |= FACTOR_DST|BLEND_DST;
+        break;
+
+    case GGL_ONE_MINUS_SRC_COLOR:
+    case GGL_SRC_COLOR:    
+        blending |= FACTOR_SRC|BLEND_DST;
+        break;
+    case GGL_ONE_MINUS_SRC_ALPHA:
+    case GGL_SRC_ALPHA:
+        // no need to extract 'component' from the source
+        // for the blend factor, because we need ALPHA only.
+        blending |= BLEND_DST;
+        break;
+    }
+    return blending;
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::build_blendFOneMinusF(
+        component_t& temp,
+        const integer_t& factor, 
+        const integer_t& fragment,
+        const integer_t& fb)
+{
+    //  R = S*f + D*(1-f) = (S-D)*f + D
+    Scratch scratches(registerFile());
+    // compute S-D
+    integer_t diff(fragment.flags & CORRUPTIBLE ?
+            fragment.reg : scratches.obtain(), fb.size(), CORRUPTIBLE);
+    const int shift = fragment.size() - fb.size();
+    if (shift>0)        RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift));
+    else if (shift<0)   RSB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift));
+    else                RSB(AL, 0, diff.reg, fb.reg, fragment.reg);
+    mul_factor_add(temp, diff, factor, component_t(fb));
+}
+
+void GGLAssembler::build_blendOneMinusFF(
+        component_t& temp,
+        const integer_t& factor, 
+        const integer_t& fragment,
+        const integer_t& fb)
+{
+    //  R = S*f + D*(1-f) = (S-D)*f + D
+    Scratch scratches(registerFile());
+    // compute D-S
+    integer_t diff(fb.flags & CORRUPTIBLE ?
+            fb.reg : scratches.obtain(), fb.size(), CORRUPTIBLE);
+    const int shift = fragment.size() - fb.size();
+    if (shift>0)        SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSR, shift));
+    else if (shift<0)   SUB(AL, 0, diff.reg, fb.reg, reg_imm(fragment.reg, LSL,-shift));
+    else                SUB(AL, 0, diff.reg, fb.reg, fragment.reg);
+    mul_factor_add(temp, diff, factor, component_t(fragment));
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::mul_factor(  component_t& d,
+                                const integer_t& v,
+                                const integer_t& f)
+{
+    int vs = v.size();
+    int fs = f.size();
+    int ms = vs+fs;
+
+    // XXX: we could have special cases for 1 bit mul
+
+    // all this code below to use the best multiply instruction
+    // wrt the parameters size. We take advantage of the fact
+    // that the 16-bits multiplies allow a 16-bit shift
+    // The trick is that we just make sure that we have at least 8-bits
+    // per component (which is enough for a 8 bits display).
+
+    int xy;
+    int vshift = 0;
+    int fshift = 0;
+    int smulw = 0;
+
+    if (vs<16) {
+        if (fs<16) {
+            xy = xyBB;
+        } else if (GGL_BETWEEN(fs, 24, 31)) {
+            ms -= 16;
+            xy = xyTB;
+        } else {
+            // eg: 15 * 18  ->  15 * 15
+            fshift = fs - 15;
+            ms -= fshift;
+            xy = xyBB;
+        }
+    } else if (GGL_BETWEEN(vs, 24, 31)) {
+        if (fs<16) {
+            ms -= 16;
+            xy = xyTB;
+        } else if (GGL_BETWEEN(fs, 24, 31)) {
+            ms -= 32;
+            xy = xyTT;
+        } else {
+            // eg: 24 * 18  ->  8 * 18
+            fshift = fs - 15;
+            ms -= 16 + fshift;
+            xy = xyTB;
+        }
+    } else {
+        if (fs<16) {
+            // eg: 18 * 15  ->  15 * 15
+            vshift = vs - 15;
+            ms -= vshift;
+            xy = xyBB;
+        } else if (GGL_BETWEEN(fs, 24, 31)) {
+            // eg: 18 * 24  ->  15 * 8
+            vshift = vs - 15;
+            ms -= 16 + vshift;
+            xy = xyBT;
+        } else {
+            // eg: 18 * 18  ->  (15 * 18)>>16
+            fshift = fs - 15;
+            ms -= 16 + fshift;
+            xy = yB;    //XXX SMULWB
+            smulw = 1;
+        }
+    }
+
+    LOGE_IF(ms>=32, "mul_factor overflow vs=%d, fs=%d", vs, fs);
+
+    int vreg = v.reg;
+    int freg = f.reg;
+    if (vshift) {
+        MOV(AL, 0, d.reg, reg_imm(vreg, LSR, vshift));
+        vreg = d.reg;
+    }
+    if (fshift) {
+        MOV(AL, 0, d.reg, reg_imm(vreg, LSR, fshift));
+        freg = d.reg;
+    }
+    if (smulw)  SMULW(AL, xy, d.reg, vreg, freg);
+    else        SMUL(AL, xy, d.reg, vreg, freg);
+
+
+    d.h = ms;
+    if (mDithering) {
+        d.l = 0; 
+    } else {
+        d.l = fs; 
+        d.flags |= CLEAR_LO;
+    }
+}
+
+void GGLAssembler::mul_factor_add(  component_t& d,
+                                    const integer_t& v,
+                                    const integer_t& f,
+                                    const component_t& a)
+{
+    // XXX: we could have special cases for 1 bit mul
+    Scratch scratches(registerFile());
+
+    int vs = v.size();
+    int fs = f.size();
+    int as = a.h;
+    int ms = vs+fs;
+
+    LOGE_IF(ms>=32, "mul_factor_add overflow vs=%d, fs=%d, as=%d", vs, fs, as);
+
+    integer_t add(a.reg, a.h, a.flags);
+
+    // 'a' is a component_t but it is guaranteed to have
+    // its high bits set to 0. However in the dithering case,
+    // we can't get away with truncating the potentially bad bits
+    // so extraction is needed.
+
+   if ((mDithering) && (a.size() < ms)) {
+        // we need to expand a
+        if (!(a.flags & CORRUPTIBLE)) {
+            // ... but it's not corruptible, so we need to pick a
+            // temporary register.
+            // Try to uses the destination register first (it's likely
+            // to be usable, unless it aliases an input).
+            if (d.reg!=a.reg && d.reg!=v.reg && d.reg!=f.reg) {
+                add.reg = d.reg;
+            } else {
+                add.reg = scratches.obtain();
+            }
+        }
+        expand(add, a, ms); // extracts and expands
+        as = ms;
+    }
+
+    if (ms == as) {
+        if (vs<16 && fs<16) SMLABB(AL, d.reg, v.reg, f.reg, add.reg);
+        else                MLA(AL, 0, d.reg, v.reg, f.reg, add.reg);
+    } else {
+        int temp = d.reg;
+        if (temp == add.reg) {
+            // the mul will modify add.reg, we need an intermediary reg
+            if (v.flags & CORRUPTIBLE)      temp = v.reg;
+            else if (f.flags & CORRUPTIBLE) temp = f.reg;
+            else                            temp = scratches.obtain();
+        }
+
+        if (vs<16 && fs<16) SMULBB(AL, temp, v.reg, f.reg);
+        else                MUL(AL, 0, temp, v.reg, f.reg);
+
+        if (ms>as) {
+            ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSL, ms-as));
+        } else if (ms<as) {
+            // not sure if we should expand the mul instead?
+            ADD(AL, 0, d.reg, temp, reg_imm(add.reg, LSR, as-ms));
+        }
+    }
+
+    d.h = ms;
+    if (mDithering) {
+        d.l = a.l; 
+    } else {
+        d.l = fs>a.l ? fs : a.l;
+        d.flags |= CLEAR_LO;
+    }
+}
+
+void GGLAssembler::component_add(component_t& d,
+        const integer_t& dst, const integer_t& src)
+{
+    // here we're guaranteed that fragment.size() >= fb.size()
+    const int shift = src.size() - dst.size();
+    if (!shift) {
+        ADD(AL, 0, d.reg, src.reg, dst.reg);
+    } else {
+        ADD(AL, 0, d.reg, src.reg, reg_imm(dst.reg, LSL, shift));
+    }
+
+    d.h = src.size();
+    if (mDithering) {
+        d.l = 0;
+    } else {
+        d.l = shift;
+        d.flags |= CLEAR_LO;
+    }
+}
+
+void GGLAssembler::component_sat(const component_t& v)
+{
+    const int one = ((1<<v.size())-1)<<v.l;
+    CMP(AL, v.reg, imm( 1<<v.h ));
+    if (isValidImmediate(one)) {
+        MOV(HS, 0, v.reg, imm( one ));
+    } else if (isValidImmediate(~one)) {
+        MVN(HS, 0, v.reg, imm( ~one ));
+    } else {
+        MOV(HS, 0, v.reg, imm( 1<<v.h ));
+        SUB(HS, 0, v.reg, v.reg, imm( 1<<v.l ));
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
new file mode 100644
index 0000000..4676da0
--- /dev/null
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -0,0 +1,702 @@
+/*	$NetBSD: disassem.c,v 1.14 2003/03/27 16:58:36 mycroft Exp $	*/
+
+/*-
+ * Copyright (c) 1996 Mark Brinicombe.
+ * Copyright (c) 1996 Brini.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Brini.
+ * 4. The name of the company nor the name of the author may be used to
+ *    endorse or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * RiscBSD kernel project
+ *
+ * db_disasm.c
+ *
+ * Kernel disassembler
+ *
+ * Created      : 10/02/96
+ *
+ * Structured after the sparc/sparc/db_disasm.c by David S. Miller &
+ * Paul Kranenburg
+ *
+ * This code is not complete. Not all instructions are disassembled.
+ */
+
+#include <sys/cdefs.h>
+//__FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/arm/arm/disassem.c,v 1.2 2005/01/05 21:58:47 imp Exp $");
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "disassem.h"
+#include "armreg.h"
+//#include <ddb/ddb.h>
+
+/*
+ * General instruction format
+ *
+ *	insn[cc][mod]	[operands]
+ *
+ * Those fields with an uppercase format code indicate that the field
+ * follows directly after the instruction before the separator i.e.
+ * they modify the instruction rather than just being an operand to
+ * the instruction. The only exception is the writeback flag which
+ * follows a operand.
+ *
+ *
+ * 2 - print Operand 2 of a data processing instruction
+ * d - destination register (bits 12-15)
+ * n - n register (bits 16-19)
+ * s - s register (bits 8-11)
+ * o - indirect register rn (bits 16-19) (used by swap)
+ * m - m register (bits 0-3)
+ * a - address operand of ldr/str instruction
+ * e - address operand of ldrh/strh instruction
+ * l - register list for ldm/stm instruction
+ * f - 1st fp operand (register) (bits 12-14)
+ * g - 2nd fp operand (register) (bits 16-18)
+ * h - 3rd fp operand (register/immediate) (bits 0-4)
+ * b - branch address
+ * t - thumb branch address (bits 24, 0-23)
+ * k - breakpoint comment (bits 0-3, 8-19)
+ * X - block transfer type
+ * Y - block transfer type (r13 base)
+ * c - comment field bits(0-23)
+ * p - saved or current status register
+ * F - PSR transfer fields
+ * D - destination-is-r15 (P) flag on TST, TEQ, CMP, CMN
+ * L - co-processor transfer size
+ * S - set status flag
+ * P - fp precision
+ * Q - fp precision (for ldf/stf)
+ * R - fp rounding
+ * v - co-processor data transfer registers + addressing mode
+ * W - writeback flag
+ * x - instruction in hex
+ * # - co-processor number
+ * y - co-processor data processing registers
+ * z - co-processor register transfer registers
+ */
+
+struct arm32_insn {
+	u_int mask;
+	u_int pattern;
+	char* name;
+	char* format;
+};
+
+static const struct arm32_insn arm32_i[] = {
+    { 0x0fffffff, 0x0ff00000, "imb",	"c" },		/* Before swi */
+    { 0x0fffffff, 0x0ff00001, "imbrange",	"c" },	/* Before swi */
+    { 0x0f000000, 0x0f000000, "swi",	"c" },
+    { 0xfe000000, 0xfa000000, "blx",	"t" },		/* Before b and bl */
+    { 0x0f000000, 0x0a000000, "b",	"b" },
+    { 0x0f000000, 0x0b000000, "bl",	"b" },
+    { 0x0fe000f0, 0x00000090, "mul",	"Snms" },
+    { 0x0fe000f0, 0x00200090, "mla",	"Snmsd" },
+    { 0x0fe000f0, 0x00800090, "umull",	"Sdnms" },
+    { 0x0fe000f0, 0x00c00090, "smull",	"Sdnms" },
+    { 0x0fe000f0, 0x00a00090, "umlal",	"Sdnms" },
+    { 0x0fe000f0, 0x00e00090, "smlal",	"Sdnms" },
+    { 0x0d700000, 0x04200000, "strt",	"daW" },
+    { 0x0d700000, 0x04300000, "ldrt",	"daW" },
+    { 0x0d700000, 0x04600000, "strbt",	"daW" },
+    { 0x0d700000, 0x04700000, "ldrbt",	"daW" },
+    { 0x0c500000, 0x04000000, "str",	"daW" },
+    { 0x0c500000, 0x04100000, "ldr",	"daW" },
+    { 0x0c500000, 0x04400000, "strb",	"daW" },
+    { 0x0c500000, 0x04500000, "ldrb",	"daW" },
+    { 0x0e1f0000, 0x080d0000, "stm",	"YnWl" },/* separate out r13 base */
+    { 0x0e1f0000, 0x081d0000, "ldm",	"YnWl" },/* separate out r13 base */    
+    { 0x0e100000, 0x08000000, "stm",	"XnWl" },
+    { 0x0e100000, 0x08100000, "ldm",	"XnWl" },    
+    { 0x0e1000f0, 0x00100090, "ldrb",	"deW" },
+    { 0x0e1000f0, 0x00000090, "strb",	"deW" },
+    { 0x0e1000f0, 0x001000d0, "ldrsb",	"deW" },
+    { 0x0e1000f0, 0x001000b0, "ldrh",	"deW" },
+    { 0x0e1000f0, 0x000000b0, "strh",	"deW" },
+    { 0x0e1000f0, 0x001000f0, "ldrsh",	"deW" },
+    { 0x0f200090, 0x00200090, "und",	"x" },	/* Before data processing */
+    { 0x0e1000d0, 0x000000d0, "und",	"x" },	/* Before data processing */
+    { 0x0ff00ff0, 0x01000090, "swp",	"dmo" },
+    { 0x0ff00ff0, 0x01400090, "swpb",	"dmo" },
+    { 0x0fbf0fff, 0x010f0000, "mrs",	"dp" },	/* Before data processing */
+    { 0x0fb0fff0, 0x0120f000, "msr",	"pFm" },/* Before data processing */
+    { 0x0fb0f000, 0x0320f000, "msr",	"pF2" },/* Before data processing */
+    { 0x0ffffff0, 0x012fff10, "bx",     "m" },
+    { 0x0fff0ff0, 0x016f0f10, "clz",	"dm" },
+    { 0x0ffffff0, 0x012fff30, "blx",	"m" },
+    { 0xfff000f0, 0xe1200070, "bkpt",	"k" },
+    { 0x0de00000, 0x00000000, "and",	"Sdn2" },
+    { 0x0de00000, 0x00200000, "eor",	"Sdn2" },
+    { 0x0de00000, 0x00400000, "sub",	"Sdn2" },
+    { 0x0de00000, 0x00600000, "rsb",	"Sdn2" },
+    { 0x0de00000, 0x00800000, "add",	"Sdn2" },
+    { 0x0de00000, 0x00a00000, "adc",	"Sdn2" },
+    { 0x0de00000, 0x00c00000, "sbc",	"Sdn2" },
+    { 0x0de00000, 0x00e00000, "rsc",	"Sdn2" },
+    { 0x0df00000, 0x01100000, "tst",	"Dn2" },
+    { 0x0df00000, 0x01300000, "teq",	"Dn2" },
+    { 0x0df00000, 0x01500000, "cmp",	"Dn2" },
+    { 0x0df00000, 0x01700000, "cmn",	"Dn2" },
+    { 0x0de00000, 0x01800000, "orr",	"Sdn2" },
+    { 0x0de00000, 0x01a00000, "mov",	"Sd2" },
+    { 0x0de00000, 0x01c00000, "bic",	"Sdn2" },
+    { 0x0de00000, 0x01e00000, "mvn",	"Sd2" },
+    { 0x0ff08f10, 0x0e000100, "adf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e100100, "muf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e200100, "suf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e300100, "rsf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e400100, "dvf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e500100, "rdf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e600100, "pow",	"PRfgh" },
+    { 0x0ff08f10, 0x0e700100, "rpw",	"PRfgh" },
+    { 0x0ff08f10, 0x0e800100, "rmf",	"PRfgh" },
+    { 0x0ff08f10, 0x0e900100, "fml",	"PRfgh" },
+    { 0x0ff08f10, 0x0ea00100, "fdv",	"PRfgh" },
+    { 0x0ff08f10, 0x0eb00100, "frd",	"PRfgh" },
+    { 0x0ff08f10, 0x0ec00100, "pol",	"PRfgh" },
+    { 0x0f008f10, 0x0e000100, "fpbop",	"PRfgh" },
+    { 0x0ff08f10, 0x0e008100, "mvf",	"PRfh" },
+    { 0x0ff08f10, 0x0e108100, "mnf",	"PRfh" },
+    { 0x0ff08f10, 0x0e208100, "abs",	"PRfh" },
+    { 0x0ff08f10, 0x0e308100, "rnd",	"PRfh" },
+    { 0x0ff08f10, 0x0e408100, "sqt",	"PRfh" },
+    { 0x0ff08f10, 0x0e508100, "log",	"PRfh" },
+    { 0x0ff08f10, 0x0e608100, "lgn",	"PRfh" },
+    { 0x0ff08f10, 0x0e708100, "exp",	"PRfh" },
+    { 0x0ff08f10, 0x0e808100, "sin",	"PRfh" },
+    { 0x0ff08f10, 0x0e908100, "cos",	"PRfh" },
+    { 0x0ff08f10, 0x0ea08100, "tan",	"PRfh" },
+    { 0x0ff08f10, 0x0eb08100, "asn",	"PRfh" },
+    { 0x0ff08f10, 0x0ec08100, "acs",	"PRfh" },
+    { 0x0ff08f10, 0x0ed08100, "atn",	"PRfh" },
+    { 0x0f008f10, 0x0e008100, "fpuop",	"PRfh" },
+    { 0x0e100f00, 0x0c000100, "stf",	"QLv" },
+    { 0x0e100f00, 0x0c100100, "ldf",	"QLv" },
+    { 0x0ff00f10, 0x0e000110, "flt",	"PRgd" },
+    { 0x0ff00f10, 0x0e100110, "fix",	"PRdh" },
+    { 0x0ff00f10, 0x0e200110, "wfs",	"d" },
+    { 0x0ff00f10, 0x0e300110, "rfs",	"d" },
+    { 0x0ff00f10, 0x0e400110, "wfc",	"d" },
+    { 0x0ff00f10, 0x0e500110, "rfc",	"d" },
+    { 0x0ff0ff10, 0x0e90f110, "cmf",	"PRgh" },
+    { 0x0ff0ff10, 0x0eb0f110, "cnf",	"PRgh" },
+    { 0x0ff0ff10, 0x0ed0f110, "cmfe",	"PRgh" },
+    { 0x0ff0ff10, 0x0ef0f110, "cnfe",	"PRgh" },
+    { 0xff100010, 0xfe000010, "mcr2",	"#z" },
+    { 0x0f100010, 0x0e000010, "mcr",	"#z" },
+    { 0xff100010, 0xfe100010, "mrc2",	"#z" },
+    { 0x0f100010, 0x0e100010, "mrc",	"#z" },
+    { 0xff000010, 0xfe000000, "cdp2",	"#y" },
+    { 0x0f000010, 0x0e000000, "cdp",	"#y" },
+    { 0xfe100090, 0xfc100000, "ldc2",	"L#v" },
+    { 0x0e100090, 0x0c100000, "ldc",	"L#v" },
+    { 0xfe100090, 0xfc000000, "stc2",	"L#v" },
+    { 0x0e100090, 0x0c000000, "stc",	"L#v" },
+    { 0xf550f000, 0xf550f000, "pld",	"ne" },
+    { 0x0ff00ff0, 0x01000050, "qaad",	"dmn" },
+    { 0x0ff00ff0, 0x01400050, "qdaad",	"dmn" },
+    { 0x0ff00ff0, 0x01600050, "qdsub",	"dmn" },
+    { 0x0ff00ff0, 0x01200050, "dsub",	"dmn" },
+    { 0x0ff000f0, 0x01000080, "smlabb",	"nmsd" },   // d & n inverted!!
+    { 0x0ff000f0, 0x010000a0, "smlatb",	"nmsd" },   // d & n inverted!!
+    { 0x0ff000f0, 0x010000c0, "smlabt",	"nmsd" },   // d & n inverted!!
+    { 0x0ff000f0, 0x010000e0, "smlatt",	"nmsd" },   // d & n inverted!!
+    { 0x0ff000f0, 0x01400080, "smlalbb","ndms" },   // d & n inverted!!
+    { 0x0ff000f0, 0x014000a0, "smlaltb","ndms" },   // d & n inverted!!
+    { 0x0ff000f0, 0x014000c0, "smlalbt","ndms" },   // d & n inverted!!
+    { 0x0ff000f0, 0x014000e0, "smlaltt","ndms" },   // d & n inverted!!
+    { 0x0ff000f0, 0x01200080, "smlawb", "nmsd" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x012000a0, "smulwb","nms" },   // d & n inverted!!
+    { 0x0ff000f0, 0x012000c0, "smlawt", "nmsd" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x012000e0, "smulwt","nms" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x01600080, "smulbb","nms" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x016000a0, "smultb","nms" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x016000c0, "smulbt","nms" },   // d & n inverted!!
+    { 0x0ff0f0f0, 0x016000e0, "smultt","nms" },   // d & n inverted!!
+    { 0x00000000, 0x00000000, NULL,	NULL }
+};
+
+static char const arm32_insn_conditions[][4] = {
+	"eq", "ne", "cs", "cc",
+	"mi", "pl", "vs", "vc",
+	"hi", "ls", "ge", "lt",
+	"gt", "le", "",   "nv"
+};
+
+static char const insn_block_transfers[][4] = {
+	"da", "ia", "db", "ib"
+};
+
+static char const insn_stack_block_transfers[][4] = {
+	"ed", "ea", "fd", "fa"
+};
+
+static char const op_shifts[][4] = {
+	"lsl", "lsr", "asr", "ror"
+};
+
+static char const insn_fpa_rounding[][2] = {
+	"", "p", "m", "z"
+};
+
+static char const insn_fpa_precision[][2] = {
+	"s", "d", "e", "p"
+};
+
+static char const insn_fpaconstants[][8] = {
+	"0.0", "1.0", "2.0", "3.0",
+	"4.0", "5.0", "0.5", "10.0"
+};
+
+#define insn_condition(x)	arm32_insn_conditions[(x >> 28) & 0x0f]
+#define insn_blktrans(x)	insn_block_transfers[(x >> 23) & 3]
+#define insn_stkblktrans(x)	insn_stack_block_transfers[(x >> 23) & 3]
+#define op2_shift(x)		op_shifts[(x >> 5) & 3]
+#define insn_fparnd(x)		insn_fpa_rounding[(x >> 5) & 0x03]
+#define insn_fpaprec(x)		insn_fpa_precision[(((x >> 18) & 2)|(x >> 7)) & 1]
+#define insn_fpaprect(x)	insn_fpa_precision[(((x >> 21) & 2)|(x >> 15)) & 1]
+#define insn_fpaimm(x)		insn_fpaconstants[x & 0x07]
+
+/* Local prototypes */
+static void disasm_register_shift(const disasm_interface_t *di, u_int insn);
+static void disasm_print_reglist(const disasm_interface_t *di, u_int insn);
+static void disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn,
+    u_int loc);
+static void disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn,
+    u_int loc);
+static void disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn,
+    u_int loc);
+static u_int disassemble_readword(u_int address);
+static void disassemble_printaddr(u_int address);
+
+u_int
+disasm(const disasm_interface_t *di, u_int loc, int altfmt)
+{
+	const struct arm32_insn *i_ptr = &arm32_i[0];
+
+	u_int insn;
+	int matchp;
+	int branch;
+	char* f_ptr;
+	int fmt;
+
+	fmt = 0;
+	matchp = 0;
+	insn = di->di_readword(loc);
+
+/*	di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/
+
+	while (i_ptr->name) {
+		if ((insn & i_ptr->mask) ==  i_ptr->pattern) {
+			matchp = 1;
+			break;
+		}
+		i_ptr++;
+	}
+
+	if (!matchp) {
+		di->di_printf("und%s\t%08x\n", insn_condition(insn), insn);
+		return(loc + INSN_SIZE);
+	}
+
+	/* If instruction forces condition code, don't print it. */
+	if ((i_ptr->mask & 0xf0000000) == 0xf0000000)
+		di->di_printf("%s", i_ptr->name);
+	else
+		di->di_printf("%s%s", i_ptr->name, insn_condition(insn));
+
+	f_ptr = i_ptr->format;
+
+	/* Insert tab if there are no instruction modifiers */
+
+	if (*(f_ptr) < 'A' || *(f_ptr) > 'Z') {
+		++fmt;
+		di->di_printf("\t");
+	}
+
+	while (*f_ptr) {
+		switch (*f_ptr) {
+		/* 2 - print Operand 2 of a data processing instruction */
+		case '2':
+			if (insn & 0x02000000) {
+				int rotate= ((insn >> 7) & 0x1e);
+
+				di->di_printf("#0x%08x",
+					      (insn & 0xff) << (32 - rotate) |
+					      (insn & 0xff) >> rotate);
+			} else {  
+				disasm_register_shift(di, insn);
+			}
+			break;
+		/* d - destination register (bits 12-15) */
+		case 'd':
+			di->di_printf("r%d", ((insn >> 12) & 0x0f));
+			break;
+		/* D - insert 'p' if Rd is R15 */
+		case 'D':
+			if (((insn >> 12) & 0x0f) == 15)
+				di->di_printf("p");
+			break;
+		/* n - n register (bits 16-19) */
+		case 'n':
+			di->di_printf("r%d", ((insn >> 16) & 0x0f));
+			break;
+		/* s - s register (bits 8-11) */
+		case 's':
+			di->di_printf("r%d", ((insn >> 8) & 0x0f));
+			break;
+		/* o - indirect register rn (bits 16-19) (used by swap) */
+		case 'o':
+			di->di_printf("[r%d]", ((insn >> 16) & 0x0f));
+			break;
+		/* m - m register (bits 0-4) */
+		case 'm':
+			di->di_printf("r%d", ((insn >> 0) & 0x0f));
+			break;
+		/* a - address operand of ldr/str instruction */
+		case 'a':
+			disasm_insn_ldrstr(di, insn, loc);
+			break;
+		/* e - address operand of ldrh/strh instruction */
+		case 'e':
+			disasm_insn_ldrhstrh(di, insn, loc);
+			break;
+		/* l - register list for ldm/stm instruction */
+		case 'l':
+			disasm_print_reglist(di, insn);
+			break;
+		/* f - 1st fp operand (register) (bits 12-14) */
+		case 'f':
+			di->di_printf("f%d", (insn >> 12) & 7);
+			break;
+		/* g - 2nd fp operand (register) (bits 16-18) */
+		case 'g':
+			di->di_printf("f%d", (insn >> 16) & 7);
+			break;
+		/* h - 3rd fp operand (register/immediate) (bits 0-4) */
+		case 'h':
+			if (insn & (1 << 3))
+				di->di_printf("#%s", insn_fpaimm(insn));
+			else
+				di->di_printf("f%d", insn & 7);
+			break;
+		/* b - branch address */
+		case 'b':
+			branch = ((insn << 2) & 0x03ffffff);
+			if (branch & 0x02000000)
+				branch |= 0xfc000000;
+			di->di_printaddr(loc + 8 + branch);
+			break;
+		/* t - blx address */
+		case 't':
+			branch = ((insn << 2) & 0x03ffffff) |
+			    (insn >> 23 & 0x00000002);
+			if (branch & 0x02000000)
+				branch |= 0xfc000000;
+			di->di_printaddr(loc + 8 + branch);
+			break;
+		/* X - block transfer type */
+		case 'X':
+			di->di_printf("%s", insn_blktrans(insn));
+			break;
+		/* Y - block transfer type (r13 base) */
+		case 'Y':
+			di->di_printf("%s", insn_stkblktrans(insn));
+			break;
+		/* c - comment field bits(0-23) */
+		case 'c':
+			di->di_printf("0x%08x", (insn & 0x00ffffff));
+			break;
+		/* k - breakpoint comment (bits 0-3, 8-19) */
+		case 'k':
+			di->di_printf("0x%04x",
+			    (insn & 0x000fff00) >> 4 | (insn & 0x0000000f));
+			break;
+		/* p - saved or current status register */
+		case 'p':
+			if (insn & 0x00400000)
+				di->di_printf("spsr");
+			else
+				di->di_printf("cpsr");
+			break;
+		/* F - PSR transfer fields */
+		case 'F':
+			di->di_printf("_");
+			if (insn & (1 << 16))
+				di->di_printf("c");
+			if (insn & (1 << 17))
+				di->di_printf("x");
+			if (insn & (1 << 18))
+				di->di_printf("s");
+			if (insn & (1 << 19))
+				di->di_printf("f");
+			break;
+		/* B - byte transfer flag */
+		case 'B':
+			if (insn & 0x00400000)
+				di->di_printf("b");
+			break;
+		/* L - co-processor transfer size */
+		case 'L':
+			if (insn & (1 << 22))
+				di->di_printf("l");
+			break;
+		/* S - set status flag */
+		case 'S':
+			if (insn & 0x00100000)
+				di->di_printf("s");
+			break;
+		/* P - fp precision */
+		case 'P':
+			di->di_printf("%s", insn_fpaprec(insn));
+			break;
+		/* Q - fp precision (for ldf/stf) */
+		case 'Q':
+			break;
+		/* R - fp rounding */
+		case 'R':
+			di->di_printf("%s", insn_fparnd(insn));
+			break;
+		/* W - writeback flag */
+		case 'W':
+			if (insn & (1 << 21))
+				di->di_printf("!");
+			break;
+		/* # - co-processor number */
+		case '#':
+			di->di_printf("p%d", (insn >> 8) & 0x0f);
+			break;
+		/* v - co-processor data transfer registers+addressing mode */
+		case 'v':
+			disasm_insn_ldcstc(di, insn, loc);
+			break;
+		/* x - instruction in hex */
+		case 'x':
+			di->di_printf("0x%08x", insn);
+			break;
+		/* y - co-processor data processing registers */
+		case 'y':
+			di->di_printf("%d, ", (insn >> 20) & 0x0f);
+
+			di->di_printf("c%d, c%d, c%d", (insn >> 12) & 0x0f,
+			    (insn >> 16) & 0x0f, insn & 0x0f);
+
+			di->di_printf(", %d", (insn >> 5) & 0x07);
+			break;
+		/* z - co-processor register transfer registers */
+		case 'z':
+			di->di_printf("%d, ", (insn >> 21) & 0x07);
+			di->di_printf("r%d, c%d, c%d, %d",
+			    (insn >> 12) & 0x0f, (insn >> 16) & 0x0f,
+			    insn & 0x0f, (insn >> 5) & 0x07);
+
+/*			if (((insn >> 5) & 0x07) != 0)
+				di->di_printf(", %d", (insn >> 5) & 0x07);*/
+			break;
+		default:
+			di->di_printf("[%c - unknown]", *f_ptr);
+			break;
+		}
+		if (*(f_ptr+1) >= 'A' && *(f_ptr+1) <= 'Z')
+			++f_ptr;
+		else if (*(++f_ptr)) {
+			++fmt;
+			if (fmt == 1)
+				di->di_printf("\t");
+			else
+				di->di_printf(", ");
+		}
+	};
+
+	di->di_printf("\n");
+
+	return(loc + INSN_SIZE);
+}
+
+
+static void
+disasm_register_shift(const disasm_interface_t *di, u_int insn)
+{
+	di->di_printf("r%d", (insn & 0x0f));
+	if ((insn & 0x00000ff0) == 0)
+		;
+	else if ((insn & 0x00000ff0) == 0x00000060)
+		di->di_printf(", rrx");
+	else {
+		if (insn & 0x10)
+			di->di_printf(", %s r%d", op2_shift(insn),
+			    (insn >> 8) & 0x0f);
+		else
+			di->di_printf(", %s #%d", op2_shift(insn),
+			    (insn >> 7) & 0x1f);
+	}
+}
+
+
+static void
+disasm_print_reglist(const disasm_interface_t *di, u_int insn)
+{
+	int loop;
+	int start;
+	int comma;
+
+	di->di_printf("{");
+	start = -1;
+	comma = 0;
+
+	for (loop = 0; loop < 17; ++loop) {
+		if (start != -1) {
+			if (loop == 16 || !(insn & (1 << loop))) {
+				if (comma)
+					di->di_printf(", ");
+				else
+					comma = 1;
+        			if (start == loop - 1)
+        				di->di_printf("r%d", start);
+        			else
+        				di->di_printf("r%d-r%d", start, loop - 1);
+        			start = -1;
+        		}
+        	} else {
+        		if (insn & (1 << loop))
+        			start = loop;
+        	}
+        }
+	di->di_printf("}");
+
+	if (insn & (1 << 22))
+		di->di_printf("^");
+}
+
+static void
+disasm_insn_ldrstr(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+	int offset;
+
+	offset = insn & 0xfff;
+	if ((insn & 0x032f0000) == 0x010f0000) {
+		/* rA = pc, immediate index */
+		if (insn & 0x00800000)
+			loc += offset;
+		else
+			loc -= offset;
+		di->di_printaddr(loc + 8);
+ 	} else {
+		di->di_printf("[r%d", (insn >> 16) & 0x0f);
+		if ((insn & 0x03000fff) != 0x01000000) {
+			di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+			if (!(insn & 0x00800000))
+				di->di_printf("-");
+			if (insn & (1 << 25))
+				disasm_register_shift(di, insn);
+			else
+				di->di_printf("#0x%03x", offset);
+		}
+		if (insn & (1 << 24))
+			di->di_printf("]");
+	}
+}
+
+static void
+disasm_insn_ldrhstrh(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+	int offset;
+
+	offset = ((insn & 0xf00) >> 4) | (insn & 0xf);
+	if ((insn & 0x004f0000) == 0x004f0000) {
+		/* rA = pc, immediate index */
+		if (insn & 0x00800000)
+			loc += offset;
+		else
+			loc -= offset;
+		di->di_printaddr(loc + 8);
+ 	} else {
+		di->di_printf("[r%d", (insn >> 16) & 0x0f);
+		if ((insn & 0x01400f0f) != 0x01400000) {
+			di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+			if (!(insn & 0x00800000))
+				di->di_printf("-");
+			if (insn & (1 << 22))
+				di->di_printf("#0x%02x", offset);
+			else
+				di->di_printf("r%d", (insn & 0x0f));
+		}
+		if (insn & (1 << 24))
+			di->di_printf("]");
+	}
+}
+
+static void
+disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc)
+{
+	if (((insn >> 8) & 0xf) == 1)
+		di->di_printf("f%d, ", (insn >> 12) & 0x07);
+	else
+		di->di_printf("c%d, ", (insn >> 12) & 0x0f);
+
+	di->di_printf("[r%d", (insn >> 16) & 0x0f);
+
+	di->di_printf("%s, ", (insn & (1 << 24)) ? "" : "]");
+
+	if (!(insn & (1 << 23)))
+		di->di_printf("-");
+
+	di->di_printf("#0x%03x", (insn & 0xff) << 2);
+
+	if (insn & (1 << 24))
+		di->di_printf("]");
+
+	if (insn & (1 << 21))
+		di->di_printf("!");
+}
+
+static u_int
+disassemble_readword(u_int address)
+{
+	return(*((u_int *)address));
+}
+
+static void
+disassemble_printaddr(u_int address)
+{
+	printf("0x%08x", address);
+}
+
+static const disasm_interface_t disassemble_di = {
+	disassemble_readword, disassemble_printaddr, printf
+};
+
+void
+disassemble(u_int address)
+{
+
+	(void)disasm(&disassemble_di, address, 0);
+}
+
+/* End of disassem.c */
diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h
new file mode 100644
index 0000000..02747cd
--- /dev/null
+++ b/libpixelflinger/codeflinger/disassem.h
@@ -0,0 +1,65 @@
+/*	$NetBSD: disassem.h,v 1.4 2001/03/04 04:15:58 matt Exp $	*/
+
+/*-
+ * Copyright (c) 1997 Mark Brinicombe.
+ * Copyright (c) 1997 Causality Limited.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Mark Brinicombe.
+ * 4. The name of the company nor the name of the author may be used to
+ *    endorse or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Define the interface structure required by the disassembler.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/arm/include/disassem.h,v 1.2 2005/01/05 21:58:48 imp Exp $
+ */
+
+#ifndef ANDROID_MACHINE_DISASSEM_H
+#define ANDROID_MACHINE_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	u_int	(*di_readword)(u_int);
+	void	(*di_printaddr)(u_int);	
+	void	(*di_printf)(const char *, ...);
+} disasm_interface_t;
+
+/* Prototypes for callable functions */
+
+u_int disasm(const disasm_interface_t *, u_int, int);
+void disassemble(u_int);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MACHINE_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
new file mode 100644
index 0000000..93c5825
--- /dev/null
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -0,0 +1,378 @@
+/* libs/pixelflinger/codeflinger/load_store.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <cutils/log.h>
+
+#include "codeflinger/GGLAssembler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void GGLAssembler::store(const pointer_t& addr, const pixel_t& s, uint32_t flags)
+{    
+    const int bits = addr.size;
+    const int inc = (flags & WRITE_BACK)?1:0;
+    switch (bits) {
+    case 32:
+        if (inc)    STR(AL, s.reg, addr.reg, immed12_post(4));
+        else        STR(AL, s.reg, addr.reg);
+        break;
+    case 24:
+        // 24 bits formats are a little special and used only for RGB
+        // 0x00BBGGRR is unpacked as R,G,B
+        STRB(AL, s.reg, addr.reg, immed12_pre(0));
+        MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8));
+        STRB(AL, s.reg, addr.reg, immed12_pre(1));
+        MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 8));
+        STRB(AL, s.reg, addr.reg, immed12_pre(2));
+        if (!(s.flags & CORRUPTIBLE)) {
+            MOV(AL, 0, s.reg, reg_imm(s.reg, ROR, 16));
+        }
+        if (inc)
+            ADD(AL, 0, addr.reg, addr.reg, imm(3));
+        break;
+    case 16:
+        if (inc)    STRH(AL, s.reg, addr.reg, immed8_post(2));
+        else        STRH(AL, s.reg, addr.reg);
+        break;
+    case  8:
+        if (inc)    STRB(AL, s.reg, addr.reg, immed12_post(1));
+        else        STRB(AL, s.reg, addr.reg);
+        break;
+    }
+}
+
+void GGLAssembler::load(const pointer_t& addr, const pixel_t& s, uint32_t flags)
+{    
+    Scratch scratches(registerFile());    
+    int s0;
+
+    const int bits = addr.size;
+    const int inc = (flags & WRITE_BACK)?1:0;
+    switch (bits) {
+    case 32:
+        if (inc)    LDR(AL, s.reg, addr.reg, immed12_post(4));
+        else        LDR(AL, s.reg, addr.reg);
+        break;
+    case 24:
+        // 24 bits formats are a little special and used only for RGB
+        // R,G,B is packed as 0x00BBGGRR 
+        s0 = scratches.obtain();
+        if (s.reg != addr.reg) {
+            LDRB(AL, s.reg, addr.reg, immed12_pre(0));      // R
+            LDRB(AL, s0, addr.reg, immed12_pre(1));         // G
+            ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 8));
+            LDRB(AL, s0, addr.reg, immed12_pre(2));         // B
+            ORR(AL, 0, s.reg, s.reg, reg_imm(s0, LSL, 16));
+        } else {
+            int s1 = scratches.obtain();
+            LDRB(AL, s1, addr.reg, immed12_pre(0));         // R
+            LDRB(AL, s0, addr.reg, immed12_pre(1));         // G
+            ORR(AL, 0, s1, s1, reg_imm(s0, LSL, 8));
+            LDRB(AL, s0, addr.reg, immed12_pre(2));         // B
+            ORR(AL, 0, s.reg, s1, reg_imm(s0, LSL, 16));
+        }
+        if (inc)
+            ADD(AL, 0, addr.reg, addr.reg, imm(3));
+        break;        
+    case 16:
+        if (inc)    LDRH(AL, s.reg, addr.reg, immed8_post(2));
+        else        LDRH(AL, s.reg, addr.reg);
+        break;
+    case  8:
+        if (inc)    LDRB(AL, s.reg, addr.reg, immed12_post(1));
+        else        LDRB(AL, s.reg, addr.reg);
+        break;
+    }
+}
+
+void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits)
+{
+    const int maskLen = h-l;
+
+    assert(maskLen<=8);
+    assert(h);
+    
+    if (h != bits) {
+        const int mask = ((1<<maskLen)-1) << l;
+        if (isValidImmediate(mask)) {
+            AND(AL, 0, d.reg, s, imm(mask));    // component = packed & mask;
+        } else if (isValidImmediate(~mask)) {
+            BIC(AL, 0, d.reg, s, imm(~mask));   // component = packed & mask;
+        } else {
+            MOV(AL, 0, d.reg, reg_imm(s, LSL, 32-h));
+            l += 32-h;
+            h = 32;
+        }
+        s = d.reg;
+    }
+    
+    if (l) {
+        MOV(AL, 0, d.reg, reg_imm(s, LSR, l));  // component = packed >> l;
+        s = d.reg;
+    }
+    
+    if (s != d.reg) {
+        MOV(AL, 0, d.reg, s);
+    }
+
+    d.s = maskLen;
+}
+
+void GGLAssembler::extract(integer_t& d, const pixel_t& s, int component)
+{
+    extract(d,  s.reg,
+                s.format.c[component].h,
+                s.format.c[component].l,
+                s.size());
+}
+
+void GGLAssembler::extract(component_t& d, const pixel_t& s, int component)
+{
+    integer_t r(d.reg, 32, d.flags);
+    extract(r,  s.reg,
+                s.format.c[component].h,
+                s.format.c[component].l,
+                s.size());
+    d = component_t(r);
+}
+
+
+void GGLAssembler::expand(integer_t& d, const component_t& s, int dbits)
+{
+    if (s.l || (s.flags & CLEAR_HI)) {
+        extract(d, s.reg, s.h, s.l, 32);
+        expand(d, d, dbits);
+    } else {
+        expand(d, integer_t(s.reg, s.size(), s.flags), dbits);
+    }
+}
+
+void GGLAssembler::expand(component_t& d, const component_t& s, int dbits)
+{
+    integer_t r(d.reg, 32, d.flags);
+    expand(r, s, dbits);
+    d = component_t(r);
+}
+
+void GGLAssembler::expand(integer_t& dst, const integer_t& src, int dbits)
+{
+    assert(src.size());
+
+    int sbits = src.size();
+    int s = src.reg;
+    int d = dst.reg;
+
+    // be sure to set 'dst' after we read 'src' as they may be identical
+    dst.s = dbits;
+    dst.flags = 0;
+
+    if (dbits<=sbits) {
+        if (s != d) {
+            MOV(AL, 0, d, s);
+        }
+        return;
+    }
+
+    if (sbits == 1) {
+        RSB(AL, 0, d, s, reg_imm(s, LSL, dbits));
+            // d = (s<<dbits) - s;
+        return;
+    }
+
+    if (dbits % sbits) {
+        MOV(AL, 0, d, reg_imm(s, LSL, dbits-sbits));
+            // d = s << (dbits-sbits);
+        dbits -= sbits;
+        do {
+            ORR(AL, 0, d, d, reg_imm(d, LSR, sbits));
+                // d |= d >> sbits;
+            dbits -= sbits;
+            sbits *= 2;
+        } while(dbits>0);
+        return;
+    }
+    
+    dbits -= sbits;
+    do {
+        ORR(AL, 0, d, s, reg_imm(s, LSL, sbits));
+            // d |= d<<sbits;
+        s = d;        
+        dbits -= sbits;
+        if (sbits*2 < dbits) {
+            sbits *= 2;
+        }
+    } while(dbits>0);
+}
+
+void GGLAssembler::downshift(
+        pixel_t& d, int component, component_t s, const reg_t& dither)
+{
+    const needs_t& needs = mBuilderContext.needs;
+    Scratch scratches(registerFile());
+
+    int sh = s.h;
+    int sl = s.l;
+    int maskHiBits = (sh!=32) ? ((s.flags & CLEAR_HI)?1:0) : 0;
+    int maskLoBits = (sl!=0)  ? ((s.flags & CLEAR_LO)?1:0) : 0;
+    int sbits = sh - sl;
+
+    int dh = d.format.c[component].h;
+    int dl = d.format.c[component].l;
+    int dbits = dh - dl;
+    int dithering = 0;
+    
+    LOGE_IF(sbits<dbits, "sbits (%d) < dbits (%d) in downshift", sbits, dbits);
+
+    if (sbits>dbits) {
+        // see if we need to dither
+        dithering = mDithering;
+    }
+    
+    int ireg = d.reg;
+    if (!(d.flags & FIRST)) {
+        if (s.flags & CORRUPTIBLE)  {
+            ireg = s.reg;
+        } else {
+            ireg = scratches.obtain();
+        }
+    }
+    d.flags &= ~FIRST;
+
+    if (maskHiBits) {
+        // we need to mask the high bits (and possibly the lowbits too)
+        // and we might be able to use immediate mask.
+        if (!dithering) {
+            // we don't do this if we only have maskLoBits because we can
+            // do it more efficiently below (in the case where dl=0)
+            const int offset = sh - dbits;
+            if (dbits<=8 && offset >= 0) {
+                const uint32_t mask = ((1<<dbits)-1) << offset;
+                if (isValidImmediate(mask) || isValidImmediate(~mask)) {
+                    build_and_immediate(ireg, s.reg, mask, 32);
+                    sl = offset;
+                    s.reg = ireg; 
+                    sbits = dbits;
+                    maskLoBits = maskHiBits = 0;
+                }
+            }
+        } else {
+            // in the dithering case though, we need to preserve the lower bits
+            const uint32_t mask = ((1<<sbits)-1) << sl;
+            if (isValidImmediate(mask) || isValidImmediate(~mask)) {
+                build_and_immediate(ireg, s.reg, mask, 32);
+                s.reg = ireg; 
+                maskLoBits = maskHiBits = 0;
+            }
+        }
+    }
+
+    // XXX: we could special case (maskHiBits & !maskLoBits)
+    // like we do for maskLoBits below, but it happens very rarely
+    // that we have maskHiBits only and the conditions necessary to lead
+    // to better code (like doing d |= s << 24)
+
+    if (maskHiBits) {
+        MOV(AL, 0, ireg, reg_imm(s.reg, LSL, 32-sh));
+        sl += 32-sh;
+        sh = 32;
+        s.reg = ireg;
+        maskHiBits = 0;
+    }
+
+    //	Downsampling should be performed as follows:
+    //  V * ((1<<dbits)-1) / ((1<<sbits)-1)
+    //	V * [(1<<dbits)/((1<<sbits)-1)	-	1/((1<<sbits)-1)]
+    //	V * [1/((1<<sbits)-1)>>dbits	-	1/((1<<sbits)-1)]
+    //	V/((1<<(sbits-dbits))-(1>>dbits))	-	(V>>sbits)/((1<<sbits)-1)>>sbits
+    //	V/((1<<(sbits-dbits))-(1>>dbits))	-	(V>>sbits)/(1-(1>>sbits))
+    //
+    //	By approximating (1>>dbits) and (1>>sbits) to 0:
+    //
+    //		V>>(sbits-dbits)	-	V>>sbits
+    //
+	//  A good approximation is V>>(sbits-dbits),
+    //  but better one (needed for dithering) is:
+    //
+    //		(V>>(sbits-dbits)<<sbits	-	V)>>sbits
+    //		(V<<dbits	-	V)>>sbits
+    //		(V	-	V>>dbits)>>(sbits-dbits)
+
+    // Dithering is done here
+    if (dithering) {
+        comment("dithering");
+        if (sl) {
+            MOV(AL, 0, ireg, reg_imm(s.reg, LSR, sl));
+            sh -= sl;
+            sl = 0;
+            s.reg = ireg; 
+        }
+        // scaling (V-V>>dbits)
+        SUB(AL, 0, ireg, s.reg, reg_imm(s.reg, LSR, dbits));
+        const int shift = (GGL_DITHER_BITS - (sbits-dbits));
+        if (shift>0)        ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSR, shift));
+        else if (shift<0)   ADD(AL, 0, ireg, ireg, reg_imm(dither.reg, LSL,-shift));
+        else                ADD(AL, 0, ireg, ireg, dither.reg);
+        s.reg = ireg; 
+    }
+
+    if ((maskLoBits|dithering) && (sh > dbits)) {
+        int shift = sh-dbits;
+        if (dl) {
+            MOV(AL, 0, ireg, reg_imm(s.reg, LSR, shift));
+            if (ireg == d.reg) {
+                MOV(AL, 0, d.reg, reg_imm(ireg, LSL, dl));
+            } else {
+                ORR(AL, 0, d.reg, d.reg, reg_imm(ireg, LSL, dl));
+            }
+        } else {
+            if (ireg == d.reg) {
+                MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift));
+            } else {
+                ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift));
+            }
+        }
+    } else {
+        int shift = sh-dh;
+        if (shift>0) {
+            if (ireg == d.reg) {
+                MOV(AL, 0, d.reg, reg_imm(s.reg, LSR, shift));
+            } else {
+                ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSR, shift));
+            }
+        } else if (shift<0) {
+            if (ireg == d.reg) {
+                MOV(AL, 0, d.reg, reg_imm(s.reg, LSL, -shift));
+            } else {
+                ORR(AL, 0, d.reg, d.reg, reg_imm(s.reg, LSL, -shift));
+            }
+        } else {
+            if (ireg == d.reg) {
+                if (s.reg != d.reg) {
+                    MOV(AL, 0, d.reg, s.reg);
+                }
+            } else {
+                ORR(AL, 0, d.reg, d.reg, s.reg);
+            }
+        }
+    }
+}
+
+}; // namespace android
diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp
new file mode 100644
index 0000000..90e6584
--- /dev/null
+++ b/libpixelflinger/codeflinger/texturing.cpp
@@ -0,0 +1,1251 @@
+/* libs/pixelflinger/codeflinger/texturing.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "codeflinger/GGLAssembler.h"
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+// iterators are initialized like this:
+// (intToFixedCenter(x) * dx)>>16 + x0
+// ((x<<16 + 0x8000) * dx)>>16 + x0
+// ((x<<16)*dx + (0x8000*dx))>>16 + x0
+// ( (x*dx) + dx>>1 ) + x0
+// (x*dx) + (dx>>1 + x0)
+
+void GGLAssembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x)
+{
+    context_t const* c = mBuilderContext.c;
+    const needs_t& needs = mBuilderContext.needs;
+
+    if (mSmooth) {
+        // NOTE: we could take this case in the mDithering + !mSmooth case,
+        // but this would use up to 4 more registers for the color components
+        // for only a little added quality.
+        // Currently, this causes the system to run out of registers in
+        // some case (see issue #719496)
+
+        comment("compute initial iterated color (smooth and/or dither case)");
+
+        parts.iterated_packed = 0;
+        parts.packed = 0;
+
+        // 0x1: color component
+        // 0x2: iterators
+        const int optReload = mOptLevel >> 1;
+        if (optReload >= 3)         parts.reload = 0; // reload nothing
+        else if (optReload == 2)    parts.reload = 2; // reload iterators
+        else if (optReload == 1)    parts.reload = 1; // reload colors
+        else if (optReload <= 0)    parts.reload = 3; // reload both
+
+        if (!mSmooth) {
+            // we're not smoothing (just dithering), we never have to 
+            // reload the iterators
+            parts.reload &= ~2;
+        }
+
+        Scratch scratches(registerFile());
+        const int t0 = (parts.reload & 1) ? scratches.obtain() : 0;
+        const int t1 = (parts.reload & 2) ? scratches.obtain() : 0;
+        for (int i=0 ; i<4 ; i++) {
+            if (!mInfo[i].iterated)
+                continue;            
+            
+            // this component exists in the destination and is not replaced
+            // by a texture unit.
+            const int c = (parts.reload & 1) ? t0 : obtainReg();              
+            if (i==0) CONTEXT_LOAD(c, iterators.ydady);
+            if (i==1) CONTEXT_LOAD(c, iterators.ydrdy);
+            if (i==2) CONTEXT_LOAD(c, iterators.ydgdy);
+            if (i==3) CONTEXT_LOAD(c, iterators.ydbdy);
+            parts.argb[i].reg = c;
+
+            if (mInfo[i].smooth) {
+                parts.argb_dx[i].reg = (parts.reload & 2) ? t1 : obtainReg();
+                const int dvdx = parts.argb_dx[i].reg;
+                CONTEXT_LOAD(dvdx, generated_vars.argb[i].dx);
+                MLA(AL, 0, c, x.reg, dvdx, c);
+                
+                // adjust the color iterator to make sure it won't overflow
+                if (!mAA) {
+                    // this is not needed when we're using anti-aliasing
+                    // because we will (have to) clamp the components
+                    // anyway.
+                    int end = scratches.obtain();
+                    MOV(AL, 0, end, reg_imm(parts.count.reg, LSR, 16));
+                    MLA(AL, 1, end, dvdx, end, c);
+                    SUB(MI, 0, c, c, end);
+                    BIC(AL, 0, c, c, reg_imm(c, ASR, 31)); 
+                    scratches.recycle(end);
+                }
+            }
+            
+            if (parts.reload & 1) {
+                CONTEXT_STORE(c, generated_vars.argb[i].c);
+            }
+        }
+    } else {
+        // We're not smoothed, so we can 
+        // just use a packed version of the color and extract the
+        // components as needed (or not at all if we don't blend)
+
+        // figure out if we need the iterated color
+        int load = 0;
+        for (int i=0 ; i<4 ; i++) {
+            component_info_t& info = mInfo[i];
+            if ((info.inDest || info.needed) && !info.replaced)
+                load |= 1;
+        }
+        
+        parts.iterated_packed = 1;
+        parts.packed = (!mTextureMachine.mask && !mBlending
+                && !mFog && !mDithering);
+        parts.reload = 0;
+        if (load || parts.packed) {
+            if (mBlending || mDithering || mInfo[GGLFormat::ALPHA].needed) {
+                comment("load initial iterated color (8888 packed)");
+                parts.iterated.setTo(obtainReg(),
+                        &(c->formats[GGL_PIXEL_FORMAT_RGBA_8888]));
+                CONTEXT_LOAD(parts.iterated.reg, packed8888);
+            } else {
+                comment("load initial iterated color (dest format packed)");
+
+                parts.iterated.setTo(obtainReg(), &mCbFormat);
+
+                // pre-mask the iterated color
+                const int bits = parts.iterated.size();
+                const uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1;
+                uint32_t mask = 0;
+                if (mMasking) {
+                    for (int i=0 ; i<4 ; i++) {
+                        const int component_mask = 1<<i;
+                        const int h = parts.iterated.format.c[i].h;
+                        const int l = parts.iterated.format.c[i].l;
+                        if (h && (!(mMasking & component_mask))) {
+                            mask |= ((1<<(h-l))-1) << l;
+                        }
+                    }
+                }
+
+                if (mMasking && ((mask & size)==0)) {
+                    // none of the components are present in the mask
+                } else {
+                    CONTEXT_LOAD(parts.iterated.reg, packed);
+                    if (mCbFormat.size == 1) {
+                        AND(AL, 0, parts.iterated.reg,
+                                parts.iterated.reg, imm(0xFF));
+                    } else if (mCbFormat.size == 2) {
+                        MOV(AL, 0, parts.iterated.reg,
+                                reg_imm(parts.iterated.reg, LSR, 16));
+                    }
+                }
+
+                // pre-mask the iterated color
+                if (mMasking) {
+                    build_and_immediate(parts.iterated.reg, parts.iterated.reg,
+                            mask, bits);
+                }
+            }
+        }
+    }
+}
+
+void GGLAssembler::build_iterated_color(
+        component_t& fragment,
+        const fragment_parts_t& parts,
+        int component,
+        Scratch& regs)
+{
+    fragment.setTo( regs.obtain(), 0, 32, CORRUPTIBLE); 
+
+    if (!mInfo[component].iterated)
+        return;
+
+    if (parts.iterated_packed) {
+        // iterated colors are packed, extract the one we need
+        extract(fragment, parts.iterated, component);
+    } else {
+        fragment.h = GGL_COLOR_BITS;
+        fragment.l = GGL_COLOR_BITS - 8;
+        fragment.flags |= CLEAR_LO;
+        // iterated colors are held in their own register,
+        // (smooth and/or dithering case)
+        if (parts.reload==3) {
+            // this implies mSmooth
+            Scratch scratches(registerFile());
+            int dx = scratches.obtain();
+            CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c);
+            CONTEXT_LOAD(dx, generated_vars.argb[component].dx);
+            ADD(AL, 0, dx, fragment.reg, dx);
+            CONTEXT_STORE(dx, generated_vars.argb[component].c);
+        } else if (parts.reload & 1) {
+            CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c);
+        } else {
+            // we don't reload, so simply rename the register and mark as
+            // non CORRUPTIBLE so that the texture env or blending code
+            // won't modify this (renamed) register
+            regs.recycle(fragment.reg);
+            fragment.reg = parts.argb[component].reg;
+            fragment.flags &= ~CORRUPTIBLE;
+        }
+        if (mInfo[component].smooth && mAA) {
+            // when using smooth shading AND anti-aliasing, we need to clamp
+            // the iterators because there is always an extra pixel on the
+            // edges, which most of the time will cause an overflow
+            // (since technically its outside of the domain).
+            BIC(AL, 0, fragment.reg, fragment.reg,
+                    reg_imm(fragment.reg, ASR, 31));
+            component_sat(fragment);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::decodeLogicOpNeeds(const needs_t& needs)
+{
+    // gather some informations about the components we need to process...
+    const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR;
+    switch(opcode) {
+    case GGL_COPY:
+        mLogicOp = 0;
+        break;
+    case GGL_CLEAR:
+    case GGL_SET:
+        mLogicOp = LOGIC_OP;
+        break;
+    case GGL_AND:
+    case GGL_AND_REVERSE:
+    case GGL_AND_INVERTED:
+    case GGL_XOR:
+    case GGL_OR:
+    case GGL_NOR:
+    case GGL_EQUIV:
+    case GGL_OR_REVERSE:
+    case GGL_OR_INVERTED:
+    case GGL_NAND:
+        mLogicOp = LOGIC_OP|LOGIC_OP_SRC|LOGIC_OP_DST;
+        break;
+    case GGL_NOOP:
+    case GGL_INVERT:
+        mLogicOp = LOGIC_OP|LOGIC_OP_DST;
+        break;        
+    case GGL_COPY_INVERTED:
+        mLogicOp = LOGIC_OP|LOGIC_OP_SRC;
+        break;
+    };        
+}
+
+void GGLAssembler::decodeTMUNeeds(const needs_t& needs, context_t const* c)
+{
+    uint8_t replaced=0;
+    mTextureMachine.mask = 0;
+    mTextureMachine.activeUnits = 0;
+    for (int i=GGL_TEXTURE_UNIT_COUNT-1 ; i>=0 ; i--) {
+        texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if (replaced == 0xF) {
+            // all components are replaced, skip this TMU.
+            tmu.format_idx = 0;
+            tmu.mask = 0;
+            tmu.replaced = replaced;
+            continue;
+        }
+        tmu.format_idx = GGL_READ_NEEDS(T_FORMAT, needs.t[i]);
+        tmu.format = c->formats[tmu.format_idx];
+        tmu.bits = tmu.format.size*8;
+        tmu.swrap = GGL_READ_NEEDS(T_S_WRAP, needs.t[i]);
+        tmu.twrap = GGL_READ_NEEDS(T_T_WRAP, needs.t[i]);
+        tmu.env = ggl_needs_to_env(GGL_READ_NEEDS(T_ENV, needs.t[i]));
+        tmu.pot = GGL_READ_NEEDS(T_POT, needs.t[i]);
+        tmu.linear = GGL_READ_NEEDS(T_LINEAR, needs.t[i])
+                && tmu.format.size!=3; // XXX: only 8, 16 and 32 modes for now
+
+        // 5551 linear filtering is not supported
+        if (tmu.format_idx == GGL_PIXEL_FORMAT_RGBA_5551)
+            tmu.linear = 0;
+        
+        tmu.mask = 0;
+        tmu.replaced = replaced;
+
+        if (tmu.format_idx) {
+            mTextureMachine.activeUnits++;
+            if (tmu.format.c[0].h)    tmu.mask |= 0x1;
+            if (tmu.format.c[1].h)    tmu.mask |= 0x2;
+            if (tmu.format.c[2].h)    tmu.mask |= 0x4;
+            if (tmu.format.c[3].h)    tmu.mask |= 0x8;
+            if (tmu.env == GGL_REPLACE) {
+                replaced |= tmu.mask;
+            } else if (tmu.env == GGL_DECAL) {
+                if (!tmu.format.c[GGLFormat::ALPHA].h) {
+                    // if we don't have alpha, decal does nothing
+                    tmu.mask = 0;
+                } else {
+                    // decal always ignores At
+                    tmu.mask &= ~(1<<GGLFormat::ALPHA);
+                }
+            }
+        }
+        mTextureMachine.mask |= tmu.mask;
+        //printf("%d: mask=%08lx, replaced=%08lx\n",
+        //    i, int(tmu.mask), int(tmu.replaced));
+    }
+    mTextureMachine.replaced = replaced;
+    mTextureMachine.directTexture = 0;
+    //printf("replaced=%08lx\n", mTextureMachine.replaced);
+}
+
+
+void GGLAssembler::init_textures(
+        tex_coord_t* coords,
+        const reg_t& x, const reg_t& y)
+{
+    context_t const* c = mBuilderContext.c;
+    const needs_t& needs = mBuilderContext.needs;
+    int Rctx = mBuilderContext.Rctx;
+    int Rx = x.reg;
+    int Ry = y.reg;
+
+    if (mTextureMachine.mask) {
+        comment("compute texture coordinates");
+    }
+
+    // init texture coordinates for each tmu
+    const int cb_format_idx = GGL_READ_NEEDS(CB_FORMAT, needs.n);
+    const bool multiTexture = mTextureMachine.activeUnits > 1;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+        const texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if (tmu.format_idx == 0)
+            continue;
+        if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+            (tmu.twrap == GGL_NEEDS_WRAP_11)) 
+        {
+            // 1:1 texture
+            pointer_t& txPtr = coords[i].ptr;
+            txPtr.setTo(obtainReg(), tmu.bits);
+            CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydsdy);
+            ADD(AL, 0, Rx, Rx, reg_imm(txPtr.reg, ASR, 16));    // x += (s>>16)
+            CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydtdy);
+            ADD(AL, 0, Ry, Ry, reg_imm(txPtr.reg, ASR, 16));    // y += (t>>16)
+            // merge base & offset
+            CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride);
+            SMLABB(AL, Rx, Ry, txPtr.reg, Rx);               // x+y*stride
+            CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data);
+            base_offset(txPtr, txPtr, Rx);
+        } else {
+            Scratch scratches(registerFile());
+            reg_t& s = coords[i].s;
+            reg_t& t = coords[i].t;
+            // s = (x * dsdx)>>16 + ydsdy
+            // s = (x * dsdx)>>16 + (y*dsdy)>>16 + s0
+            // t = (x * dtdx)>>16 + ydtdy
+            // t = (x * dtdx)>>16 + (y*dtdy)>>16 + t0
+            s.setTo(obtainReg());
+            t.setTo(obtainReg());
+            const int need_w = GGL_READ_NEEDS(W, needs.n);
+            if (need_w) {
+                CONTEXT_LOAD(s.reg, state.texture[i].iterators.ydsdy);
+                CONTEXT_LOAD(t.reg, state.texture[i].iterators.ydtdy);
+            } else {
+                int ydsdy = scratches.obtain();
+                int ydtdy = scratches.obtain();
+                CONTEXT_LOAD(s.reg, generated_vars.texture[i].dsdx);
+                CONTEXT_LOAD(ydsdy, state.texture[i].iterators.ydsdy);
+                CONTEXT_LOAD(t.reg, generated_vars.texture[i].dtdx);
+                CONTEXT_LOAD(ydtdy, state.texture[i].iterators.ydtdy);
+                MLA(AL, 0, s.reg, Rx, s.reg, ydsdy);
+                MLA(AL, 0, t.reg, Rx, t.reg, ydtdy);
+            }
+            
+            if ((mOptLevel&1)==0) {
+                CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]);
+                CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]);
+                recycleReg(s.reg);
+                recycleReg(t.reg);
+            }
+        }
+
+        // direct texture?
+        if (!multiTexture && !mBlending && !mDithering && !mFog && 
+            cb_format_idx == tmu.format_idx && !tmu.linear &&
+            mTextureMachine.replaced == tmu.mask) 
+        {
+                mTextureMachine.directTexture = i + 1; 
+        }
+    }
+}
+
+void GGLAssembler::build_textures(  fragment_parts_t& parts,
+                                    Scratch& regs)
+{
+    context_t const* c = mBuilderContext.c;
+    const needs_t& needs = mBuilderContext.needs;
+    int Rctx = mBuilderContext.Rctx;
+
+    // We don't have a way to spill registers automatically
+    // spill depth and AA regs, when we know we may have to.
+    // build the spill list...
+    uint32_t spill_list = 0;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+        const texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if (tmu.format_idx == 0)
+            continue;
+        if (tmu.linear) {
+            // we may run out of register if we have linear filtering
+            // at 1 or 4 bytes / pixel on any texture unit.
+            if (tmu.format.size == 1) {
+                // if depth and AA enabled, we'll run out of 1 register
+                if (parts.z.reg > 0 && parts.covPtr.reg > 0)
+                    spill_list |= 1<<parts.covPtr.reg;
+            }
+            if (tmu.format.size == 4) {
+                // if depth or AA enabled, we'll run out of 1 or 2 registers
+                if (parts.z.reg > 0)
+                    spill_list |= 1<<parts.z.reg;
+                if (parts.covPtr.reg > 0)   
+                    spill_list |= 1<<parts.covPtr.reg;
+            }
+        }
+    }
+
+    Spill spill(registerFile(), *this, spill_list);
+
+    const bool multiTexture = mTextureMachine.activeUnits > 1;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+        const texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if (tmu.format_idx == 0)
+            continue;
+
+        pointer_t& txPtr = parts.coords[i].ptr;
+        pixel_t& texel = parts.texel[i];
+            
+        // repeat...
+        if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+            (tmu.twrap == GGL_NEEDS_WRAP_11))
+        { // 1:1 textures
+            comment("fetch texel");
+            texel.setTo(regs.obtain(), &tmu.format);
+            load(txPtr, texel, WRITE_BACK);
+        } else {
+            Scratch scratches(registerFile());
+            reg_t& s = parts.coords[i].s;
+            reg_t& t = parts.coords[i].t;
+            if ((mOptLevel&1)==0) {
+                comment("reload s/t (multitexture or linear filtering)");
+                s.reg = scratches.obtain();
+                t.reg = scratches.obtain();
+                CONTEXT_LOAD(s.reg, generated_vars.texture[i].spill[0]);
+                CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]);
+            }
+
+            comment("compute repeat/clamp");
+            int u       = scratches.obtain();
+            int v       = scratches.obtain();
+            int width   = scratches.obtain();
+            int height  = scratches.obtain();
+            int U = 0;
+            int V = 0;
+
+            CONTEXT_LOAD(width,  generated_vars.texture[i].width);
+            CONTEXT_LOAD(height, generated_vars.texture[i].height);
+
+            int FRAC_BITS = 0;
+            if (tmu.linear) {
+                // linear interpolation
+                if (tmu.format.size == 1) {
+                    // for 8-bits textures, we can afford
+                    // 7 bits of fractional precision at no
+                    // additional cost (we can't do 8 bits
+                    // because filter8 uses signed 16 bits muls)
+                    FRAC_BITS = 7;
+                } else if (tmu.format.size == 2) {
+                    // filter16() is internally limited to 4 bits, so:
+                    // FRAC_BITS=2 generates less instructions,
+                    // FRAC_BITS=3,4,5 creates unpleasant artifacts,
+                    // FRAC_BITS=6+ looks good
+                    FRAC_BITS = 6;
+                } else if (tmu.format.size == 4) {
+                    // filter32() is internally limited to 8 bits, so:
+                    // FRAC_BITS=4 looks good
+                    // FRAC_BITS=5+ looks better, but generates 3 extra ipp
+                    FRAC_BITS = 6;
+                } else {
+                    // for all other cases we use 4 bits.
+                    FRAC_BITS = 4;
+                }
+            }
+            wrapping(u, s.reg, width,  tmu.swrap, FRAC_BITS);
+            wrapping(v, t.reg, height, tmu.twrap, FRAC_BITS);
+
+            if (tmu.linear) {
+                comment("compute linear filtering offsets");
+                // pixel size scale
+                const int shift = 31 - gglClz(tmu.format.size);
+                U = scratches.obtain();
+                V = scratches.obtain();
+
+                // sample the texel center
+                SUB(AL, 0, u, u, imm(1<<(FRAC_BITS-1)));
+                SUB(AL, 0, v, v, imm(1<<(FRAC_BITS-1)));
+
+                // get the fractionnal part of U,V
+                AND(AL, 0, U, u, imm((1<<FRAC_BITS)-1));
+                AND(AL, 0, V, v, imm((1<<FRAC_BITS)-1));
+
+                // compute width-1 and height-1
+                SUB(AL, 0, width,  width,  imm(1));
+                SUB(AL, 0, height, height, imm(1));
+
+                // get the integer part of U,V and clamp/wrap
+                // and compute offset to the next texel
+                if (tmu.swrap == GGL_NEEDS_WRAP_REPEAT) {
+                    // u has already been REPEATed
+                    MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS));
+                    MOV(MI, 0, u, width);                    
+                    CMP(AL, u, width);
+                    MOV(LT, 0, width, imm(1 << shift));
+                    if (shift)
+                        MOV(GE, 0, width, reg_imm(width, LSL, shift));
+                    RSB(GE, 0, width, width, imm(0));
+                } else {
+                    // u has not been CLAMPed yet
+                    // algorithm:
+                    // if ((u>>4) >= width)
+                    //      u = width<<4
+                    //      width = 0
+                    // else
+                    //      width = 1<<shift
+                    // u = u>>4; // get integer part
+                    // if (u<0)
+                    //      u = 0
+                    //      width = 0
+                    // generated_vars.rt = width
+                    
+                    CMP(AL, width, reg_imm(u, ASR, FRAC_BITS));
+                    MOV(LE, 0, u, reg_imm(width, LSL, FRAC_BITS));
+                    MOV(LE, 0, width, imm(0));
+                    MOV(GT, 0, width, imm(1 << shift));
+                    MOV(AL, 1, u, reg_imm(u, ASR, FRAC_BITS));
+                    MOV(MI, 0, u, imm(0));
+                    MOV(MI, 0, width, imm(0));
+                }
+                CONTEXT_STORE(width, generated_vars.rt);
+
+                const int stride = width;
+                CONTEXT_LOAD(stride, generated_vars.texture[i].stride);
+                if (tmu.twrap == GGL_NEEDS_WRAP_REPEAT) {
+                    // v has already been REPEATed
+                    MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS));
+                    MOV(MI, 0, v, height);
+                    CMP(AL, v, height);
+                    MOV(LT, 0, height, imm(1 << shift));
+                    if (shift)
+                        MOV(GE, 0, height, reg_imm(height, LSL, shift));
+                    RSB(GE, 0, height, height, imm(0));
+                    MUL(AL, 0, height, stride, height);
+                } else {
+                    // u has not been CLAMPed yet
+                    CMP(AL, height, reg_imm(v, ASR, FRAC_BITS));
+                    MOV(LE, 0, v, reg_imm(height, LSL, FRAC_BITS));
+                    MOV(LE, 0, height, imm(0));
+                    if (shift) {
+                        MOV(GT, 0, height, reg_imm(stride, LSL, shift));
+                    } else {
+                        MOV(GT, 0, height, stride);
+                    }
+                    MOV(AL, 1, v, reg_imm(v, ASR, FRAC_BITS));
+                    MOV(MI, 0, v, imm(0));
+                    MOV(MI, 0, height, imm(0));
+                }
+                CONTEXT_STORE(height, generated_vars.lb);
+            }
+    
+            scratches.recycle(width);
+            scratches.recycle(height);
+
+            // iterate texture coordinates...
+            comment("iterate s,t");
+            int dsdx = scratches.obtain();
+            int dtdx = scratches.obtain();
+            CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx);
+            CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx);
+            ADD(AL, 0, s.reg, s.reg, dsdx);
+            ADD(AL, 0, t.reg, t.reg, dtdx);
+            if ((mOptLevel&1)==0) {
+                CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]);
+                CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]);
+                scratches.recycle(s.reg);
+                scratches.recycle(t.reg);
+            }
+            scratches.recycle(dsdx);
+            scratches.recycle(dtdx);
+
+            // merge base & offset...
+            comment("merge base & offset");
+            texel.setTo(regs.obtain(), &tmu.format);
+            txPtr.setTo(texel.reg, tmu.bits);
+            int stride = scratches.obtain();
+            CONTEXT_LOAD(stride,    generated_vars.texture[i].stride);
+            CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data);
+            SMLABB(AL, u, v, stride, u);    // u+v*stride 
+            base_offset(txPtr, txPtr, u);
+
+            // load texel
+            if (!tmu.linear) {
+                comment("fetch texel");
+                load(txPtr, texel, 0);
+            } else {
+                // recycle registers we don't need anymore
+                scratches.recycle(u);
+                scratches.recycle(v);
+                scratches.recycle(stride);
+
+                comment("fetch texel, bilinear");
+                switch (tmu.format.size) {
+                case 1:  filter8(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+                case 2: filter16(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+                case 3: filter24(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+                case 4: filter32(parts, texel, tmu, U, V, txPtr, FRAC_BITS); break;
+                }
+            }            
+        }
+    }
+}
+
+void GGLAssembler::build_iterate_texture_coordinates(
+    const fragment_parts_t& parts)
+{
+    const bool multiTexture = mTextureMachine.activeUnits > 1;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) {
+        const texture_unit_t& tmu = mTextureMachine.tmu[i];
+        if (tmu.format_idx == 0)
+            continue;
+
+        if ((tmu.swrap == GGL_NEEDS_WRAP_11) &&
+            (tmu.twrap == GGL_NEEDS_WRAP_11))
+        { // 1:1 textures
+            const pointer_t& txPtr = parts.coords[i].ptr;
+            ADD(AL, 0, txPtr.reg, txPtr.reg, imm(txPtr.size>>3));
+        } else {
+            Scratch scratches(registerFile());
+            int s = parts.coords[i].s.reg;
+            int t = parts.coords[i].t.reg;
+            if ((mOptLevel&1)==0) {
+                s = scratches.obtain();
+                t = scratches.obtain();
+                CONTEXT_LOAD(s, generated_vars.texture[i].spill[0]);
+                CONTEXT_LOAD(t, generated_vars.texture[i].spill[1]);
+            }
+            int dsdx = scratches.obtain();
+            int dtdx = scratches.obtain();
+            CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx);
+            CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx);
+            ADD(AL, 0, s, s, dsdx);
+            ADD(AL, 0, t, t, dtdx);
+            if ((mOptLevel&1)==0) {
+                CONTEXT_STORE(s, generated_vars.texture[i].spill[0]);
+                CONTEXT_STORE(t, generated_vars.texture[i].spill[1]);
+            }
+        }
+    }
+}
+
+void GGLAssembler::filter8(
+        const fragment_parts_t& parts,
+        pixel_t& texel, const texture_unit_t& tmu,
+        int U, int V, pointer_t& txPtr,
+        int FRAC_BITS)
+{
+    if (tmu.format.components != GGL_ALPHA &&
+        tmu.format.components != GGL_LUMINANCE)
+    {
+        // this is a packed format, and we don't support
+        // linear filtering (it's probably RGB 332)
+        // Should not happen with OpenGL|ES
+        LDRB(AL, texel.reg, txPtr.reg);
+        return;
+    }
+
+    // ------------------------
+    // about ~22 cycles / pixel
+    Scratch scratches(registerFile());
+
+    int pixel= scratches.obtain();
+    int d    = scratches.obtain();
+    int u    = scratches.obtain();
+    int k    = scratches.obtain();
+    int rt   = scratches.obtain();
+    int lb   = scratches.obtain();
+
+    // RB -> U * V
+
+    CONTEXT_LOAD(rt, generated_vars.rt);
+    CONTEXT_LOAD(lb, generated_vars.lb);
+
+    int offset = pixel;
+    ADD(AL, 0, offset, lb, rt);
+    LDRB(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+    SMULBB(AL, u, U, V);
+    SMULBB(AL, d, pixel, u);
+    RSB(AL, 0, k, u, imm(1<<(FRAC_BITS*2)));
+    
+    // LB -> (1-U) * V
+    RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+    LDRB(AL, pixel, txPtr.reg, reg_scale_pre(lb));
+    SMULBB(AL, u, U, V);
+    SMLABB(AL, d, pixel, u, d);
+    SUB(AL, 0, k, k, u);
+    
+    // LT -> (1-U)*(1-V)
+    RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+    LDRB(AL, pixel, txPtr.reg);
+    SMULBB(AL, u, U, V);
+    SMLABB(AL, d, pixel, u, d);
+
+    // RT -> U*(1-V)
+    LDRB(AL, pixel, txPtr.reg, reg_scale_pre(rt));
+    SUB(AL, 0, u, k, u);
+    SMLABB(AL, texel.reg, pixel, u, d);
+    
+    for (int i=0 ; i<4 ; i++) {
+        if (!texel.format.c[i].h) continue;
+        texel.format.c[i].h = FRAC_BITS*2+8;
+        texel.format.c[i].l = FRAC_BITS*2; // keeping 8 bits in enough
+    }
+    texel.format.size = 4;
+    texel.format.bitsPerPixel = 32;
+    texel.flags |= CLEAR_LO;
+}
+
+void GGLAssembler::filter16(
+        const fragment_parts_t& parts,
+        pixel_t& texel, const texture_unit_t& tmu,
+        int U, int V, pointer_t& txPtr,
+        int FRAC_BITS)
+{    
+    // compute the mask
+    // XXX: it would be nice if the mask below could be computed
+    // automatically.
+    uint32_t mask = 0;
+    int shift = 0;
+    int prec = 0;
+    switch (tmu.format_idx) {
+        case GGL_PIXEL_FORMAT_RGB_565:
+            // source: 00000ggg.ggg00000 | rrrrr000.000bbbbb
+            // result: gggggggg.gggrrrrr | rrrrr0bb.bbbbbbbb
+            mask = 0x07E0F81F;
+            shift = 16;
+            prec = 5;
+            break;
+        case GGL_PIXEL_FORMAT_RGBA_4444:
+            // 0000,1111,0000,1111 | 0000,1111,0000,1111
+            mask = 0x0F0F0F0F;
+            shift = 12;
+            prec = 4;
+            break;
+        case GGL_PIXEL_FORMAT_LA_88:
+            // 0000,0000,1111,1111 | 0000,0000,1111,1111
+            // AALL -> 00AA | 00LL
+            mask = 0x00FF00FF;
+            shift = 8;
+            prec = 8;
+            break;
+        default:
+            // unsupported format, do something sensical...
+            LOGE("Unsupported 16-bits texture format (%d)", tmu.format_idx);
+            LDRH(AL, texel.reg, txPtr.reg);
+            return;
+    }
+
+    const int adjust = FRAC_BITS*2 - prec;
+    const int round  = 0;
+
+    // update the texel format
+    texel.format.size = 4;
+    texel.format.bitsPerPixel = 32;
+    texel.flags |= CLEAR_HI|CLEAR_LO;
+    for (int i=0 ; i<4 ; i++) {
+        if (!texel.format.c[i].h) continue;
+        const uint32_t offset = (mask & tmu.format.mask(i)) ? 0 : shift;
+        texel.format.c[i].h = tmu.format.c[i].h + offset + prec;
+        texel.format.c[i].l = texel.format.c[i].h - (tmu.format.bits(i) + prec);
+    }
+
+    // ------------------------
+    // about ~40 cycles / pixel
+    Scratch scratches(registerFile());
+
+    int pixel= scratches.obtain();
+    int d    = scratches.obtain();
+    int u    = scratches.obtain();
+    int k    = scratches.obtain();
+
+    // RB -> U * V
+    int offset = pixel;
+    CONTEXT_LOAD(offset, generated_vars.rt);
+    CONTEXT_LOAD(u, generated_vars.lb);
+    ADD(AL, 0, offset, offset, u);
+
+    LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+    SMULBB(AL, u, U, V);
+    ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+    build_and_immediate(pixel, pixel, mask, 32);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MUL(AL, 0, d, pixel, u);
+    RSB(AL, 0, k, u, imm(1<<prec));
+    
+    // LB -> (1-U) * V
+    CONTEXT_LOAD(offset, generated_vars.lb);
+    RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+    LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+    SMULBB(AL, u, U, V);
+    ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+    build_and_immediate(pixel, pixel, mask, 32);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MLA(AL, 0, d, pixel, u, d);
+    SUB(AL, 0, k, k, u);
+    
+    // LT -> (1-U)*(1-V)
+    RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+    LDRH(AL, pixel, txPtr.reg);
+    SMULBB(AL, u, U, V);
+    ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+    build_and_immediate(pixel, pixel, mask, 32);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MLA(AL, 0, d, pixel, u, d);
+
+    // RT -> U*(1-V)            
+    CONTEXT_LOAD(offset, generated_vars.rt);
+    LDRH(AL, pixel, txPtr.reg, reg_pre(offset));
+    SUB(AL, 0, u, k, u);
+    ORR(AL, 0, pixel, pixel, reg_imm(pixel, LSL, shift));
+    build_and_immediate(pixel, pixel, mask, 32);
+    MLA(AL, 0, texel.reg, pixel, u, d);
+}
+
+void GGLAssembler::filter24(
+        const fragment_parts_t& parts,
+        pixel_t& texel, const texture_unit_t& tmu,
+        int U, int V, pointer_t& txPtr,
+        int FRAC_BITS)
+{
+    // not supported yet (currently disabled)
+    load(txPtr, texel, 0);
+}
+
+void GGLAssembler::filter32(
+        const fragment_parts_t& parts,
+        pixel_t& texel, const texture_unit_t& tmu,
+        int U, int V, pointer_t& txPtr,
+        int FRAC_BITS)
+{
+    const int adjust = FRAC_BITS*2 - 8;
+    const int round  = 0;
+
+    // ------------------------
+    // about ~38 cycles / pixel
+    Scratch scratches(registerFile());
+    
+    int pixel= scratches.obtain();
+    int dh   = scratches.obtain();
+    int u    = scratches.obtain();
+    int k    = scratches.obtain();
+
+    int temp = scratches.obtain();
+    int dl   = scratches.obtain();
+    int mask = scratches.obtain();
+
+    MOV(AL, 0, mask, imm(0xFF));
+    ORR(AL, 0, mask, mask, imm(0xFF0000));
+
+    // RB -> U * V
+    int offset = pixel;
+    CONTEXT_LOAD(offset, generated_vars.rt);
+    CONTEXT_LOAD(u, generated_vars.lb);
+    ADD(AL, 0, offset, offset, u);
+
+    LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+    SMULBB(AL, u, U, V);
+    AND(AL, 0, temp, mask, pixel);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MUL(AL, 0, dh, temp, u);
+    AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+    MUL(AL, 0, dl, temp, u);
+    RSB(AL, 0, k, u, imm(0x100));
+
+    // LB -> (1-U) * V
+    CONTEXT_LOAD(offset, generated_vars.lb);
+    RSB(AL, 0, U, U, imm(1<<FRAC_BITS));
+    LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+    SMULBB(AL, u, U, V);
+    AND(AL, 0, temp, mask, pixel);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MLA(AL, 0, dh, temp, u, dh);    
+    AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+    MLA(AL, 0, dl, temp, u, dl);
+    SUB(AL, 0, k, k, u);
+
+    // LT -> (1-U)*(1-V)
+    RSB(AL, 0, V, V, imm(1<<FRAC_BITS));
+    LDR(AL, pixel, txPtr.reg);
+    SMULBB(AL, u, U, V);
+    AND(AL, 0, temp, mask, pixel);
+    if (adjust) {
+        if (round)
+            ADD(AL, 0, u, u, imm(1<<(adjust-1)));
+        MOV(AL, 0, u, reg_imm(u, LSR, adjust));
+    }
+    MLA(AL, 0, dh, temp, u, dh);    
+    AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+    MLA(AL, 0, dl, temp, u, dl);
+
+    // RT -> U*(1-V)            
+    CONTEXT_LOAD(offset, generated_vars.rt);
+    LDR(AL, pixel, txPtr.reg, reg_scale_pre(offset));
+    SUB(AL, 0, u, k, u);
+    AND(AL, 0, temp, mask, pixel);
+    MLA(AL, 0, dh, temp, u, dh);    
+    AND(AL, 0, temp, mask, reg_imm(pixel, LSR, 8));
+    MLA(AL, 0, dl, temp, u, dl);
+
+    AND(AL, 0, dh, mask, reg_imm(dh, LSR, 8));
+    AND(AL, 0, dl, dl, reg_imm(mask, LSL, 8));
+    ORR(AL, 0, texel.reg, dh, dl);
+}
+
+void GGLAssembler::build_texture_environment(
+        component_t& fragment,
+        const fragment_parts_t& parts,
+        int component,
+        Scratch& regs)
+{
+    const uint32_t component_mask = 1<<component;
+    const bool multiTexture = mTextureMachine.activeUnits > 1;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+        texture_unit_t& tmu = mTextureMachine.tmu[i];
+
+        if (tmu.mask & component_mask) {
+            // replace or modulate with this texture
+            if ((tmu.replaced & component_mask) == 0) {
+                // not replaced by a later tmu...
+
+                Scratch scratches(registerFile());
+                pixel_t texel(parts.texel[i]);
+                if (multiTexture && 
+                    tmu.swrap == GGL_NEEDS_WRAP_11 &&
+                    tmu.twrap == GGL_NEEDS_WRAP_11)
+                {
+                    texel.reg = scratches.obtain();
+                    texel.flags |= CORRUPTIBLE;
+                    comment("fetch texel (multitexture 1:1)");
+                    load(parts.coords[i].ptr, texel, WRITE_BACK);
+                 }
+
+                component_t incoming(fragment);
+                modify(fragment, regs);
+                
+                switch (tmu.env) {
+                case GGL_REPLACE:
+                    extract(fragment, texel, component);
+                    break;
+                case GGL_MODULATE:
+                    modulate(fragment, incoming, texel, component);
+                    break;
+                case GGL_DECAL:
+                    decal(fragment, incoming, texel, component);
+                    break;
+                case GGL_BLEND:
+                    blend(fragment, incoming, texel, component, i);
+                    break;
+                case GGL_ADD:
+                    add(fragment, incoming, texel, component);
+                    break;
+                }
+            }
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::wrapping(
+            int d,
+            int coord, int size,
+            int tx_wrap, int tx_linear)
+{
+    // notes:
+    // if tx_linear is set, we need 4 extra bits of precision on the result
+    // SMULL/UMULL is 3 cycles
+    Scratch scratches(registerFile());
+    int c = coord;
+    if (tx_wrap == GGL_NEEDS_WRAP_REPEAT) {
+        // UMULL takes 4 cycles (interlocked), and we can get away with
+        // 2 cycles using SMULWB, but we're loosing 16 bits of precision
+        // out of 32 (this is not a problem because the iterator keeps
+        // its full precision)
+        // UMULL(AL, 0, size, d, c, size);
+        // note: we can't use SMULTB because it's signed.
+        MOV(AL, 0, d, reg_imm(c, LSR, 16-tx_linear));
+        SMULWB(AL, d, d, size);
+    } else if (tx_wrap == GGL_NEEDS_WRAP_CLAMP_TO_EDGE) {
+        if (tx_linear) {
+            // 1 cycle
+            MOV(AL, 0, d, reg_imm(coord, ASR, 16-tx_linear));
+        } else {
+            // 4 cycles (common case)
+            MOV(AL, 0, d, reg_imm(coord, ASR, 16));
+            BIC(AL, 0, d, d, reg_imm(d, ASR, 31));
+            CMP(AL, d, size);
+            SUB(GE, 0, d, size, imm(1));
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+void GGLAssembler::modulate(
+        component_t& dest, 
+        const component_t& incoming,
+        const pixel_t& incomingTexel, int component)
+{
+    Scratch locals(registerFile());
+    integer_t texel(locals.obtain(), 32, CORRUPTIBLE);            
+    extract(texel, incomingTexel, component);
+
+    const int Nt = texel.size();
+        // Nt should always be less than 10 bits because it comes
+        // from the TMU.
+
+    int Ni = incoming.size();
+        // Ni could be big because it comes from previous MODULATEs
+
+    if (Nt == 1) {
+        // texel acts as a bit-mask
+        // dest = incoming & ((texel << incoming.h)-texel)
+        RSB(AL, 0, dest.reg, texel.reg, reg_imm(texel.reg, LSL, incoming.h));
+        AND(AL, 0, dest.reg, dest.reg, incoming.reg);
+        dest.l = incoming.l;
+        dest.h = incoming.h;
+        dest.flags |= (incoming.flags & CLEAR_LO);
+    } else if (Ni == 1) {
+        MOV(AL, 0, dest.reg, reg_imm(incoming.reg, LSL, 31-incoming.h));
+        AND(AL, 0, dest.reg, texel.reg, reg_imm(dest.reg, ASR, 31));
+        dest.l = 0;
+        dest.h = Nt;
+    } else {
+        int inReg = incoming.reg;
+        int shift = incoming.l;
+        if ((Nt + Ni) > 32) {
+            // we will overflow, reduce the precision of Ni to 8 bits
+            // (Note Nt cannot be more than 10 bits which happens with 
+            // 565 textures and GGL_LINEAR)
+            shift += Ni-8;
+            Ni = 8;
+        }
+
+        // modulate by the component with the lowest precision
+        if (Nt >= Ni) {
+            if (shift) {
+                // XXX: we should be able to avoid this shift
+                // when shift==16 && Nt<16 && Ni<16, in which
+                // we could use SMULBT below.
+                MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift));
+                inReg = dest.reg;
+                shift = 0;
+            }
+            // operation:           (Cf*Ct)/((1<<Ni)-1)
+            // approximated with:   Cf*(Ct + Ct>>(Ni-1))>>Ni
+            // this operation doesn't change texel's size
+            ADD(AL, 0, dest.reg, inReg, reg_imm(inReg, LSR, Ni-1));
+            if (Nt<16 && Ni<16) SMULBB(AL, dest.reg, texel.reg, dest.reg);
+            else                MUL(AL, 0, dest.reg, texel.reg, dest.reg);
+            dest.l = Ni;
+            dest.h = Nt + Ni;            
+        } else {
+            if (shift && (shift != 16)) {
+                // if shift==16, we can use 16-bits mul instructions later
+                MOV(AL, 0, dest.reg, reg_imm(inReg, LSR, shift));
+                inReg = dest.reg;
+                shift = 0;
+            }
+            // operation:           (Cf*Ct)/((1<<Nt)-1)
+            // approximated with:   Ct*(Cf + Cf>>(Nt-1))>>Nt
+            // this operation doesn't change incoming's size
+            Scratch scratches(registerFile());
+            int t = (texel.flags & CORRUPTIBLE) ? texel.reg : dest.reg;
+            if (t == inReg)
+                t = scratches.obtain();
+            ADD(AL, 0, t, texel.reg, reg_imm(texel.reg, LSR, Nt-1));
+            if (Nt<16 && Ni<16) {
+                if (shift==16)  SMULBT(AL, dest.reg, t, inReg);
+                else            SMULBB(AL, dest.reg, t, inReg);
+            } else              MUL(AL, 0, dest.reg, t, inReg);
+            dest.l = Nt;
+            dest.h = Nt + Ni;
+        }
+
+        // low bits are not valid
+        dest.flags |= CLEAR_LO;
+
+        // no need to keep more than 8 bits/component
+        if (dest.size() > 8)
+            dest.l = dest.h-8;
+    }
+}
+
+void GGLAssembler::decal(
+        component_t& dest, 
+        const component_t& incoming,
+        const pixel_t& incomingTexel, int component)
+{
+    // RGBA:
+    // Cv = Cf*(1 - At) + Ct*At = Cf + (Ct - Cf)*At
+    // Av = Af
+    Scratch locals(registerFile());
+    integer_t texel(locals.obtain(), 32, CORRUPTIBLE);            
+    integer_t factor(locals.obtain(), 32, CORRUPTIBLE);
+    extract(texel, incomingTexel, component);
+    extract(factor, incomingTexel, GGLFormat::ALPHA);
+
+    // no need to keep more than 8-bits for decal 
+    int Ni = incoming.size();
+    int shift = incoming.l;
+    if (Ni > 8) {
+        shift += Ni-8;
+        Ni = 8;
+    }
+    integer_t incomingNorm(incoming.reg, Ni, incoming.flags);
+    if (shift) {
+        MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift));
+        incomingNorm.reg = dest.reg;
+        incomingNorm.flags |= CORRUPTIBLE;
+    }
+    ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1));
+    build_blendOneMinusFF(dest, factor, incomingNorm, texel);
+}
+
+void GGLAssembler::blend(
+        component_t& dest, 
+        const component_t& incoming,
+        const pixel_t& incomingTexel, int component, int tmu)
+{
+    // RGBA:
+    // Cv = (1 - Ct)*Cf + Ct*Cc = Cf + (Cc - Cf)*Ct
+    // Av = At*Af
+
+    if (component == GGLFormat::ALPHA) {
+        modulate(dest, incoming, incomingTexel, component);
+        return;
+    }
+    
+    Scratch locals(registerFile());
+    integer_t color(locals.obtain(), 8, CORRUPTIBLE);            
+    integer_t factor(locals.obtain(), 32, CORRUPTIBLE);
+    LDRB(AL, color.reg, mBuilderContext.Rctx,
+            immed12_pre(GGL_OFFSETOF(state.texture[tmu].env_color[component])));
+    extract(factor, incomingTexel, component);
+
+    // no need to keep more than 8-bits for blend 
+    int Ni = incoming.size();
+    int shift = incoming.l;
+    if (Ni > 8) {
+        shift += Ni-8;
+        Ni = 8;
+    }
+    integer_t incomingNorm(incoming.reg, Ni, incoming.flags);
+    if (shift) {
+        MOV(AL, 0, dest.reg, reg_imm(incomingNorm.reg, LSR, shift));
+        incomingNorm.reg = dest.reg;
+        incomingNorm.flags |= CORRUPTIBLE;
+    }
+    ADD(AL, 0, factor.reg, factor.reg, reg_imm(factor.reg, LSR, factor.s-1));
+    build_blendOneMinusFF(dest, factor, incomingNorm, color);
+}
+
+void GGLAssembler::add(
+        component_t& dest, 
+        const component_t& incoming,
+        const pixel_t& incomingTexel, int component)
+{
+    // RGBA:
+    // Cv = Cf + Ct;
+    Scratch locals(registerFile());
+    
+    component_t incomingTemp(incoming);
+
+    // use "dest" as a temporary for extracting the texel, unless "dest"
+    // overlaps "incoming".
+    integer_t texel(dest.reg, 32, CORRUPTIBLE);
+    if (dest.reg == incomingTemp.reg)
+        texel.reg = locals.obtain();
+    extract(texel, incomingTexel, component);
+
+    if (texel.s < incomingTemp.size()) {
+        expand(texel, texel, incomingTemp.size());
+    } else if (texel.s > incomingTemp.size()) {
+        if (incomingTemp.flags & CORRUPTIBLE) {
+            expand(incomingTemp, incomingTemp, texel.s);
+        } else {
+            incomingTemp.reg = locals.obtain();
+            expand(incomingTemp, incoming, texel.s);
+        }
+    }
+
+    if (incomingTemp.l) {
+        ADD(AL, 0, dest.reg, texel.reg,
+                reg_imm(incomingTemp.reg, LSR, incomingTemp.l));
+    } else {
+        ADD(AL, 0, dest.reg, texel.reg, incomingTemp.reg);
+    }
+    dest.l = 0;
+    dest.h = texel.size();
+    component_sat(dest);
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp
new file mode 100644
index 0000000..5b92062
--- /dev/null
+++ b/libpixelflinger/fixed.cpp
@@ -0,0 +1,339 @@
+/* libs/pixelflinger/fixed.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdio.h>
+
+#include <private/pixelflinger/ggl_context.h>
+#include <private/pixelflinger/ggl_fixed.h>
+
+
+// ------------------------------------------------------------------------
+
+int32_t gglRecipQNormalized(int32_t x, int* exponent)
+{
+    const int32_t s = x>>31;
+    uint32_t a = s ? -x : x;
+
+    // the result will overflow, so just set it to the biggest/inf value
+    if (ggl_unlikely(a <= 2LU)) {
+        *exponent = 0;
+        return s ? FIXED_MIN : FIXED_MAX;
+    }
+
+    // Newton-Raphson iteration:
+    // x = r*(2 - a*r)
+
+    const int32_t lz = gglClz(a);
+    a <<= lz;  // 0.32
+    uint32_t r = a;
+    // note: if a == 0x80000000, this means x was a power-of-2, in this
+    // case we don't need to compute anything. We get the reciprocal for
+    // (almost) free.
+    if (a != 0x80000000) {
+        r = (0x2E800 << (30-16)) - (r>>(2-1)); // 2.30, r = 2.90625 - 2*a
+        // 0.32 + 2.30 = 2.62 -> 2.30
+        // 2.30 + 2.30 = 4.60 -> 2.30
+        r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30;
+        r = (((2LU<<30) - uint32_t((uint64_t(a)*r) >> 32)) * uint64_t(r)) >> 30;
+    }
+
+    // shift right 1-bit to make room for the sign bit
+    *exponent = 30-lz-1;
+    r >>= 1;
+    return s ? -r : r;
+}
+
+int32_t gglRecipQ(GGLfixed x, int q)
+{
+    int shift;
+    x = gglRecipQNormalized(x, &shift);
+    shift += 16-q;
+    x += 1L << (shift-1);   // rounding
+    x >>= shift;
+    return x;
+}    
+
+// ------------------------------------------------------------------------
+
+GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
+{
+    if ((d>>24) && ((d>>24)+1)) {
+        n >>= 8;
+        d >>= 8;
+    }
+    return gglMulx(n, gglRecip(d));
+}
+
+// ------------------------------------------------------------------------
+
+static const GGLfixed ggl_sqrt_reciproc_approx_tab[8] = {
+    // 1/sqrt(x) with x = 1-N/16, N=[8...1]
+    0x16A09, 0x15555, 0x143D1, 0x134BF, 0x1279A, 0x11C01, 0x111AC, 0x10865
+};
+
+GGLfixed gglSqrtRecipx(GGLfixed x)
+{
+    if (x == 0)         return FIXED_MAX;
+    if (x == FIXED_ONE) return x;
+    const GGLfixed a = x;
+    const int32_t lz = gglClz(x);
+    x = ggl_sqrt_reciproc_approx_tab[(a>>(28-lz))&0x7];
+    const int32_t exp = lz - 16;
+    if (exp <= 0)   x >>= -exp>>1;
+    else            x <<= (exp>>1) + (exp & 1);        
+    if (exp & 1) {
+        x = gglMulx(x, ggl_sqrt_reciproc_approx_tab[0])>>1;
+    }
+    // 2 Newton-Raphson iterations: x = x/2*(3-(a*x)*x)
+    x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x)));
+    x = gglMulx((x>>1),(0x30000 - gglMulx(gglMulx(a,x),x)));
+    return x;
+}
+
+GGLfixed gglSqrtx(GGLfixed a)
+{
+    // Compute a full precision square-root (24 bits accuracy)
+    GGLfixed r = 0;
+    GGLfixed bit = 0x800000;
+    int32_t bshift = 15;
+    do {
+        GGLfixed temp = bit + (r<<1);
+        if (bshift >= 8)    temp <<= (bshift-8);
+        else                temp >>= (8-bshift);
+        if (a >= temp) {
+            r += bit;
+            a -= temp;
+        }
+        bshift--;
+    } while (bit>>=1);
+    return r;
+}
+
+// ------------------------------------------------------------------------
+
+static const GGLfixed ggl_log_approx_tab[] = {
+    // -ln(x)/ln(2) with x = N/16, N=[8...16]
+    0xFFFF, 0xd47f, 0xad96, 0x8a62, 0x6a3f, 0x4caf, 0x3151, 0x17d6, 0x0000
+};
+
+static const GGLfixed ggl_alog_approx_tab[] = { // domain [0 - 1.0]
+	0xffff, 0xeac0, 0xd744, 0xc567, 0xb504, 0xa5fe, 0x9837, 0x8b95, 0x8000
+};
+
+GGLfixed gglPowx(GGLfixed x, GGLfixed y)
+{
+    // prerequisite: 0 <= x <= 1, and y >=0
+
+    // pow(x,y) = 2^(y*log2(x))
+    // =  2^(y*log2(x*(2^exp)*(2^-exp))))
+    // =  2^(y*(log2(X)-exp))
+    // =  2^(log2(X)*y - y*exp)
+    // =  2^( - (-log2(X)*y + y*exp) )
+    
+    int32_t exp = gglClz(x) - 16;
+    GGLfixed f = x << exp;
+    x = (f & 0x0FFF)<<4;
+    f = (f >> 12) & 0x7;
+    GGLfixed p = gglMulAddx(
+            ggl_log_approx_tab[f+1] - ggl_log_approx_tab[f], x,
+            ggl_log_approx_tab[f]);
+    p = gglMulAddx(p, y, y*exp);
+    exp = gglFixedToIntFloor(p);
+    if (exp < 31) {
+        p = gglFracx(p);
+        x = (p & 0x1FFF)<<3;
+        p >>= 13;    
+        p = gglMulAddx(
+                ggl_alog_approx_tab[p+1] - ggl_alog_approx_tab[p], x,
+                ggl_alog_approx_tab[p]);
+        p >>= exp;
+    } else {
+        p = 0;
+    }
+    return p;
+        // ( powf((a*65536.0f), (b*65536.0f)) ) * 65536.0f;
+}
+
+// ------------------------------------------------------------------------
+
+int32_t gglDivQ(GGLfixed n, GGLfixed d, int32_t i)
+{
+    //int32_t r =int32_t((int64_t(n)<<i)/d);
+    const int32_t ds = n^d;
+    if (n<0) n = -n;
+    if (d<0) d = -d;
+    int nd = gglClz(d) - gglClz(n);
+    i += nd + 1;
+    if (nd > 0) d <<= nd;
+    else        n <<= -nd;
+    uint32_t q = 0;
+
+    int j = i & 7;
+    i >>= 3;
+
+    // gcc deals with the code below pretty well.
+    // we get 3.75 cycles per bit in the main loop
+    // and 8 cycles per bit in the termination loop
+    if (ggl_likely(i)) {
+        n -= d;
+        do {
+            q <<= 8;
+            if (n>=0)   q |= 128;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 64;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 32;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 16;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 8;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 4;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 2;
+            else        n += d;
+            n = n*2 - d;
+            if (n>=0)   q |= 1;
+            else        n += d;
+            
+            if (--i == 0)
+                goto finish;
+
+            n = n*2 - d;
+        } while(true);
+        do {
+            q <<= 1;
+            n = n*2 - d;
+            if (n>=0)   q |= 1;
+            else        n += d;
+        finish: ;
+        } while (j--);
+        return (ds<0) ? -q : q;
+    }
+
+    n -= d;
+    if (n>=0)   q |= 1;
+    else        n += d;
+    j--;
+    goto finish;
+}
+
+// ------------------------------------------------------------------------
+
+// assumes that the int32_t values of a, b, and c are all positive
+// use when both a and b are larger than c
+
+template <typename T>
+static inline void swap(T& a, T& b) {
+    T t(a);
+    a = b;
+    b = t;
+}
+
+static __attribute__((noinline))
+int32_t slow_muldiv(uint32_t a, uint32_t b, uint32_t c)
+{
+	// first we compute a*b as a 64-bit integer
+    // (GCC generates umull with the code below)
+    uint64_t ab = uint64_t(a)*b;
+    uint32_t hi = ab>>32;
+    uint32_t lo = ab;
+    uint32_t result;
+
+	// now perform the division
+	if (hi >= c) {
+	overflow:
+		result = 0x7fffffff;  // basic overflow
+	} else if (hi == 0) {
+		result = lo/c;  // note: c can't be 0
+		if ((result >> 31) != 0)  // result must fit in 31 bits
+			goto overflow;
+	} else {
+		uint32_t r = hi;
+		int bits = 31;
+	    result = 0;
+		do {
+			r = (r << 1) | (lo >> 31);
+			lo <<= 1;
+			result <<= 1;
+			if (r >= c) {
+				r -= c;
+				result |= 1;
+			}
+		} while (bits--);
+	}
+	return int32_t(result);
+}
+
+// assumes a >= 0 and c >= b >= 0
+static inline
+int32_t quick_muldiv(int32_t a, int32_t b, int32_t c)
+{
+    int32_t r = 0, q = 0, i;
+    int leading = gglClz(a);
+    i = 32 - leading;
+    a <<= leading;
+    do {
+        r <<= 1;
+        if (a < 0)
+            r += b;
+        a <<= 1;
+        q <<= 1;
+        if (r >= c) {
+            r -= c;
+            q++;
+        }
+        asm(""::); // gcc generates better code this way
+        if (r >= c) {
+            r -= c;
+            q++;
+        }
+    }
+    while (--i);
+    return q;
+}
+
+// this function computes a*b/c with 64-bit intermediate accuracy
+// overflows (e.g. division by 0) are handled and return INT_MAX
+
+int32_t gglMulDivi(int32_t a, int32_t b, int32_t c)
+{
+	int32_t result;
+	int32_t sign = a^b^c;
+
+	if (a < 0) a = -a;
+	if (b < 0) b = -b;
+	if (c < 0) c = -c;
+
+    if (a < b) {
+        swap(a, b);
+    }
+    
+	if (b <= c) result = quick_muldiv(a, b, c);
+	else        result = slow_muldiv((uint32_t)a, (uint32_t)b, (uint32_t)c);
+	
+	if (sign < 0)
+		result = -result;
+	  
+    return result;
+}
diff --git a/libpixelflinger/format.cpp b/libpixelflinger/format.cpp
new file mode 100644
index 0000000..161e6d6
--- /dev/null
+++ b/libpixelflinger/format.cpp
@@ -0,0 +1,67 @@
+/* libs/pixelflinger/format.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <pixelflinger/format.h>
+
+namespace android {
+
+static GGLFormat const gPixelFormatInfos[] =
+{   //          Alpha    Red     Green   Blue
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    {  4, 32, {{32,24,   8, 0,  16, 8,  24,16 }}, GGL_RGBA },   // PIXEL_FORMAT_RGBA_8888
+    {  4, 24, {{ 0, 0,   8, 0,  16, 8,  24,16 }}, GGL_RGB  },   // PIXEL_FORMAT_RGBX_8888
+    {  3, 24, {{ 0, 0,   8, 0,  16, 8,  24,16 }}, GGL_RGB  },   // PIXEL_FORMAT_RGB_888
+    {  2, 16, {{ 0, 0,  16,11,  11, 5,   5, 0 }}, GGL_RGB  },   // PIXEL_FORMAT_RGB_565
+    {  4, 32, {{32,24,  24,16,  16, 8,   8, 0 }}, GGL_RGBA },   // PIXEL_FORMAT_BGRA_8888
+    {  2, 16, {{ 1, 0,  16,11,  11, 6,   6, 1 }}, GGL_RGBA },   // PIXEL_FORMAT_RGBA_5551
+    {  2, 16, {{ 4, 0,  16,12,  12, 8,   8, 4 }}, GGL_RGBA },   // PIXEL_FORMAT_RGBA_4444
+    {  1,  8, {{ 8, 0,   0, 0,   0, 0,   0, 0 }}, GGL_ALPHA},   // PIXEL_FORMAT_A8
+    {  1,  8, {{ 0, 0,   8, 0,   8, 0,   8, 0 }}, GGL_LUMINANCE},//PIXEL_FORMAT_L8
+    {  2, 16, {{16, 8,   8, 0,   8, 0,   8, 0 }}, GGL_LUMINANCE_ALPHA},// PIXEL_FORMAT_LA_88
+    {  1,  8, {{ 0, 0,   8, 5,   5, 2,   2, 0 }}, GGL_RGB  },   // PIXEL_FORMAT_RGB_332
+    
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+
+    {  1, 16, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_422_SP
+    {  1, 12, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_SP },// PIXEL_FORMAT_YCbCr_420_SP
+    {  1, 16, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_422_P
+    {  1, 12, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_P }, // PIXEL_FORMAT_YCbCr_420_P
+    {  1, 16, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_422_I
+    {  1, 12, {{ 0, 8,   0, 8,   0, 8,   0, 0 }}, GGL_Y_CB_CR_I }, // PIXEL_FORMAT_YCbCr_420_I
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE
+    
+    {  2, 16, {{  0, 0, 16, 0,   0, 0,   0, 0 }}, GGL_DEPTH_COMPONENT},
+    {  1,  8, {{  8, 0,  0, 0,   0, 0,   0, 0 }}, GGL_STENCIL_INDEX  },
+    {  4, 24, {{  0, 0, 24, 0,   0, 0,   0, 0 }}, GGL_DEPTH_COMPONENT},
+    {  4,  8, {{ 32,24,  0, 0,   0, 0,   0, 0 }}, GGL_STENCIL_INDEX  },
+};
+
+}; // namespace android
+
+
+const GGLFormat* gglGetPixelFormatTable(size_t* numEntries)
+{
+    if (numEntries) {
+        *numEntries = sizeof(android::gPixelFormatInfos)/sizeof(GGLFormat);
+    }
+    return android::gPixelFormatInfos;
+}
diff --git a/libpixelflinger/picker.cpp b/libpixelflinger/picker.cpp
new file mode 100644
index 0000000..030ef19
--- /dev/null
+++ b/libpixelflinger/picker.cpp
@@ -0,0 +1,173 @@
+/* libs/pixelflinger/picker.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#include <stdio.h>
+
+#include "buffer.h"
+#include "scanline.h"
+#include "picker.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_picker(context_t* c)
+{
+}
+
+void ggl_pick(context_t* c)
+{
+    if (ggl_likely(!c->dirty))
+        return;
+        
+    // compute needs, see if they changed...
+    const uint32_t enables = c->state.enables;
+    needs_t new_needs(c->state.needs);
+
+    if (c->dirty & GGL_CB_STATE) {
+        new_needs.n &= ~GGL_NEEDS_CB_FORMAT_MASK;
+        new_needs.n |= GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT);
+        if (enables & GGL_ENABLE_BLENDING)
+            c->dirty |= GGL_PIXEL_PIPELINE_STATE;
+    }
+
+    if (c->dirty & GGL_PIXEL_PIPELINE_STATE) {
+        uint32_t n = GGL_BUILD_NEEDS(c->state.buffers.color.format, CB_FORMAT);
+        uint32_t p = 0;
+        if (enables & GGL_ENABLE_BLENDING) {
+            uint32_t src = c->state.blend.src;
+            uint32_t dst = c->state.blend.dst;
+            uint32_t src_alpha = c->state.blend.src_alpha;
+            uint32_t dst_alpha = c->state.blend.dst_alpha;
+            const GGLFormat& cbf = c->formats[ c->state.buffers.color.format ];
+            if (!cbf.c[GGLFormat::ALPHA].h) {
+                if ((src == GGL_ONE_MINUS_DST_ALPHA) ||
+                    (src == GGL_DST_ALPHA)) {
+                    src = GGL_ONE;
+                }
+                if ((src_alpha == GGL_ONE_MINUS_DST_ALPHA) ||
+                    (src_alpha == GGL_DST_ALPHA)) {
+                    src_alpha = GGL_ONE;
+                }
+                if ((dst == GGL_ONE_MINUS_DST_ALPHA) ||
+                    (dst == GGL_DST_ALPHA)) {
+                    dst = GGL_ONE;
+                }
+                if ((dst_alpha == GGL_ONE_MINUS_DST_ALPHA) ||
+                    (dst_alpha == GGL_DST_ALPHA)) {
+                    dst_alpha = GGL_ONE;
+                }
+            }
+
+            src       = ggl_blendfactor_to_needs(src);
+            dst       = ggl_blendfactor_to_needs(dst);
+            src_alpha = ggl_blendfactor_to_needs(src_alpha);
+            dst_alpha = ggl_blendfactor_to_needs(dst_alpha);
+                    
+            n |= GGL_BUILD_NEEDS( src, BLEND_SRC );
+            n |= GGL_BUILD_NEEDS( dst, BLEND_DST );
+            if (c->state.blend.alpha_separate) {
+                n |= GGL_BUILD_NEEDS( src_alpha, BLEND_SRCA );
+                n |= GGL_BUILD_NEEDS( dst_alpha, BLEND_DSTA );
+            } else {
+                n |= GGL_BUILD_NEEDS( src, BLEND_SRCA );
+                n |= GGL_BUILD_NEEDS( dst, BLEND_DSTA );
+            }
+        } else {
+            n |= GGL_BUILD_NEEDS( GGL_ONE,  BLEND_SRC );
+            n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DST );
+            n |= GGL_BUILD_NEEDS( GGL_ONE,  BLEND_SRCA );
+            n |= GGL_BUILD_NEEDS( GGL_ZERO, BLEND_DSTA );
+        }
+
+
+        n |= GGL_BUILD_NEEDS(c->state.mask.color^0xF,               MASK_ARGB);
+        n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_SMOOTH)  ?1:0,   SHADE);
+        if (enables & GGL_ENABLE_TMUS) {
+            n |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_W)       ?1:0,   W);
+        }
+        p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_DITHER)  ?1:0,   P_DITHER);
+        p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_AA)      ?1:0,   P_AA);
+        p |= GGL_BUILD_NEEDS((enables & GGL_ENABLE_FOG)     ?1:0,   P_FOG);
+
+        if (enables & GGL_ENABLE_LOGIC_OP) {
+            n |= GGL_BUILD_NEEDS(c->state.logic_op.opcode, LOGIC_OP);
+        } else {
+            n |= GGL_BUILD_NEEDS(GGL_COPY, LOGIC_OP);
+        }
+
+        if (enables & GGL_ENABLE_ALPHA_TEST) {
+            p |= GGL_BUILD_NEEDS(c->state.alpha_test.func, P_ALPHA_TEST);
+        } else {
+            p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_ALPHA_TEST);
+        }
+
+        if (enables & GGL_ENABLE_DEPTH_TEST) {
+            p |= GGL_BUILD_NEEDS(c->state.depth_test.func, P_DEPTH_TEST);
+            p |= GGL_BUILD_NEEDS(c->state.mask.depth&1, P_MASK_Z);
+        } else {
+            p |= GGL_BUILD_NEEDS(GGL_ALWAYS, P_DEPTH_TEST);
+            // writing to the z-buffer is always disabled if depth-test
+            // is disabled.
+        }
+        new_needs.n = n;
+        new_needs.p = p;
+    }
+
+    if (c->dirty & GGL_TMU_STATE) {
+        int idx = 0;
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+            const texture_t& tx = c->state.texture[i];
+            if (tx.enable) {
+                uint32_t t = 0;
+                t |= GGL_BUILD_NEEDS(tx.surface.format, T_FORMAT);
+                t |= GGL_BUILD_NEEDS(ggl_env_to_needs(tx.env), T_ENV);
+                t |= GGL_BUILD_NEEDS(0, T_POT);       // XXX: not used yet
+                if (tx.s_coord==GGL_ONE_TO_ONE && tx.t_coord==GGL_ONE_TO_ONE) {
+                    // we encode 1-to-1 into the wrap mode
+                    t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_S_WRAP);
+                    t |= GGL_BUILD_NEEDS(GGL_NEEDS_WRAP_11, T_T_WRAP);
+                } else {
+                    t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.s_wrap), T_S_WRAP);
+                    t |= GGL_BUILD_NEEDS(ggl_wrap_to_needs(tx.t_wrap), T_T_WRAP);
+                }
+                if (tx.mag_filter == GGL_LINEAR) {
+                    t |= GGL_BUILD_NEEDS(1, T_LINEAR);
+                }
+                if (tx.min_filter == GGL_LINEAR) {
+                    t |= GGL_BUILD_NEEDS(1, T_LINEAR);
+                }
+                new_needs.t[idx++] = t;
+            } else {
+                new_needs.t[i] = 0;
+            }
+        }
+    }
+
+    if (new_needs != c->state.needs) {
+        c->state.needs = new_needs;
+        ggl_pick_texture(c);
+        ggl_pick_cb(c);
+        ggl_pick_scanline(c);
+    }
+    c->dirty = 0;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libpixelflinger/picker.h b/libpixelflinger/picker.h
new file mode 100644
index 0000000..9cdbc3c
--- /dev/null
+++ b/libpixelflinger/picker.h
@@ -0,0 +1,31 @@
+/* libs/pixelflinger/picker.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_PICKER_H
+#define ANDROID_PICKER_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_picker(context_t* c);
+void ggl_pick(context_t* c);
+
+}; // namespace android
+
+#endif
diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp
new file mode 100644
index 0000000..b54da0c
--- /dev/null
+++ b/libpixelflinger/pixelflinger.cpp
@@ -0,0 +1,843 @@
+/* libs/pixelflinger/pixelflinger.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/time.h>
+
+#include <pixelflinger/pixelflinger.h>
+#include <private/pixelflinger/ggl_context.h>
+
+#include "buffer.h"
+#include "clear.h"
+#include "picker.h"
+#include "raster.h"
+#include "scanline.h"
+#include "trap.h"
+
+#include "codeflinger/GGLAssembler.h"
+#include "codeflinger/CodeCache.h"
+
+#include <stdio.h> 
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// 8x8 Bayer dither matrix
+static const uint8_t gDitherMatrix[GGL_DITHER_SIZE] = {
+     0, 32,  8, 40,  2, 34, 10, 42,
+    48, 16, 56, 24, 50, 18, 58, 26,
+    12, 44,  4, 36, 14, 46,  6, 38,
+    60, 28, 52, 20, 62, 30, 54, 22,
+     3, 35, 11, 43,  1, 33,  9, 41,
+    51, 19, 59, 27, 49, 17, 57, 25,
+    15, 47,  7, 39, 13, 45,  5, 37,
+    63, 31, 55, 23, 61, 29, 53, 21
+};
+
+static void ggl_init_procs(context_t* c);
+static void ggl_set_scissor(context_t* c);
+
+static void ggl_enable_blending(context_t* c, int enable);
+static void ggl_enable_scissor_test(context_t* c, int enable);
+static void ggl_enable_alpha_test(context_t* c, int enable);
+static void ggl_enable_logic_op(context_t* c, int enable);
+static void ggl_enable_dither(context_t* c, int enable);
+static void ggl_enable_stencil_test(context_t* c, int enable);
+static void ggl_enable_depth_test(context_t* c, int enable);
+static void ggl_enable_aa(context_t* c, int enable);
+static void ggl_enable_point_aa_nice(context_t* c, int enable);
+static void ggl_enable_texture2d(context_t* c, int enable);
+static void ggl_enable_w_lerp(context_t* c, int enable);
+static void ggl_enable_fog(context_t* c, int enable);
+
+static inline int min(int a, int b) CONST;
+static inline int min(int a, int b) {
+    return a < b ? a : b;
+}
+
+static inline int max(int a, int b) CONST;
+static inline int max(int a, int b) {
+    return a < b ? b : a;
+}
+
+// ----------------------------------------------------------------------------
+
+void ggl_error(context_t* c, GGLenum error)
+{
+    if (c->error == GGL_NO_ERROR)
+        c->error = error;
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_bindTexture(void* con, const GGLSurface* surface)
+{
+    GGL_CONTEXT(c, con);
+    if (surface->format != c->activeTMU->surface.format)
+        ggl_state_changed(c, GGL_TMU_STATE);    
+    ggl_set_surface(c, &(c->activeTMU->surface), surface);
+}
+
+
+static void ggl_bindTextureLod(void* con, GGLuint tmu,const GGLSurface* surface)
+{
+    GGL_CONTEXT(c, con);
+    // All LODs must have the same format
+    ggl_set_surface(c, &c->state.texture[tmu].surface, surface);
+}
+
+static void ggl_colorBuffer(void* con, const GGLSurface* surface)
+{
+    GGL_CONTEXT(c, con);
+    if (surface->format != c->state.buffers.color.format)
+        ggl_state_changed(c, GGL_CB_STATE);
+
+    if (surface->width > c->state.buffers.coverageBufferSize) {
+        // allocate the coverage factor buffer
+        free(c->state.buffers.coverage);
+        c->state.buffers.coverage = (int16_t*)malloc(surface->width * 2);
+        c->state.buffers.coverageBufferSize =
+                c->state.buffers.coverage ? surface->width : 0;
+    }
+    ggl_set_surface(c, &(c->state.buffers.color), surface);
+    if (c->state.buffers.read.format == 0) {
+        ggl_set_surface(c, &(c->state.buffers.read), surface);
+    }
+    ggl_set_scissor(c);
+}
+
+static void ggl_readBuffer(void* con, const GGLSurface* surface)
+{
+    GGL_CONTEXT(c, con);
+    ggl_set_surface(c, &(c->state.buffers.read), surface);
+}
+
+static void ggl_depthBuffer(void* con, const GGLSurface* surface)
+{
+    GGL_CONTEXT(c, con);
+    if (surface->format == GGL_PIXEL_FORMAT_Z_16) {
+        ggl_set_surface(c, &(c->state.buffers.depth), surface);
+    } else {
+        c->state.buffers.depth.format = GGL_PIXEL_FORMAT_NONE;
+        ggl_enable_depth_test(c, 0);
+    }
+}
+
+static void ggl_scissor(void* con, GGLint x, GGLint y,
+        GGLsizei width, GGLsizei height)
+{
+    GGL_CONTEXT(c, con);
+    c->state.scissor.user_left = x;
+    c->state.scissor.user_top = y;
+    c->state.scissor.user_right = x + width;
+    c->state.scissor.user_bottom = y + height;
+    ggl_set_scissor(c);
+}
+
+// ----------------------------------------------------------------------------
+
+static void enable_disable(context_t* c, GGLenum name, int en)
+{
+    switch (name) {
+    case GGL_BLEND:             ggl_enable_blending(c, en);      break;
+    case GGL_SCISSOR_TEST:      ggl_enable_scissor_test(c, en);  break;
+    case GGL_ALPHA_TEST:        ggl_enable_alpha_test(c, en);    break;
+    case GGL_COLOR_LOGIC_OP:    ggl_enable_logic_op(c, en);      break;
+    case GGL_DITHER:            ggl_enable_dither(c, en);        break;
+    case GGL_STENCIL_TEST:      ggl_enable_stencil_test(c, en);  break;
+    case GGL_DEPTH_TEST:        ggl_enable_depth_test(c, en);    break;
+    case GGL_AA:                ggl_enable_aa(c, en);            break;
+    case GGL_TEXTURE_2D:        ggl_enable_texture2d(c, en);     break;
+    case GGL_W_LERP:            ggl_enable_w_lerp(c, en);        break;
+    case GGL_FOG:               ggl_enable_fog(c, en);           break;
+    case GGL_POINT_SMOOTH_NICE: ggl_enable_point_aa_nice(c, en); break;
+    }
+}
+
+static void ggl_enable(void* con, GGLenum name)
+{
+    GGL_CONTEXT(c, con);
+    enable_disable(c, name, 1);
+}
+
+static void ggl_disable(void* con, GGLenum name)
+{
+    GGL_CONTEXT(c, con);
+    enable_disable(c, name, 0);
+}
+
+static void ggl_enableDisable(void* con, GGLenum name, GGLboolean en)
+{
+    GGL_CONTEXT(c, con);
+    enable_disable(c, name, en ? 1 : 0);
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_shadeModel(void* con, GGLenum mode)
+{
+    GGL_CONTEXT(c, con);
+    switch (mode) {
+    case GGL_FLAT:
+        if (c->state.enables & GGL_ENABLE_SMOOTH) {
+            c->state.enables &= ~GGL_ENABLE_SMOOTH;
+            ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+        }
+        break;
+    case GGL_SMOOTH:
+        if (!(c->state.enables & GGL_ENABLE_SMOOTH)) {
+            c->state.enables |= GGL_ENABLE_SMOOTH;
+            ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+        }
+        break;
+    default:
+        ggl_error(c, GGL_INVALID_ENUM);
+    }
+}
+
+static void ggl_color4xv(void* con, const GGLclampx* color)
+{
+    GGL_CONTEXT(c, con);
+	c->shade.r0 = gglFixedToIteratedColor(color[0]);
+	c->shade.g0 = gglFixedToIteratedColor(color[1]);
+	c->shade.b0 = gglFixedToIteratedColor(color[2]);
+	c->shade.a0 = gglFixedToIteratedColor(color[3]);
+}
+
+static void ggl_colorGrad12xv(void* con, const GGLcolor* grad)
+{
+    GGL_CONTEXT(c, con);
+    // it is very important to round the iterated value here because
+    // the rasterizer doesn't clamp them, therefore the iterated value
+    //must absolutely be correct.
+    // GGLColor is encoded as 8.16 value
+    const int32_t round = 0x8000;
+	c->shade.r0   = grad[ 0] + round;
+	c->shade.drdx = grad[ 1];
+	c->shade.drdy = grad[ 2];
+	c->shade.g0   = grad[ 3] + round;
+	c->shade.dgdx = grad[ 4];
+	c->shade.dgdy = grad[ 5];
+	c->shade.b0   = grad[ 6] + round;
+	c->shade.dbdx = grad[ 7];
+	c->shade.dbdy = grad[ 8];
+	c->shade.a0   = grad[ 9] + round;
+	c->shade.dadx = grad[10];
+	c->shade.dady = grad[11];
+}
+
+static void ggl_zGrad3xv(void* con, const GGLfixed32* grad)
+{
+    GGL_CONTEXT(c, con);
+    // z iterators are encoded as 0.32 fixed point and the z-buffer
+    // holds 16 bits, the rounding value is 0x8000.
+    const uint32_t round = 0x8000;
+	c->shade.z0   = grad[0] + round;
+	c->shade.dzdx = grad[1];
+	c->shade.dzdy = grad[2];
+}
+
+static void ggl_wGrad3xv(void* con, const GGLfixed* grad)
+{
+    GGL_CONTEXT(c, con);
+	c->shade.w0   = grad[0];
+	c->shade.dwdx = grad[1];
+	c->shade.dwdy = grad[2];
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_fogGrad3xv(void* con, const GGLfixed* grad)
+{
+    GGL_CONTEXT(c, con);
+	c->shade.f0     = grad[0];
+	c->shade.dfdx   = grad[1];
+	c->shade.dfdy   = grad[2];
+}
+
+static void ggl_fogColor3xv(void* con, const GGLclampx* color)
+{
+    GGL_CONTEXT(c, con);
+    const int32_t r = gglClampx(color[0]);
+    const int32_t g = gglClampx(color[1]);
+    const int32_t b = gglClampx(color[2]);
+	c->state.fog.color[GGLFormat::RED]  = (r - (r>>8))>>8;
+	c->state.fog.color[GGLFormat::GREEN]= (g - (g>>8))>>8;
+	c->state.fog.color[GGLFormat::BLUE] = (b - (b>>8))>>8;
+}
+
+static void ggl_enable_fog(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_FOG)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_FOG;
+        else        c->state.enables &= ~GGL_ENABLE_FOG;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_blendFunc(void* con, GGLenum src, GGLenum dst)
+{
+    GGL_CONTEXT(c, con);
+    c->state.blend.src = src;
+    c->state.blend.src_alpha = src;
+    c->state.blend.dst = dst;
+    c->state.blend.dst_alpha = dst;
+    c->state.blend.alpha_separate = 0;
+    if (c->state.enables & GGL_ENABLE_BLENDING) {
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+static void ggl_blendFuncSeparate(void* con,
+        GGLenum src, GGLenum dst,
+        GGLenum srcAlpha, GGLenum dstAplha)
+{
+    GGL_CONTEXT(c, con);
+    c->state.blend.src = src;
+    c->state.blend.src_alpha = srcAlpha;
+    c->state.blend.dst = dst;
+    c->state.blend.dst_alpha = dstAplha;
+    c->state.blend.alpha_separate = 1;
+    if (c->state.enables & GGL_ENABLE_BLENDING) {
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_texEnvi(void* con,	GGLenum target,
+							GGLenum pname,
+							GGLint param)
+{
+    GGL_CONTEXT(c, con);
+    if (target != GGL_TEXTURE_ENV || pname != GGL_TEXTURE_ENV_MODE) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    switch (param) {
+    case GGL_REPLACE:
+    case GGL_MODULATE:
+    case GGL_DECAL:
+    case GGL_BLEND:
+    case GGL_ADD:
+        if (c->activeTMU->env != param) {
+            c->activeTMU->env = param;
+            ggl_state_changed(c, GGL_TMU_STATE);
+        }
+        break;
+    default:
+        ggl_error(c, GGL_INVALID_ENUM);
+    }
+}
+
+static void ggl_texEnvxv(void* con, GGLenum target,
+        GGLenum pname, const GGLfixed* params)
+{
+    GGL_CONTEXT(c, con);
+    if (target != GGL_TEXTURE_ENV) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    switch (pname) {
+    case GGL_TEXTURE_ENV_MODE:
+        ggl_texEnvi(con, target, pname, params[0]);
+        break;
+    case GGL_TEXTURE_ENV_COLOR: {
+        uint8_t* const color = c->activeTMU->env_color;
+        const GGLclampx r = gglClampx(params[0]);
+        const GGLclampx g = gglClampx(params[1]);
+        const GGLclampx b = gglClampx(params[2]);
+        const GGLclampx a = gglClampx(params[3]);
+        color[0] = (a-(a>>8))>>8;
+        color[1] = (r-(r>>8))>>8;
+        color[2] = (g-(g>>8))>>8;
+        color[3] = (b-(b>>8))>>8;
+        break;
+    }
+    default:
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+}
+
+
+static void ggl_texParameteri(void* con,
+        GGLenum target,
+        GGLenum pname,
+        GGLint param)
+{
+    GGL_CONTEXT(c, con);
+    if (target != GGL_TEXTURE_2D) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+
+    if (param == GGL_CLAMP_TO_EDGE)
+        param = GGL_CLAMP;
+
+    uint16_t* what = 0;
+    switch (pname) {
+    case GGL_TEXTURE_WRAP_S:
+        if ((param == GGL_CLAMP) ||
+            (param == GGL_REPEAT)) {
+            what = &c->activeTMU->s_wrap;
+        }
+        break;
+    case GGL_TEXTURE_WRAP_T:
+        if ((param == GGL_CLAMP) ||
+            (param == GGL_REPEAT)) {
+            what = &c->activeTMU->t_wrap;
+        }
+        break;
+    case GGL_TEXTURE_MIN_FILTER:
+        if ((param == GGL_NEAREST) ||
+            (param == GGL_NEAREST_MIPMAP_NEAREST) ||
+            (param == GGL_NEAREST_MIPMAP_LINEAR)) {
+            what = &c->activeTMU->min_filter;
+            param = GGL_NEAREST;
+        }
+        if ((param == GGL_LINEAR) ||
+            (param == GGL_LINEAR_MIPMAP_NEAREST) ||
+            (param == GGL_LINEAR_MIPMAP_LINEAR)) {
+            what = &c->activeTMU->min_filter;
+            param = GGL_LINEAR;
+        }
+        break;
+    case GGL_TEXTURE_MAG_FILTER:
+        if ((param == GGL_NEAREST) ||
+            (param == GGL_LINEAR)) {
+            what = &c->activeTMU->mag_filter;
+        }
+        break;
+    }
+    
+    if (!what) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    
+    if (*what != param) {
+        *what = param;
+        ggl_state_changed(c, GGL_TMU_STATE);
+    }
+}
+
+static void ggl_texCoordGradScale8xv(void* con, GGLint tmu, const int32_t* grad)
+{
+    GGL_CONTEXT(c, con);
+    texture_t& u = c->state.texture[tmu];
+	u.shade.is0   = grad[0];
+	u.shade.idsdx = grad[1];
+	u.shade.idsdy = grad[2];
+	u.shade.it0   = grad[3];
+	u.shade.idtdx = grad[4];
+	u.shade.idtdy = grad[5];
+    u.shade.sscale= grad[6];
+    u.shade.tscale= grad[7];
+}
+
+static void ggl_texCoord2x(void* con, GGLfixed s, GGLfixed t)
+{
+    GGL_CONTEXT(c, con);
+	c->activeTMU->shade.is0 = s;
+	c->activeTMU->shade.it0 = t;
+    c->activeTMU->shade.sscale= 0;
+    c->activeTMU->shade.tscale= 0;
+}
+
+static void ggl_texCoord2i(void* con, GGLint s, GGLint t)
+{
+    ggl_texCoord2x(con, s<<16, t<<16);
+}
+
+static void ggl_texGeni(void* con, GGLenum coord, GGLenum pname, GGLint param)
+{
+    GGL_CONTEXT(c, con);
+    if (pname != GGL_TEXTURE_GEN_MODE) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+
+    uint32_t* coord_ptr = 0;
+    if (coord == GGL_S)         coord_ptr = &(c->activeTMU->s_coord);
+    else if (coord == GGL_T)    coord_ptr = &(c->activeTMU->t_coord);
+
+    if (coord_ptr) {
+        if (*coord_ptr != uint32_t(param)) {
+            *coord_ptr = uint32_t(param);
+            ggl_state_changed(c, GGL_TMU_STATE);
+        }
+    } else {
+        ggl_error(c, GGL_INVALID_ENUM);
+    }
+}
+
+static void ggl_activeTexture(void* con, GGLuint tmu)
+{
+    GGL_CONTEXT(c, con);
+    if (tmu >= GGLuint(GGL_TEXTURE_UNIT_COUNT)) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    c->activeTMUIndex = tmu;
+    c->activeTMU = &(c->state.texture[tmu]);
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_colorMask(void* con, GGLboolean r,
+                                     GGLboolean g,
+                                     GGLboolean b,
+                                     GGLboolean a)
+{
+    GGL_CONTEXT(c, con);
+    int mask = 0;
+    if (a)  mask |= 1 << GGLFormat::ALPHA;
+    if (r)  mask |= 1 << GGLFormat::RED;
+    if (g)  mask |= 1 << GGLFormat::GREEN;
+    if (b)  mask |= 1 << GGLFormat::BLUE;
+    if (c->state.mask.color != mask) {
+        c->state.mask.color = mask;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+static void ggl_depthMask(void* con, GGLboolean flag)
+{
+    GGL_CONTEXT(c, con);
+    if (c->state.mask.depth != flag?1:0) {
+        c->state.mask.depth = flag?1:0;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+static void ggl_stencilMask(void* con, GGLuint mask)
+{
+    GGL_CONTEXT(c, con);
+    if (c->state.mask.stencil != mask) {
+        c->state.mask.stencil = mask;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_alphaFuncx(void* con, GGLenum func, GGLclampx ref)
+{
+    GGL_CONTEXT(c, con);
+    if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    c->state.alpha_test.ref = gglFixedToIteratedColor(gglClampx(ref));
+    if (c->state.alpha_test.func != func) {
+        c->state.alpha_test.func = func;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_depthFunc(void* con, GGLenum func)
+{
+    GGL_CONTEXT(c, con);
+    if ((func < GGL_NEVER) || (func > GGL_ALWAYS)) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    if (c->state.depth_test.func != func) {
+        c->state.depth_test.func = func;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void ggl_logicOp(void* con, GGLenum opcode)
+{
+    GGL_CONTEXT(c, con);
+    if ((opcode < GGL_CLEAR) || (opcode > GGL_SET)) {
+        ggl_error(c, GGL_INVALID_ENUM);
+        return;
+    }
+    if (c->state.logic_op.opcode != opcode) {
+        c->state.logic_op.opcode = opcode;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+
+void ggl_set_scissor(context_t* c)
+{
+    if (c->state.enables & GGL_ENABLE_SCISSOR_TEST) {
+        const int32_t l = c->state.scissor.user_left;
+        const int32_t t = c->state.scissor.user_top;
+        const int32_t r = c->state.scissor.user_right;
+        const int32_t b = c->state.scissor.user_bottom;
+        c->state.scissor.left   = max(0, l);
+        c->state.scissor.right  = min(c->state.buffers.color.width, r);
+        c->state.scissor.top    = max(0, t);
+        c->state.scissor.bottom = min(c->state.buffers.color.height, b);
+    } else {
+        c->state.scissor.left   = 0;
+        c->state.scissor.top    = 0;
+        c->state.scissor.right  = c->state.buffers.color.width;
+        c->state.scissor.bottom = c->state.buffers.color.height;
+    }
+}
+
+void ggl_enable_blending(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_BLENDING)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_BLENDING;
+        else        c->state.enables &= ~GGL_ENABLE_BLENDING;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_scissor_test(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_SCISSOR_TEST)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_SCISSOR_TEST;
+        else        c->state.enables &= ~GGL_ENABLE_SCISSOR_TEST;
+        ggl_set_scissor(c);
+    }
+}
+
+void ggl_enable_alpha_test(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_ALPHA_TEST)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_ALPHA_TEST;
+        else        c->state.enables &= ~GGL_ENABLE_ALPHA_TEST;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_logic_op(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_LOGIC_OP)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_LOGIC_OP;
+        else        c->state.enables &= ~GGL_ENABLE_LOGIC_OP;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_dither(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_DITHER)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_DITHER;
+        else        c->state.enables &= ~GGL_ENABLE_DITHER;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_stencil_test(context_t* c, int enable)
+{
+}
+
+void ggl_enable_depth_test(context_t* c, int enable)
+{
+    if (c->state.buffers.depth.format == 0)
+        enable = 0;
+    const int e = (c->state.enables & GGL_ENABLE_DEPTH_TEST)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_DEPTH_TEST;
+        else        c->state.enables &= ~GGL_ENABLE_DEPTH_TEST;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_aa(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_AA)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_AA;
+        else        c->state.enables &= ~GGL_ENABLE_AA;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_point_aa_nice(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_POINT_AA_NICE)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_POINT_AA_NICE;
+        else        c->state.enables &= ~GGL_ENABLE_POINT_AA_NICE;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_w_lerp(context_t* c, int enable)
+{
+    const int e = (c->state.enables & GGL_ENABLE_W)?1:0;
+    if (e != enable) {
+        if (enable) c->state.enables |= GGL_ENABLE_W;
+        else        c->state.enables &= ~GGL_ENABLE_W;
+        ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE);
+    }
+}
+
+void ggl_enable_texture2d(context_t* c, int enable)
+{
+    if (c->activeTMU->enable != enable) {
+        const uint32_t tmu = c->activeTMUIndex;
+        c->activeTMU->enable = enable;
+        const uint32_t mask = 1UL << tmu;
+        if (enable)                 c->state.enabled_tmu |= mask;
+        else                        c->state.enabled_tmu &= ~mask;
+        if (c->state.enabled_tmu)   c->state.enables |= GGL_ENABLE_TMUS;
+        else                        c->state.enables &= ~GGL_ENABLE_TMUS;
+        ggl_state_changed(c, GGL_TMU_STATE);
+    }
+}
+
+        
+// ----------------------------------------------------------------------------
+
+int64_t ggl_system_time()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    struct timespec t;
+    t.tv_sec = t.tv_nsec = 0;
+    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t);
+    return int64_t(t.tv_sec)*1000000000LL + t.tv_nsec;
+#else
+    // we don't support the clocks here.
+    struct timeval t;
+    t.tv_sec = t.tv_usec = 0;
+    gettimeofday(&t, NULL);
+    return int64_t(t.tv_sec)*1000000000LL + int64_t(t.tv_usec)*1000LL;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_procs(context_t* c)
+{
+    GGLContext& procs = *(GGLContext*)c;
+    GGL_INIT_PROC(procs, scissor);
+    GGL_INIT_PROC(procs, activeTexture);
+    GGL_INIT_PROC(procs, bindTexture);
+    GGL_INIT_PROC(procs, bindTextureLod);
+    GGL_INIT_PROC(procs, colorBuffer);
+    GGL_INIT_PROC(procs, readBuffer);
+    GGL_INIT_PROC(procs, depthBuffer);
+    GGL_INIT_PROC(procs, enable);
+    GGL_INIT_PROC(procs, disable);
+    GGL_INIT_PROC(procs, enableDisable);
+    GGL_INIT_PROC(procs, shadeModel);
+    GGL_INIT_PROC(procs, color4xv);
+    GGL_INIT_PROC(procs, colorGrad12xv);
+    GGL_INIT_PROC(procs, zGrad3xv);
+    GGL_INIT_PROC(procs, wGrad3xv);
+    GGL_INIT_PROC(procs, fogGrad3xv);
+    GGL_INIT_PROC(procs, fogColor3xv);
+    GGL_INIT_PROC(procs, blendFunc);
+    GGL_INIT_PROC(procs, blendFuncSeparate);
+    GGL_INIT_PROC(procs, texEnvi);
+    GGL_INIT_PROC(procs, texEnvxv);
+    GGL_INIT_PROC(procs, texParameteri);
+    GGL_INIT_PROC(procs, texCoord2i);
+    GGL_INIT_PROC(procs, texCoord2x);
+    GGL_INIT_PROC(procs, texCoordGradScale8xv);
+    GGL_INIT_PROC(procs, texGeni);
+    GGL_INIT_PROC(procs, colorMask);
+    GGL_INIT_PROC(procs, depthMask);
+    GGL_INIT_PROC(procs, stencilMask);
+    GGL_INIT_PROC(procs, alphaFuncx);
+    GGL_INIT_PROC(procs, depthFunc);
+    GGL_INIT_PROC(procs, logicOp);
+    ggl_init_clear(c);
+}
+
+void ggl_init_context(context_t* c)
+{
+    memset(c, 0, sizeof(context_t));
+    ggl_init_procs(c);
+    ggl_init_trap(c);
+    ggl_init_scanline(c);
+    ggl_init_texture(c);
+    ggl_init_picker(c);
+    ggl_init_raster(c);
+    c->formats = gglGetPixelFormatTable();
+    c->state.blend.src = GGL_ONE;
+    c->state.blend.dst = GGL_ZERO;
+    c->state.blend.src_alpha = GGL_ONE;
+    c->state.blend.dst_alpha = GGL_ZERO;
+    c->state.mask.color = 0xF;
+    c->state.mask.depth = 0;
+    c->state.mask.stencil = 0xFFFFFFFF;
+    c->state.logic_op.opcode = GGL_COPY;
+    c->state.alpha_test.func = GGL_ALWAYS;
+    c->state.depth_test.func = GGL_LESS;
+    c->state.depth_test.clearValue = FIXED_ONE;
+    c->shade.w0 = FIXED_ONE;
+    memcpy(c->ditherMatrix, gDitherMatrix, sizeof(gDitherMatrix));
+}
+
+void ggl_uninit_context(context_t* c)
+{
+    ggl_uninit_scanline(c);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+
+
+using namespace android;
+
+ssize_t gglInit(GGLContext** context)
+{
+    void* const base = malloc(sizeof(context_t) + 32);
+	if (base) {
+        // always align the context on cache lines
+        context_t *c = (context_t *)((ptrdiff_t(base)+31) & ~0x1FL);
+        ggl_init_context(c);
+        c->base = base;
+		*context = (GGLContext*)c;
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+ssize_t gglUninit(GGLContext* con)
+{
+    GGL_CONTEXT(c, (void*)con);
+    ggl_uninit_context(c);
+	free(c->base);
+	return 0;
+}
+
diff --git a/libpixelflinger/raster.cpp b/libpixelflinger/raster.cpp
new file mode 100644
index 0000000..d751202
--- /dev/null
+++ b/libpixelflinger/raster.cpp
@@ -0,0 +1,217 @@
+/* libs/pixelflinger/raster.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+
+#include <string.h>
+
+#include "raster.h"
+#include "trap.h"
+
+namespace android {
+
+static void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y);
+static void ggl_rasterPos2i(void* con, GGLint x, GGLint y);
+static void ggl_copyPixels(void* con, GGLint xs, GGLint ys,
+        GGLsizei width, GGLsizei height, GGLenum type);
+
+void ggl_init_raster(context_t* c)
+{
+    GGLContext& procs = *(GGLContext*)c;
+    GGL_INIT_PROC(procs, copyPixels);
+    GGL_INIT_PROC(procs, rasterPos2x);
+    GGL_INIT_PROC(procs, rasterPos2i);
+}
+
+void ggl_rasterPos2x(void* con, GGLfixed x, GGLfixed y)
+{
+    GGL_CONTEXT(c, con);
+    // raster pos should be processed just like glVertex
+    c->state.raster.x = x;
+    c->state.raster.y = y;
+}
+
+void ggl_rasterPos2i(void* con, GGLint x, GGLint y)
+{
+    ggl_rasterPos2x(con, gglIntToFixed(x), gglIntToFixed(y));
+}
+
+void ggl_copyPixels(void* con, GGLint xs, GGLint ys,
+        GGLsizei width, GGLsizei height, GGLenum type)
+{
+    GGL_CONTEXT(c, con);
+
+    // color-buffer
+    surface_t* cb = &(c->state.buffers.color);
+
+    // undefined behaviour if we try to copy from outside the surface
+    if (uint32_t(xs) > cb->width)
+        return;
+    if (uint32_t(ys) > cb->height)
+        return;
+    if (uint32_t(xs + width) > cb->width)
+        return;
+    if (uint32_t(ys + height) > cb->height)
+        return;
+
+    // copy to current raster position
+    GGLint xd = gglFixedToIntRound(c->state.raster.x);
+    GGLint yd = gglFixedToIntRound(c->state.raster.y);
+
+    // clip to scissor
+    if (xd < GGLint(c->state.scissor.left)) {
+        GGLint offset = GGLint(c->state.scissor.left) - xd;
+        xd = GGLint(c->state.scissor.left);
+        xs += offset;
+        width -= offset;
+    }
+    if (yd < GGLint(c->state.scissor.top)) {
+        GGLint offset = GGLint(c->state.scissor.top) - yd;
+        yd = GGLint(c->state.scissor.top);
+        ys += offset;
+        height -= offset;
+    }
+    if ((xd + width) > GGLint(c->state.scissor.right)) {
+        width = GGLint(c->state.scissor.right) - xd;
+    }
+    if ((yd + height) > GGLint(c->state.scissor.bottom)) {
+        height = GGLint(c->state.scissor.bottom) - yd;
+    }
+
+    if (width<=0 || height<=0) {
+        return; // nothing to copy
+    }
+
+    if (xs==xd && ys==yd) {
+        // nothing to do, but be careful, this might not be true when we support
+        // gglPixelTransfer, gglPixelMap and gglPixelZoom
+        return;
+    }
+
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* src = reinterpret_cast<uint8_t*>(cb->data)
+            + (xs + (cb->stride * ys)) * fp->size;
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data)
+            + (xd + (cb->stride * yd)) * fp->size;
+    const size_t bpr = cb->stride * fp->size;
+    const size_t rowsize = width * fp->size;
+    size_t yc = height;
+
+    if (ys < yd) {
+        // bottom to top
+        src += height * bpr;
+        dst += height * bpr;
+        do {
+            dst -= bpr;
+            src -= bpr;
+            memcpy(dst, src, rowsize);
+        } while (--yc);
+    } else {
+        if (ys == yd) {
+            // might be right to left
+            do {
+                memmove(dst, src, rowsize);
+                dst += bpr;
+                src += bpr;
+            } while (--yc);
+        } else {
+            // top to bottom
+            do {
+                memcpy(dst, src, rowsize);
+                dst += bpr;
+                src += bpr;
+            } while (--yc);
+        }
+    }
+}
+
+}; // namespace android
+
+using namespace android;
+
+GGLint gglBitBlti(GGLContext* con, int tmu, GGLint crop[4], GGLint where[4])
+{
+    GGL_CONTEXT(c, (void*)con);
+
+     GGLint x = where[0];
+     GGLint y = where[1];
+     GGLint w = where[2];
+     GGLint h = where[3];
+
+    // exclsively enable this tmu
+    const GGLSurface& cbSurface = c->state.buffers.color.s;
+    c->procs.activeTexture(c, tmu);
+    c->procs.disable(c, GGL_W_LERP);
+
+    uint32_t tmus = 1UL<<tmu;
+    if (c->state.enabled_tmu != tmus) {
+        c->activeTMU->enable = 1;
+        c->state.enabled_tmu = tmus;
+        c->state.enables |= GGL_ENABLE_TMUS;
+        ggl_state_changed(c, GGL_TMU_STATE);
+    }
+
+    const GGLint Wcr = crop[2];
+    const GGLint Hcr = crop[3];
+    if ((w == Wcr) && (h == Hcr)) {
+        c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+        c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+        const GGLint Ucr = crop[0];
+        const GGLint Vcr = crop[1];
+        const GGLint s0  = Ucr - x;
+        const GGLint t0  = Vcr - y;
+        c->procs.texCoord2i(c, s0, t0);
+        c->procs.recti(c, x, y, x+w, y+h);
+    } else {
+        int32_t texcoords[8];
+        x = gglIntToFixed(x);
+        y = gglIntToFixed(y); 
+    
+        // we CLAMP here, which works with premultiplied (s,t)
+        c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
+        c->procs.texParameteri(c, GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
+        c->procs.texGeni(c, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+        c->procs.texGeni(c, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
+    
+        const GGLint Ucr = crop[0] << 16;
+        const GGLint Vcr = crop[1] << 16;
+        const GGLint Wcr = crop[2] << 16;
+        const GGLint Hcr = crop[3] << 16;
+    
+        // computes texture coordinates (pre-multiplied)
+        int32_t dsdx = Wcr / w;   // dsdx = ((Wcr/w)/Wt)*Wt
+        int32_t dtdy = Hcr / h;   // dtdy = ((Hcr/h)/Ht)*Ht
+        int32_t s0   = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
+        int32_t t0   = Vcr - gglMulx(dtdy, y); // t0 = Vcr - y * dtdy
+        texcoords[0] = s0;
+        texcoords[1] = dsdx;
+        texcoords[2] = 0;
+        texcoords[3] = t0;
+        texcoords[4] = 0;
+        texcoords[5] = dtdy;
+        texcoords[6] = 0;
+        texcoords[7] = 0;
+        c->procs.texCoordGradScale8xv(c, tmu, texcoords);
+        c->procs.recti(c, 
+                gglFixedToIntRound(x),
+                gglFixedToIntRound(y),
+                gglFixedToIntRound(x)+w,
+                gglFixedToIntRound(y)+h);
+    }
+    return 0;
+}
+
diff --git a/libpixelflinger/raster.h b/libpixelflinger/raster.h
new file mode 100644
index 0000000..9f0f240
--- /dev/null
+++ b/libpixelflinger/raster.h
@@ -0,0 +1,33 @@
+/* libs/pixelflinger/raster.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_GGL_RASTER_H
+#define ANDROID_GGL_RASTER_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_raster(context_t* c);
+
+void gglCopyPixels(void* c, GGLint x, GGLint y, GGLsizei width, GGLsizei height, GGLenum type);
+void gglRasterPos2d(void* c, GGLint x, GGLint y);
+
+}; // namespace android
+
+#endif // ANDROID_GGL_RASTER_H
diff --git a/libpixelflinger/rotate90CW_4x4_16v6.S b/libpixelflinger/rotate90CW_4x4_16v6.S
new file mode 100644
index 0000000..8e3e142
--- /dev/null
+++ b/libpixelflinger/rotate90CW_4x4_16v6.S
@@ -0,0 +1,62 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+    .text
+    .align
+    
+    .global rotate90CW_4x4_16v6
+
+// Rotates 90deg CW a 4x4 block of 16bpp pixels using ARMv6
+// src and dst must be 4 pixels-aligned (2-pixels aligned might
+// actually work)
+//
+// The code below is complicated by ARM's little endianness. 
+
+rotate90CW_4x4_16v6:
+    // r0 = dst
+    // r1 = src
+    // r2 = dst stride in pixels
+    // r3 = src stride in pixels
+
+    stmfd   sp!, {r4,r5, r6,r7, r8,r9, r10,r11, lr}
+    add     r14, r3, r3
+    add     r12, r2, r2
+
+    ldrd    r2, r3, [r1], r14
+    ldrd    r4, r5, [r1], r14
+    ldrd    r6, r7, [r1], r14
+    ldrd    r8, r9, [r1]
+
+    pkhbt   r10, r8, r6, lsl #16
+    pkhbt   r11, r4, r2, lsl #16
+    strd    r10, r11, [r0], r12  
+
+    pkhtb   r10, r6, r8, asr #16
+    pkhtb   r11, r2, r4, asr #16
+
+    strd    r10, r11, [r0], r12  
+    pkhbt   r10, r9, r7, lsl #16
+    pkhbt   r11, r5, r3, lsl #16
+
+    strd    r10, r11, [r0], r12  
+
+    pkhtb   r10, r7, r9, asr #16
+    pkhtb   r11, r3, r5, asr #16
+    strd    r10, r11, [r0]
+
+    ldmfd   sp!, {r4,r5, r6,r7, r8,r9, r10,r11, pc}
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
new file mode 100644
index 0000000..f700306
--- /dev/null
+++ b/libpixelflinger/scanline.cpp
@@ -0,0 +1,1496 @@
+/* libs/pixelflinger/scanline.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "pixelflinger"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <cutils/memory.h>
+#include <cutils/log.h>
+
+#include "buffer.h"
+#include "scanline.h"
+
+#include "codeflinger/CodeCache.h"
+#include "codeflinger/GGLAssembler.h"
+#include "codeflinger/ARMAssembler.h"
+//#include "codeflinger/ARMAssemblerOptimizer.h"
+
+// ----------------------------------------------------------------------------
+
+#define ANDROID_CODEGEN_GENERIC     0   // force generic pixel pipeline
+#define ANDROID_CODEGEN_C           1   // hand-written C, fallback generic 
+#define ANDROID_CODEGEN_ASM         2   // hand-written asm, fallback generic
+#define ANDROID_CODEGEN_GENERATED   3   // hand-written asm, fallback codegen
+
+#ifdef NDEBUG
+#   define ANDROID_RELEASE
+#   define ANDROID_CODEGEN      ANDROID_CODEGEN_GENERATED
+#else
+#   define ANDROID_DEBUG
+#   define ANDROID_CODEGEN      ANDROID_CODEGEN_GENERATED
+#endif
+
+#if defined(__arm__)
+#   define ANDROID_ARM_CODEGEN  1
+#else
+#   define ANDROID_ARM_CODEGEN  0
+#endif
+
+#define DEBUG__CODEGEN_ONLY     0
+
+
+#define ASSEMBLY_SCRATCH_SIZE   2048
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+static void init_y(context_t*, int32_t);
+static void init_y_noop(context_t*, int32_t);
+static void init_y_packed(context_t*, int32_t);
+static void init_y_error(context_t*, int32_t);
+
+static void step_y__generic(context_t* c);
+static void step_y__nop(context_t*);
+static void step_y__smooth(context_t* c);
+static void step_y__tmu(context_t* c);
+static void step_y__w(context_t* c);
+
+static void scanline(context_t* c);
+static void scanline_perspective(context_t* c);
+static void scanline_perspective_single(context_t* c);
+static void scanline_t32cb16blend(context_t* c);
+static void scanline_t32cb16(context_t* c);
+static void scanline_memcpy(context_t* c);
+static void scanline_memset8(context_t* c);
+static void scanline_memset16(context_t* c);
+static void scanline_memset32(context_t* c);
+static void scanline_noop(context_t* c);
+static void scanline_set(context_t* c);
+static void scanline_clear(context_t* c);
+
+static void rect_generic(context_t* c, size_t yc);
+static void rect_memcpy(context_t* c, size_t yc);
+
+extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct);
+
+// ----------------------------------------------------------------------------
+
+struct shortcut_t {
+    needs_filter_t  filter;
+    const char*     desc;
+    void            (*scanline)(context_t*);
+    void            (*init_y)(context_t*, int32_t);
+};
+
+// Keep in sync with needs
+static shortcut_t shortcuts[] = {
+    { { { 0x03515104, 0x00000077, { 0x00000A01, 0x00000000 } },
+        { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+        "565 fb, 8888 tx, blend", scanline_t32cb16blend, init_y_noop },
+    { { { 0x03010104, 0x00000077, { 0x00000A01, 0x00000000 } },
+        { 0xFFFFFFFF, 0xFFFFFFFF, { 0xFFFFFFFF, 0x0000003F } } },
+        "565 fb, 8888 tx", scanline_t32cb16, init_y_noop  },  
+    { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+        { 0x00000000, 0x00000007, { 0x00000000, 0x00000000 } } },
+        "(nop) alpha test", scanline_noop, init_y_noop },
+    { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+        { 0x00000000, 0x00000070, { 0x00000000, 0x00000000 } } },
+        "(nop) depth test", scanline_noop, init_y_noop },
+    { { { 0x05000000, 0x00000000, { 0x00000000, 0x00000000 } },
+        { 0x0F000000, 0x00000080, { 0x00000000, 0x00000000 } } },
+        "(nop) logic_op", scanline_noop, init_y_noop },
+    { { { 0xF0000000, 0x00000000, { 0x00000000, 0x00000000 } },
+        { 0xF0000000, 0x00000080, { 0x00000000, 0x00000000 } } },
+        "(nop) color mask", scanline_noop, init_y_noop },
+    { { { 0x0F000000, 0x00000077, { 0x00000000, 0x00000000 } },
+        { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } },
+        "(set) logic_op", scanline_set, init_y_noop },
+    { { { 0x00000000, 0x00000077, { 0x00000000, 0x00000000 } },
+        { 0xFF000000, 0x000000F7, { 0x00000000, 0x00000000 } } },
+        "(clear) logic_op", scanline_clear, init_y_noop },
+    { { { 0x03000000, 0x00000077, { 0x00000000, 0x00000000 } },
+        { 0xFFFFFF00, 0x000000F7, { 0x00000000, 0x00000000 } } },
+        "(clear) blending 0/0", scanline_clear, init_y_noop },
+    { { { 0x00000000, 0x00000000, { 0x00000000, 0x00000000 } },
+        { 0x0000003F, 0x00000000, { 0x00000000, 0x00000000 } } },
+        "(error) invalid color-buffer format", scanline_noop, init_y_error },
+};
+static const needs_filter_t noblend1to1 = {
+        // (disregard dithering, see below)
+        { 0x03010100, 0x00000077, { 0x00000A00, 0x00000000 } },
+        { 0xFFFFFFC0, 0xFFFFFEFF, { 0xFFFFFFC0, 0x0000003F } }
+};
+static  const needs_filter_t fill16noblend = {
+        { 0x03010100, 0x00000077, { 0x00000000, 0x00000000 } },
+        { 0xFFFFFFC0, 0xFFFFFFFF, { 0x0000003F, 0x0000003F } }
+};
+
+// ----------------------------------------------------------------------------
+
+#if ANDROID_ARM_CODEGEN
+static CodeCache gCodeCache(12 * 1024);
+
+class ScanlineAssembly : public Assembly {
+    AssemblyKey<needs_t> mKey;
+public:
+    ScanlineAssembly(needs_t needs, size_t size)
+        : Assembly(size), mKey(needs) { }
+    const AssemblyKey<needs_t>& key() const { return mKey; }
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+void ggl_init_scanline(context_t* c)
+{
+    c->init_y = init_y;
+    c->step_y = step_y__generic;
+    c->scanline = scanline;
+}
+
+void ggl_uninit_scanline(context_t* c)
+{
+    if (c->state.buffers.coverage)
+        free(c->state.buffers.coverage);
+#if ANDROID_ARM_CODEGEN
+    if (c->scanline_as)
+        c->scanline_as->decStrong(c);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+static void pick_scanline(context_t* c)
+{
+#if (!defined(DEBUG__CODEGEN_ONLY) || (DEBUG__CODEGEN_ONLY == 0))
+
+#if ANDROID_CODEGEN == ANDROID_CODEGEN_GENERIC
+    c->init_y = init_y;
+    c->step_y = step_y__generic;
+    c->scanline = scanline;
+    return;
+#endif
+
+    //printf("*** needs [%08lx:%08lx:%08lx:%08lx]\n",
+    //    c->state.needs.n, c->state.needs.p,
+    //    c->state.needs.t[0], c->state.needs.t[1]);
+
+    // first handle the special case that we cannot test with a filter
+    const uint32_t cb_format = GGL_READ_NEEDS(CB_FORMAT, c->state.needs.n);
+    if (GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0]) == cb_format) {
+        if (c->state.needs.match(noblend1to1)) {
+            // this will match regardless of dithering state, since both
+            // src and dest have the same format anyway, there is no dithering
+            // to be done.
+            const GGLFormat* f =
+                &(c->formats[GGL_READ_NEEDS(T_FORMAT, c->state.needs.t[0])]);
+            if ((f->components == GGL_RGB) ||
+                (f->components == GGL_RGBA) ||
+                (f->components == GGL_LUMINANCE) ||
+                (f->components == GGL_LUMINANCE_ALPHA))
+            {
+                // format must have all of RGB components
+                // (so the current color doesn't show through)
+                c->scanline = scanline_memcpy;
+                c->init_y = init_y_noop;
+                return;
+            }
+        }
+    }
+
+    if (c->state.needs.match(fill16noblend)) {
+        c->init_y = init_y_packed;
+        switch (c->formats[cb_format].size) {
+        case 1: c->scanline = scanline_memset8;  return;
+        case 2: c->scanline = scanline_memset16; return;
+        case 4: c->scanline = scanline_memset32; return;
+        }
+    }
+
+    const int numFilters = sizeof(shortcuts)/sizeof(shortcut_t);
+    for (int i=0 ; i<numFilters ; i++) {
+        if (c->state.needs.match(shortcuts[i].filter)) {
+            c->scanline = shortcuts[i].scanline;
+            c->init_y = shortcuts[i].init_y;
+            return;
+        }
+    }
+
+#endif // DEBUG__CODEGEN_ONLY
+
+    c->init_y = init_y;
+    c->step_y = step_y__generic;
+
+#if ANDROID_ARM_CODEGEN
+    // we're going to have to generate some code...
+    // here, generate code for our pixel pipeline
+    const AssemblyKey<needs_t> key(c->state.needs);
+    sp<Assembly> assembly = gCodeCache.lookup(key);
+    if (assembly == 0) {
+        // create a new assembly region
+        sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs, 
+                ASSEMBLY_SCRATCH_SIZE);
+        // initialize our assembler
+        GGLAssembler assembler( new ARMAssembler(a) );
+        //GGLAssembler assembler(
+        //        new ARMAssemblerOptimizer(new ARMAssembler(a)) );
+        // generate the scanline code for the given needs
+        int err = assembler.scanline(c->state.needs, c);
+        if (ggl_likely(!err)) {
+            // finally, cache this assembly
+            err = gCodeCache.cache(a->key(), a);
+        }
+        if (ggl_unlikely(err)) {
+            LOGE("error generating or caching assembly. Reverting to NOP.");
+            c->scanline = scanline_noop;
+            c->init_y = init_y_noop;
+            c->step_y = step_y__nop;
+            return;
+        }
+        assembly = a;
+    }
+
+    // release the previous assembly
+    if (c->scanline_as) {
+        c->scanline_as->decStrong(c);
+    }
+
+    //LOGI("using generated pixel-pipeline");
+    c->scanline_as = assembly.get();
+    c->scanline_as->incStrong(c); //  hold on to assembly
+    c->scanline = (void(*)(context_t* c))assembly->base();
+#else
+//    LOGW("using generic (slow) pixel-pipeline");
+    c->scanline = scanline;
+#endif
+}
+
+void ggl_pick_scanline(context_t* c)
+{
+    pick_scanline(c);
+    if ((c->state.enables & GGL_ENABLE_W) &&
+        (c->state.enables & GGL_ENABLE_TMUS))
+    {
+        c->span = c->scanline;
+        c->scanline = scanline_perspective;
+        if (!(c->state.enabled_tmu & (c->state.enabled_tmu - 1))) {
+            // only one TMU enabled
+            c->scanline = scanline_perspective_single;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+static void blending(context_t* c, pixel_t* fragment, pixel_t* fb);
+static void blend_factor(context_t* c, pixel_t* r, uint32_t factor,
+        const pixel_t* src, const pixel_t* dst);
+static void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv);
+
+#if ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED)
+
+// no need to compile the generic-pipeline, it can't be reached
+void scanline(context_t*)
+{
+}
+
+#else
+
+void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv)
+{
+    if (su && sv) {
+        if (su > sv) {
+            v = ggl_expand(v, sv, su);
+            sv = su;
+        } else if (su < sv) {
+            u = ggl_expand(u, su, sv);
+            su = sv;
+        }
+    }
+}
+
+void blending(context_t* c, pixel_t* fragment, pixel_t* fb)
+{
+    rescale(fragment->c[0], fragment->s[0], fb->c[0], fb->s[0]);
+    rescale(fragment->c[1], fragment->s[1], fb->c[1], fb->s[1]);
+    rescale(fragment->c[2], fragment->s[2], fb->c[2], fb->s[2]);
+    rescale(fragment->c[3], fragment->s[3], fb->c[3], fb->s[3]);
+
+    pixel_t sf, df;
+    blend_factor(c, &sf, c->state.blend.src, fragment, fb);
+    blend_factor(c, &df, c->state.blend.dst, fragment, fb);
+
+    fragment->c[1] =
+            gglMulAddx(fragment->c[1], sf.c[1], gglMulx(fb->c[1], df.c[1]));
+    fragment->c[2] =
+            gglMulAddx(fragment->c[2], sf.c[2], gglMulx(fb->c[2], df.c[2]));
+    fragment->c[3] =
+            gglMulAddx(fragment->c[3], sf.c[3], gglMulx(fb->c[3], df.c[3]));
+
+    if (c->state.blend.alpha_separate) {
+        blend_factor(c, &sf, c->state.blend.src_alpha, fragment, fb);
+        blend_factor(c, &df, c->state.blend.dst_alpha, fragment, fb);
+    }
+
+    fragment->c[0] =
+            gglMulAddx(fragment->c[0], sf.c[0], gglMulx(fb->c[0], df.c[0]));
+
+    // clamp to 1.0
+    if (fragment->c[0] >= (1LU<<fragment->s[0]))
+        fragment->c[0] = (1<<fragment->s[0])-1;
+    if (fragment->c[1] >= (1LU<<fragment->s[1]))
+        fragment->c[1] = (1<<fragment->s[1])-1;
+    if (fragment->c[2] >= (1LU<<fragment->s[2]))
+        fragment->c[2] = (1<<fragment->s[2])-1;
+    if (fragment->c[3] >= (1LU<<fragment->s[3]))
+        fragment->c[3] = (1<<fragment->s[3])-1;
+}
+
+static inline int blendfactor(uint32_t x, uint32_t size, uint32_t def = 0)
+{
+    if (!size)
+        return def;
+
+    // scale to 16 bits
+    if (size > 16) {
+        x >>= (size - 16);
+    } else if (size < 16) {
+        x = ggl_expand(x, size, 16);
+    }
+    x += x >> 15;
+    return x;
+}
+
+void blend_factor(context_t* c, pixel_t* r, 
+        uint32_t factor, const pixel_t* src, const pixel_t* dst)
+{
+    switch (factor) {
+        case GGL_ZERO:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = 0;
+            break;
+        case GGL_ONE:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = FIXED_ONE;
+            break;
+        case GGL_DST_COLOR:
+            r->c[1] = blendfactor(dst->c[1], dst->s[1]);
+            r->c[2] = blendfactor(dst->c[2], dst->s[2]);
+            r->c[3] = blendfactor(dst->c[3], dst->s[3]);
+            r->c[0] = blendfactor(dst->c[0], dst->s[0]);
+            break;
+        case GGL_SRC_COLOR:
+            r->c[1] = blendfactor(src->c[1], src->s[1]);
+            r->c[2] = blendfactor(src->c[2], src->s[2]);
+            r->c[3] = blendfactor(src->c[3], src->s[3]);
+            r->c[0] = blendfactor(src->c[0], src->s[0]);
+            break;
+        case GGL_ONE_MINUS_DST_COLOR:
+            r->c[1] = FIXED_ONE - blendfactor(dst->c[1], dst->s[1]);
+            r->c[2] = FIXED_ONE - blendfactor(dst->c[2], dst->s[2]);
+            r->c[3] = FIXED_ONE - blendfactor(dst->c[3], dst->s[3]);
+            r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0]);
+            break;
+        case GGL_ONE_MINUS_SRC_COLOR:
+            r->c[1] = FIXED_ONE - blendfactor(src->c[1], src->s[1]);
+            r->c[2] = FIXED_ONE - blendfactor(src->c[2], src->s[2]);
+            r->c[3] = FIXED_ONE - blendfactor(src->c[3], src->s[3]);
+            r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0]);
+            break;
+        case GGL_SRC_ALPHA:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = blendfactor(src->c[0], src->s[0], FIXED_ONE);
+            break;
+        case GGL_ONE_MINUS_SRC_ALPHA:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = FIXED_ONE - blendfactor(src->c[0], src->s[0], FIXED_ONE);
+            break;
+        case GGL_DST_ALPHA:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = blendfactor(dst->c[0], dst->s[0], FIXED_ONE);
+            break;
+        case GGL_ONE_MINUS_DST_ALPHA:
+            r->c[1] = 
+            r->c[2] = 
+            r->c[3] = 
+            r->c[0] = FIXED_ONE - blendfactor(dst->c[0], dst->s[0], FIXED_ONE);
+            break;
+        case GGL_SRC_ALPHA_SATURATE:
+            // XXX: GGL_SRC_ALPHA_SATURATE
+            break;
+    }
+}
+
+static GGLfixed wrapping(int32_t coord, uint32_t size, int tx_wrap)
+{
+    GGLfixed d;
+    if (tx_wrap == GGL_REPEAT) {
+        d = (uint32_t(coord)>>16) * size;
+    } else if (tx_wrap == GGL_CLAMP) { // CLAMP_TO_EDGE semantics
+        const GGLfixed clamp_min = FIXED_HALF;
+        const GGLfixed clamp_max = (size << 16) - FIXED_HALF;
+        if (coord < clamp_min)     coord = clamp_min;
+        if (coord > clamp_max)     coord = clamp_max;
+        d = coord;
+    } else { // 1:1
+        const GGLfixed clamp_min = 0;
+        const GGLfixed clamp_max = (size << 16);
+        if (coord < clamp_min)     coord = clamp_min;
+        if (coord > clamp_max)     coord = clamp_max;
+        d = coord;
+    }
+    return d;
+}
+
+static inline
+GGLcolor ADJUST_COLOR_ITERATOR(GGLcolor v, GGLcolor dvdx, int len)
+{
+    const int32_t end = dvdx * (len-1) + v;
+    if (end < 0)
+        v -= end;
+    v &= ~(v>>31);
+    return v;
+}
+
+void scanline(context_t* c)
+{
+    const uint32_t enables = c->state.enables;
+    const int xs = c->iterators.xl;
+    const int x1 = c->iterators.xr;
+	int xc = x1 - xs;
+    const int16_t* covPtr = c->state.buffers.coverage + xs;
+
+    // All iterated values are sampled at the pixel center
+
+    // reset iterators for that scanline...
+    GGLcolor r, g, b, a;
+    iterators_t& ci = c->iterators;
+    if (enables & GGL_ENABLE_SMOOTH) {
+        r = (xs * c->shade.drdx) + ci.ydrdy;
+        g = (xs * c->shade.dgdx) + ci.ydgdy;
+        b = (xs * c->shade.dbdx) + ci.ydbdy;
+        a = (xs * c->shade.dadx) + ci.ydady;
+        r = ADJUST_COLOR_ITERATOR(r, c->shade.drdx, xc);
+        g = ADJUST_COLOR_ITERATOR(g, c->shade.dgdx, xc);
+        b = ADJUST_COLOR_ITERATOR(b, c->shade.dbdx, xc);
+        a = ADJUST_COLOR_ITERATOR(a, c->shade.dadx, xc);
+    } else {
+        r = ci.ydrdy;
+        g = ci.ydgdy;
+        b = ci.ydbdy;
+        a = ci.ydady;
+    }
+
+    // z iterators are 1.31
+    GGLfixed z = (xs * c->shade.dzdx) + ci.ydzdy;
+    GGLfixed f = (xs * c->shade.dfdx) + ci.ydfdy;
+
+    struct {
+        GGLfixed s, t;
+    } tc[GGL_TEXTURE_UNIT_COUNT];
+    if (enables & GGL_ENABLE_TMUS) {
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+            if (c->state.texture[i].enable) {
+                texture_iterators_t& ti = c->state.texture[i].iterators;
+                if (enables & GGL_ENABLE_W) {
+                    tc[i].s = ti.ydsdy;
+                    tc[i].t = ti.ydtdy;
+                } else {
+                    tc[i].s = (xs * ti.dsdx) + ti.ydsdy;
+                    tc[i].t = (xs * ti.dtdx) + ti.ydtdy;
+                }
+            }
+        }
+    }
+
+    pixel_t fragment;
+    pixel_t texel;
+    pixel_t fb;
+
+	uint32_t x = xs;
+	uint32_t y = c->iterators.y;
+
+	while (xc--) {
+    
+        { // just a scope
+
+		// read color (convert to 8 bits by keeping only the integer part)
+        fragment.s[1] = fragment.s[2] =
+        fragment.s[3] = fragment.s[0] = 8;
+        fragment.c[1] = r >> (GGL_COLOR_BITS-8);
+        fragment.c[2] = g >> (GGL_COLOR_BITS-8);
+        fragment.c[3] = b >> (GGL_COLOR_BITS-8);
+        fragment.c[0] = a >> (GGL_COLOR_BITS-8);
+
+		// texturing
+        if (enables & GGL_ENABLE_TMUS) {
+            for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+                texture_t& tx = c->state.texture[i];
+                if (!tx.enable)
+                    continue;
+                texture_iterators_t& ti = tx.iterators;
+                int32_t u, v;
+
+                // s-coordinate
+                if (tx.s_coord != GGL_ONE_TO_ONE) {
+                    const int w = tx.surface.width;
+                    u = wrapping(tc[i].s, w, tx.s_wrap);
+                    tc[i].s += ti.dsdx;
+                } else {
+                    u = (((tx.shade.is0>>16) + x)<<16) + FIXED_HALF;
+                }
+
+                // t-coordinate
+                if (tx.t_coord != GGL_ONE_TO_ONE) {
+                    const int h = tx.surface.height;
+                    v = wrapping(tc[i].t, h, tx.t_wrap);
+                    tc[i].t += ti.dtdx;
+                } else {
+                    v = (((tx.shade.it0>>16) + y)<<16) + FIXED_HALF;
+                }
+
+                // read texture
+                if (tx.mag_filter == GGL_NEAREST &&
+                    tx.min_filter == GGL_NEAREST)
+                {
+                    u >>= 16;
+                    v >>= 16;
+                    tx.surface.read(&tx.surface, c, u, v, &texel);
+                } else {
+                    const int w = tx.surface.width;
+                    const int h = tx.surface.height;
+                    u -= FIXED_HALF;
+                    v -= FIXED_HALF;
+                    int u0 = u >> 16;
+                    int v0 = v >> 16;
+                    int u1 = u0 + 1;
+                    int v1 = v0 + 1;
+                    if (tx.s_wrap == GGL_REPEAT) {
+                        if (u0<0)  u0 += w;
+                        if (u1<0)  u1 += w;
+                        if (u0>=w) u0 -= w;
+                        if (u1>=w) u1 -= w;
+                    } else {
+                        if (u0<0)  u0 = 0;
+                        if (u1<0)  u1 = 0;
+                        if (u0>=w) u0 = w-1;
+                        if (u1>=w) u1 = w-1;
+                    }
+                    if (tx.t_wrap == GGL_REPEAT) {
+                        if (v0<0)  v0 += h;
+                        if (v1<0)  v1 += h;
+                        if (v0>=h) v0 -= h;
+                        if (v1>=h) v1 -= h;
+                    } else {
+                        if (v0<0)  v0 = 0;
+                        if (v1<0)  v1 = 0;
+                        if (v0>=h) v0 = h-1;
+                        if (v1>=h) v1 = h-1;
+                    }
+                    pixel_t texels[4];
+                    uint32_t mm[4];
+                    tx.surface.read(&tx.surface, c, u0, v0, &texels[0]);
+                    tx.surface.read(&tx.surface, c, u0, v1, &texels[1]);
+                    tx.surface.read(&tx.surface, c, u1, v0, &texels[2]);
+                    tx.surface.read(&tx.surface, c, u1, v1, &texels[3]);
+                    u = (u >> 12) & 0xF; 
+                    v = (v >> 12) & 0xF;
+                    u += u>>3;
+                    v += v>>3;
+                    mm[0] = (0x10 - u) * (0x10 - v);
+                    mm[1] = (0x10 - u) * v;
+                    mm[2] = u * (0x10 - v);
+                    mm[3] = 0x100 - (mm[0] + mm[1] + mm[2]);
+                    for (int j=0 ; j<4 ; j++) {
+                        texel.s[j] = texels[0].s[j];
+                        if (!texel.s[j]) continue;
+                        texel.s[j] += 8;
+                        texel.c[j] =    texels[0].c[j]*mm[0] +
+                                        texels[1].c[j]*mm[1] +
+                                        texels[2].c[j]*mm[2] +
+                                        texels[3].c[j]*mm[3] ;
+                    }
+                }
+
+                // Texture environnement...
+                for (int j=0 ; j<4 ; j++) {
+                    uint32_t& Cf = fragment.c[j];
+                    uint32_t& Ct = texel.c[j];
+                    uint8_t& sf  = fragment.s[j];
+                    uint8_t& st  = texel.s[j];
+                    uint32_t At = texel.c[0];
+                    uint8_t sat = texel.s[0];
+                    switch (tx.env) {
+                    case GGL_REPLACE:
+                        if (st) {
+                            Cf = Ct;
+                            sf = st;
+                        }
+                        break;
+                    case GGL_MODULATE:
+                        if (st) {
+                            uint32_t factor = Ct + (Ct>>(st-1));
+                            Cf = (Cf * factor) >> st;
+                        }
+                        break;
+                    case GGL_DECAL:
+                        if (sat) {
+                            rescale(Cf, sf, Ct, st);
+                            Cf += ((Ct - Cf) * (At + (At>>(sat-1)))) >> sat;
+                        }
+                        break;
+                    case GGL_BLEND:
+                        if (st) {
+                            uint32_t Cc = tx.env_color[i];
+                            if (sf>8)       Cc = (Cc * ((1<<sf)-1))>>8;
+                            else if (sf<8)  Cc = (Cc - (Cc>>(8-sf)))>>(8-sf);
+                            uint32_t factor = Ct + (Ct>>(st-1));
+                            Cf = ((((1<<st) - factor) * Cf) + Ct*Cc)>>st;
+                        }
+                        break;
+                    case GGL_ADD:
+                        if (st) {
+                            rescale(Cf, sf, Ct, st);
+                            Cf += Ct;
+                        }
+                        break;
+                    }
+                }
+            }
+		}
+    
+        // coverage application
+        if (enables & GGL_ENABLE_AA) {
+            int16_t cf = *covPtr++;
+            fragment.c[0] = (int64_t(fragment.c[0]) * cf) >> 15;
+        }
+        
+        // alpha-test
+        if (enables & GGL_ENABLE_ALPHA_TEST) {
+            GGLcolor ref = c->state.alpha_test.ref;
+            GGLcolor alpha = (uint64_t(fragment.c[0]) *
+                    ((1<<GGL_COLOR_BITS)-1)) / ((1<<fragment.s[0])-1);
+            switch (c->state.alpha_test.func) {
+            case GGL_NEVER:     goto discard;
+            case GGL_LESS:      if (alpha<ref)  break; goto discard;
+            case GGL_EQUAL:     if (alpha==ref) break; goto discard;
+            case GGL_LEQUAL:    if (alpha<=ref) break; goto discard;
+            case GGL_GREATER:   if (alpha>ref)  break; goto discard;
+            case GGL_NOTEQUAL:  if (alpha!=ref) break; goto discard;
+            case GGL_GEQUAL:    if (alpha>=ref) break; goto discard;
+            }
+        }
+        
+        // depth test
+        if (c->state.buffers.depth.format) {
+            if (enables & GGL_ENABLE_DEPTH_TEST) {
+                surface_t* cb = &(c->state.buffers.depth);
+                uint16_t* p = (uint16_t*)(cb->data)+(x+(cb->stride*y));
+                uint16_t zz = uint32_t(z)>>(16);
+                uint16_t depth = *p;
+                switch (c->state.depth_test.func) {
+                case GGL_NEVER:     goto discard;
+                case GGL_LESS:      if (zz<depth)    break; goto discard;
+                case GGL_EQUAL:     if (zz==depth)   break; goto discard;
+                case GGL_LEQUAL:    if (zz<=depth)   break; goto discard;
+                case GGL_GREATER:   if (zz>depth)    break; goto discard;
+                case GGL_NOTEQUAL:  if (zz!=depth)   break; goto discard;
+                case GGL_GEQUAL:    if (zz>=depth)   break; goto discard;
+                }
+                // depth buffer is not enabled, if depth-test is not enabled
+/*
+        fragment.s[1] = fragment.s[2] =
+        fragment.s[3] = fragment.s[0] = 8;
+        fragment.c[1] = 
+        fragment.c[2] = 
+        fragment.c[3] = 
+        fragment.c[0] = 255 - (zz>>8);
+*/
+                if (c->state.mask.depth) {
+                    *p = zz;
+                }
+            }
+        }
+
+        // fog
+        if (enables & GGL_ENABLE_FOG) {
+            for (int i=1 ; i<=3 ; i++) {
+                GGLfixed fc = (c->state.fog.color[i] * 0x10000) / 0xFF;
+                uint32_t& c = fragment.c[i];
+                uint8_t& s  = fragment.s[i];
+                c = (c * 0x10000) / ((1<<s)-1);
+                c = gglMulAddx(c, f, gglMulx(fc, 0x10000 - f));
+                s = 16;
+            }
+        }
+
+        // blending
+        if (enables & GGL_ENABLE_BLENDING) {
+            fb.c[1] = fb.c[2] = fb.c[3] = fb.c[0] = 0; // placate valgrind
+            fb.s[1] = fb.s[2] = fb.s[3] = fb.s[0] = 0;
+            c->state.buffers.color.read(
+                    &(c->state.buffers.color), c, x, y, &fb);
+            blending( c, &fragment, &fb );
+        }
+
+		// write
+        c->state.buffers.color.write(
+                &(c->state.buffers.color), c, x, y, &fragment);
+        }
+
+discard:
+		// iterate...
+        x += 1;
+        if (enables & GGL_ENABLE_SMOOTH) {
+            r += c->shade.drdx;
+            g += c->shade.dgdx;
+            b += c->shade.dbdx;
+            a += c->shade.dadx;
+        }
+        z += c->shade.dzdx;
+        f += c->shade.dfdx;
+	}
+}
+
+#endif // ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED)
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Scanline
+#endif
+
+template <typename T, typename U>
+static inline __attribute__((const))
+T interpolate(int y, T v0, U dvdx, U dvdy) {
+    // interpolates in pixel's centers
+    // v = v0 + (y + 0.5) * dvdy + (0.5 * dvdx)
+    return (y * dvdy) + (v0 + ((dvdy + dvdx) >> 1));
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void init_y(context_t* c, int32_t ys)
+{
+    const uint32_t enables = c->state.enables;
+
+    // compute iterators...
+    iterators_t& ci = c->iterators;
+    
+    // sample in the center
+    ci.y = ys;
+
+    if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_W|GGL_ENABLE_FOG)) {
+        ci.ydzdy = interpolate(ys, c->shade.z0, c->shade.dzdx, c->shade.dzdy);
+        ci.ydwdy = interpolate(ys, c->shade.w0, c->shade.dwdx, c->shade.dwdy);
+        ci.ydfdy = interpolate(ys, c->shade.f0, c->shade.dfdx, c->shade.dfdy);
+    }
+
+    if (ggl_unlikely(enables & GGL_ENABLE_SMOOTH)) {
+        ci.ydrdy = interpolate(ys, c->shade.r0, c->shade.drdx, c->shade.drdy);
+        ci.ydgdy = interpolate(ys, c->shade.g0, c->shade.dgdx, c->shade.dgdy);
+        ci.ydbdy = interpolate(ys, c->shade.b0, c->shade.dbdx, c->shade.dbdy);
+        ci.ydady = interpolate(ys, c->shade.a0, c->shade.dadx, c->shade.dady);
+        c->step_y = step_y__smooth;
+    } else {
+        ci.ydrdy = c->shade.r0;
+        ci.ydgdy = c->shade.g0;
+        ci.ydbdy = c->shade.b0;
+        ci.ydady = c->shade.a0;
+        // XXX: do only if needed, or make sure this is fast
+        c->packed = ggl_pack_color(c, c->state.buffers.color.format,
+                ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady);
+        c->packed8888 = ggl_pack_color(c, GGL_PIXEL_FORMAT_RGBA_8888, 
+                ci.ydrdy, ci.ydgdy, ci.ydbdy, ci.ydady);
+    }
+
+    // initialize the variables we need in the shader
+    generated_vars_t& gen = c->generated_vars;
+    gen.argb[GGLFormat::ALPHA].c  = ci.ydady;
+    gen.argb[GGLFormat::ALPHA].dx = c->shade.dadx;
+    gen.argb[GGLFormat::RED  ].c  = ci.ydrdy;
+    gen.argb[GGLFormat::RED  ].dx = c->shade.drdx;
+    gen.argb[GGLFormat::GREEN].c  = ci.ydgdy;
+    gen.argb[GGLFormat::GREEN].dx = c->shade.dgdx;
+    gen.argb[GGLFormat::BLUE ].c  = ci.ydbdy;
+    gen.argb[GGLFormat::BLUE ].dx = c->shade.dbdx;
+    gen.dzdx = c->shade.dzdx;
+    gen.f    = ci.ydfdy;
+    gen.dfdx = c->shade.dfdx;
+
+    if (enables & GGL_ENABLE_TMUS) {
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+            texture_t& t = c->state.texture[i];
+            if (!t.enable) continue;
+
+            texture_iterators_t& ti = t.iterators;
+            if (t.s_coord == GGL_ONE_TO_ONE && t.t_coord == GGL_ONE_TO_ONE) {
+                // we need to set all of these to 0 because in some cases
+                // step_y__generic() or step_y__tmu() will be used and
+                // therefore will update dtdy, however, in 1:1 mode
+                // this is always done by the scanline rasterizer.
+                ti.dsdx = ti.dsdy = ti.dtdx = ti.dtdy = 0;
+                ti.ydsdy = t.shade.is0;
+                ti.ydtdy = t.shade.it0;
+            } else {
+                const int adjustSWrap = ((t.s_wrap==GGL_CLAMP)?0:16);
+                const int adjustTWrap = ((t.t_wrap==GGL_CLAMP)?0:16);
+                ti.sscale = t.shade.sscale + adjustSWrap;
+                ti.tscale = t.shade.tscale + adjustTWrap;
+                if (!(enables & GGL_ENABLE_W)) {
+                    // S coordinate
+                    const int32_t sscale = ti.sscale;
+                    const int32_t sy = interpolate(ys,
+                            t.shade.is0, t.shade.idsdx, t.shade.idsdy);
+                    if (sscale>=0) {
+                        ti.ydsdy= sy            << sscale;
+                        ti.dsdx = t.shade.idsdx << sscale; 
+                        ti.dsdy = t.shade.idsdy << sscale;
+                    } else {
+                        ti.ydsdy= sy            >> -sscale;
+                        ti.dsdx = t.shade.idsdx >> -sscale; 
+                        ti.dsdy = t.shade.idsdy >> -sscale;
+                    }
+                    // T coordinate
+                    const int32_t tscale = ti.tscale;
+                    const int32_t ty = interpolate(ys,
+                            t.shade.it0, t.shade.idtdx, t.shade.idtdy);
+                    if (tscale>=0) {
+                        ti.ydtdy= ty            << tscale;
+                        ti.dtdx = t.shade.idtdx << tscale; 
+                        ti.dtdy = t.shade.idtdy << tscale;
+                    } else {
+                        ti.ydtdy= ty            >> -tscale;
+                        ti.dtdx = t.shade.idtdx >> -tscale; 
+                        ti.dtdy = t.shade.idtdy >> -tscale;
+                    }
+                }
+            }
+            // mirror for generated code...
+            generated_tex_vars_t& gen = c->generated_vars.texture[i];
+            gen.width   = t.surface.width;
+            gen.height  = t.surface.height;
+            gen.stride  = t.surface.stride;
+            gen.data    = int32_t(t.surface.data);
+            gen.dsdx = ti.dsdx;
+            gen.dtdx = ti.dtdx;
+        }
+    }
+
+    // choose the y-stepper
+    c->step_y = step_y__nop;
+    if (enables & GGL_ENABLE_FOG) {
+        c->step_y = step_y__generic;
+    } else if (enables & GGL_ENABLE_TMUS) {
+        if (enables & GGL_ENABLE_SMOOTH) {
+            c->step_y = step_y__generic;
+        } else if (enables & GGL_ENABLE_W) {
+            c->step_y = step_y__w;
+        } else {
+            c->step_y = step_y__tmu;
+        }
+    } else {
+        if (enables & GGL_ENABLE_SMOOTH) {
+            c->step_y = step_y__smooth;
+        }
+    }
+    
+    // choose the rectangle blitter
+    c->rect = rect_generic;
+    if ((c->step_y == step_y__nop) &&
+        (c->scanline == scanline_memcpy))
+    {
+        c->rect = rect_memcpy;
+    }
+}
+
+void init_y_packed(context_t* c, int32_t y0)
+{
+    uint8_t f = c->state.buffers.color.format;
+    c->packed = ggl_pack_color(c, f,
+            c->shade.r0, c->shade.g0, c->shade.b0, c->shade.a0);
+    c->iterators.y = y0;
+    c->step_y = step_y__nop;
+    // choose the rectangle blitter
+    c->rect = rect_generic;
+    if (c->scanline == scanline_memcpy) {
+        c->rect = rect_memcpy;
+    }
+}
+
+void init_y_noop(context_t* c, int32_t y0)
+{
+    c->iterators.y = y0;
+    c->step_y = step_y__nop;
+    // choose the rectangle blitter
+    c->rect = rect_generic;
+    if (c->scanline == scanline_memcpy) {
+        c->rect = rect_memcpy;
+    }
+}
+
+void init_y_error(context_t* c, int32_t y0)
+{
+    // woooops, shoud never happen,
+    // fail gracefully (don't display anything)
+    init_y_noop(c, y0);
+    LOGE("color-buffer has an invalid format!");
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void step_y__generic(context_t* c)
+{
+    const uint32_t enables = c->state.enables;
+
+    // iterate...
+    iterators_t& ci = c->iterators;
+    ci.y += 1;
+                
+    if (enables & GGL_ENABLE_SMOOTH) {
+        ci.ydrdy += c->shade.drdy;
+        ci.ydgdy += c->shade.dgdy;
+        ci.ydbdy += c->shade.dbdy;
+        ci.ydady += c->shade.dady;
+    }
+
+    const uint32_t mask =
+            GGL_ENABLE_DEPTH_TEST |
+            GGL_ENABLE_W |
+            GGL_ENABLE_FOG;
+    if (enables & mask) {
+        ci.ydzdy += c->shade.dzdy;
+        ci.ydwdy += c->shade.dwdy;
+        ci.ydfdy += c->shade.dfdy;
+    }
+
+    if ((enables & GGL_ENABLE_TMUS) && (!(enables & GGL_ENABLE_W))) {
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+            if (c->state.texture[i].enable) {
+                texture_iterators_t& ti = c->state.texture[i].iterators;
+                ti.ydsdy += ti.dsdy;
+                ti.ydtdy += ti.dtdy;
+            }
+        }
+    }
+}
+
+void step_y__nop(context_t* c)
+{
+    c->iterators.y += 1;
+    c->iterators.ydzdy += c->shade.dzdy;
+}
+
+void step_y__smooth(context_t* c)
+{
+    iterators_t& ci = c->iterators;
+    ci.y += 1;
+    ci.ydrdy += c->shade.drdy;
+    ci.ydgdy += c->shade.dgdy;
+    ci.ydbdy += c->shade.dbdy;
+    ci.ydady += c->shade.dady;
+    ci.ydzdy += c->shade.dzdy;
+}
+
+void step_y__w(context_t* c)
+{
+    iterators_t& ci = c->iterators;
+    ci.y += 1;
+    ci.ydzdy += c->shade.dzdy;
+    ci.ydwdy += c->shade.dwdy;
+}
+
+void step_y__tmu(context_t* c)
+{
+    iterators_t& ci = c->iterators;
+    ci.y += 1;
+    ci.ydzdy += c->shade.dzdy;
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+        if (c->state.texture[i].enable) {
+            texture_iterators_t& ti = c->state.texture[i].iterators;
+            ti.ydsdy += ti.dsdy;
+            ti.ydtdy += ti.dtdy;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+void scanline_perspective(context_t* c)
+{
+    struct {
+        union {
+            struct {
+                int32_t s, sq;
+                int32_t t, tq;
+            };
+            struct {
+                int32_t v, q;
+            } st[2];
+        };
+    } tc[GGL_TEXTURE_UNIT_COUNT] __attribute__((aligned(16)));
+
+    // XXX: we should have a special case when dwdx = 0
+
+    // 32 pixels spans works okay. 16 is a lot better,
+    // but hey, it's a software renderer...
+    const uint32_t SPAN_BITS = 5; 
+    const uint32_t ys = c->iterators.y;
+    const uint32_t xs = c->iterators.xl;
+    const uint32_t x1 = c->iterators.xr;
+	const uint32_t xc = x1 - xs;
+    uint32_t remainder = xc & ((1<<SPAN_BITS)-1);
+    uint32_t numSpans = xc >> SPAN_BITS;
+
+    const iterators_t& ci = c->iterators;
+    int32_t w0 = (xs * c->shade.dwdx) + ci.ydwdy;
+    int32_t q0 = gglRecipQ(w0, 30);
+    const int iwscale = 32 - gglClz(q0);
+
+    const int32_t dwdx = c->shade.dwdx << SPAN_BITS;
+    int32_t xl = c->iterators.xl;
+
+    // We process s & t with a loop to reduce the code size
+    // (and i-cache pressure).
+
+    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+        const texture_t& tmu = c->state.texture[i];
+        if (!tmu.enable) continue;
+        int32_t s =   tmu.shade.is0 +
+                     (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) +
+                     ((tmu.shade.idsdx + tmu.shade.idsdy)>>1);
+        int32_t t =   tmu.shade.it0 +
+                     (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) +
+                     ((tmu.shade.idtdx + tmu.shade.idtdy)>>1);
+        tc[i].s  = s;
+        tc[i].t  = t;
+        tc[i].sq = gglMulx(s, q0, iwscale);
+        tc[i].tq = gglMulx(t, q0, iwscale);
+    }
+
+    int32_t span = 0;
+    do {
+        int32_t w1;
+        if (ggl_likely(numSpans)) {
+            w1 = w0 + dwdx;
+        } else {
+            if (remainder) {
+                // finish off the scanline...
+                span = remainder;
+                w1 = (c->shade.dwdx * span) + w0;
+            } else {
+                break;
+            }
+        }
+        int32_t q1 = gglRecipQ(w1, 30);
+        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
+            texture_t& tmu = c->state.texture[i];
+            if (!tmu.enable) continue;
+            texture_iterators_t& ti = tmu.iterators;
+
+            for (int j=0 ; j<2 ; j++) {
+                int32_t v = tc[i].st[j].v;
+                if (span)   v += (tmu.shade.st[j].dx)*span;
+                else        v += (tmu.shade.st[j].dx)<<SPAN_BITS;
+                const int32_t v0 = tc[i].st[j].q;
+                const int32_t v1 = gglMulx(v, q1, iwscale);
+                int32_t dvdx = v1 - v0;
+                if (span)   dvdx /= span;
+                else        dvdx >>= SPAN_BITS;
+                tc[i].st[j].v = v;
+                tc[i].st[j].q = v1;
+
+                const int scale = ti.st[j].scale + (iwscale - 30);
+                if (scale >= 0) {
+                    ti.st[j].ydvdy = v0   << scale;
+                    ti.st[j].dvdx  = dvdx << scale;
+                } else {
+                    ti.st[j].ydvdy = v0   >> -scale;
+                    ti.st[j].dvdx  = dvdx >> -scale;
+                }
+            }
+            generated_tex_vars_t& gen = c->generated_vars.texture[i];
+            gen.dsdx = ti.st[0].dvdx;
+            gen.dtdx = ti.st[1].dvdx;
+        }
+        c->iterators.xl = xl;
+        c->iterators.xr = xl = xl + (span ? span : (1<<SPAN_BITS));
+        w0 = w1;
+        q0 = q1;
+        c->span(c);
+    } while(numSpans--);
+}
+
+void scanline_perspective_single(context_t* c)
+{
+    // 32 pixels spans works okay. 16 is a lot better,
+    // but hey, it's a software renderer...
+    const uint32_t SPAN_BITS = 5; 
+    const uint32_t ys = c->iterators.y;
+    const uint32_t xs = c->iterators.xl;
+    const uint32_t x1 = c->iterators.xr;
+	const uint32_t xc = x1 - xs;
+
+    const iterators_t& ci = c->iterators;
+    int32_t w = (xs * c->shade.dwdx) + ci.ydwdy;
+    int32_t iw = gglRecipQ(w, 30);
+    const int iwscale = 32 - gglClz(iw);
+
+    const int i = 31 - gglClz(c->state.enabled_tmu);
+    generated_tex_vars_t& gen = c->generated_vars.texture[i];
+    texture_t& tmu = c->state.texture[i];
+    texture_iterators_t& ti = tmu.iterators;
+    const int sscale = ti.sscale + (iwscale - 30);
+    const int tscale = ti.tscale + (iwscale - 30);
+    int32_t s =   tmu.shade.is0 +
+                 (tmu.shade.idsdy * ys) + (tmu.shade.idsdx * xs) +
+                 ((tmu.shade.idsdx + tmu.shade.idsdy)>>1);
+    int32_t t =   tmu.shade.it0 +
+                 (tmu.shade.idtdy * ys) + (tmu.shade.idtdx * xs) +
+                 ((tmu.shade.idtdx + tmu.shade.idtdy)>>1);
+    int32_t s0 = gglMulx(s, iw, iwscale);
+    int32_t t0 = gglMulx(t, iw, iwscale);
+    int32_t xl = c->iterators.xl;
+
+    int32_t sq, tq, dsdx, dtdx;
+    int32_t premainder = xc & ((1<<SPAN_BITS)-1);
+    uint32_t numSpans = xc >> SPAN_BITS;
+    if (c->shade.dwdx == 0) {
+        // XXX: we could choose to do this if the error is small enough
+        numSpans = 0;
+        premainder = xc;
+        goto no_perspective;
+    }
+
+    if (premainder) {
+        w += c->shade.dwdx   * premainder;
+        iw = gglRecipQ(w, 30);
+no_perspective:        
+        s += tmu.shade.idsdx * premainder;
+        t += tmu.shade.idtdx * premainder;
+        sq = gglMulx(s, iw, iwscale);
+        tq = gglMulx(t, iw, iwscale);
+        dsdx = (sq - s0) / premainder;
+        dtdx = (tq - t0) / premainder;
+        c->iterators.xl = xl;
+        c->iterators.xr = xl = xl + premainder;
+        goto finish;
+    }
+
+    while (numSpans--) {
+        w += c->shade.dwdx   << SPAN_BITS;
+        s += tmu.shade.idsdx << SPAN_BITS;
+        t += tmu.shade.idtdx << SPAN_BITS;
+        iw = gglRecipQ(w, 30);
+        sq = gglMulx(s, iw, iwscale);
+        tq = gglMulx(t, iw, iwscale);
+        dsdx = (sq - s0) >> SPAN_BITS;
+        dtdx = (tq - t0) >> SPAN_BITS;
+        c->iterators.xl = xl;
+        c->iterators.xr = xl = xl + (1<<SPAN_BITS);
+finish:
+        if (sscale >= 0) {
+            ti.ydsdy = s0   << sscale;
+            ti.dsdx  = dsdx << sscale;
+        } else {
+            ti.ydsdy = s0   >>-sscale;
+            ti.dsdx  = dsdx >>-sscale;
+        }
+        if (tscale >= 0) {
+            ti.ydtdy = t0   << tscale;
+            ti.dtdx  = dtdx << tscale;
+        } else {
+            ti.ydtdy = t0   >>-tscale;
+            ti.dtdx  = dtdx >>-tscale;
+        }
+        s0 = sq;
+        t0 = tq;
+        gen.dsdx = ti.dsdx;
+        gen.dtdx = ti.dtdx;
+        c->span(c);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+void scanline_t32cb16(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;    
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    union {
+        uint16_t* dst;
+        uint32_t* dst32;
+    };
+    dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+
+    surface_t* tex = &(c->state.texture[0].surface);
+    const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+    const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+    uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
+    int sR, sG, sB;
+    uint32_t s, d;
+
+    if (ct==1 || uint32_t(dst)&2) {
+last_one:
+        s = GGL_RGBA_TO_HOST( *src++ );
+        sR = (s >> (   3))&0x1F;
+        sG = (s >> ( 8+2))&0x3F;
+        sB = (s >> (16+3))&0x1F;
+        *dst++ = uint16_t((sR<<11)|(sG<<5)|sB);
+        ct--;
+    }
+
+    while (ct >= 2) {
+        s = GGL_RGBA_TO_HOST( *src++ );
+        sR = (s >> (   3))&0x1F;
+        sG = (s >> ( 8+2))&0x3F;
+        sB = (s >> (16+3))&0x1F;
+        d = (sR<<11)|(sG<<5)|sB;
+        
+        s = GGL_RGBA_TO_HOST( *src++ );
+        sR = (s >> (   3))&0x1F;
+        sG = (s >> ( 8+2))&0x3F;
+        sB = (s >> (16+3))&0x1F;        
+        d |= ((sR<<11)|(sG<<5)|sB)<<16;
+
+#if BYTE_ORDER == BIG_ENDIAN
+        d = (d>>16) | (d<<16);
+#endif
+
+        *dst32++ = d;
+        ct -= 2;
+    }
+    
+    if (ct > 0) {
+        goto last_one;
+    }
+}
+
+void scanline_t32cb16blend(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+
+    surface_t* tex = &(c->state.texture[0].surface);
+    const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+    const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+    uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v));
+
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__))
+    scanline_t32cb16blend_arm(dst, src, ct);
+#else
+    while (ct--) {
+        uint32_t s = *src++;
+        if (!s) {
+            dst++;
+            continue;
+        }
+        uint16_t d = *dst;
+        s = GGL_RGBA_TO_HOST(s);
+        int sR = (s >> (   3))&0x1F;
+        int sG = (s >> ( 8+2))&0x3F;
+        int sB = (s >> (16+3))&0x1F;
+        int sA = (s>>24);
+        int f = 0x100 - (sA + (sA>>7));
+        int dR = (d>>11)&0x1f;
+        int dG = (d>>5)&0x3f;
+        int dB = (d)&0x1f;
+        sR += (f*dR)>>8;
+        sG += (f*dG)>>8;
+        sB += (f*dB)>>8;
+        *dst++ = uint16_t((sR<<11)|(sG<<5)|sB);
+    }
+#endif
+}
+
+void scanline_memcpy(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+                            (x + (cb->stride * y)) * fp->size;
+
+    surface_t* tex = &(c->state.texture[0].surface);
+    const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+    const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+    uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) +
+                            (u + (tex->stride * v)) * fp->size;
+
+    const size_t size = ct * fp->size;
+    memcpy(dst, src, size);
+}
+
+void scanline_memset8(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + (x+(cb->stride*y));
+    uint32_t packed = c->packed;
+    memset(dst, packed, ct);
+}
+
+void scanline_memset16(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    uint16_t* dst = reinterpret_cast<uint16_t*>(cb->data) + (x+(cb->stride*y));
+    uint32_t packed = c->packed;
+    android_memset16(dst, packed, ct*2);
+}
+
+void scanline_memset32(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    uint32_t* dst = reinterpret_cast<uint32_t*>(cb->data) + (x+(cb->stride*y));
+    uint32_t packed = GGL_HOST_TO_RGBA(c->packed);
+    android_memset32(dst, packed, ct*4);
+}
+
+void scanline_clear(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+                            (x + (cb->stride * y)) * fp->size;
+    const size_t size = ct * fp->size;
+    memset(dst, 0, size);
+}
+
+void scanline_set(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+                            (x + (cb->stride * y)) * fp->size;
+    const size_t size = ct * fp->size;
+    memset(dst, 0xFF, size);
+}
+
+void scanline_noop(context_t* c)
+{
+}
+
+void rect_generic(context_t* c, size_t yc)
+{
+    do {
+        c->scanline(c);
+        c->step_y(c);
+    } while (--yc);
+}
+
+void rect_memcpy(context_t* c, size_t yc)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+                            (x + (cb->stride * y)) * fp->size;
+
+    surface_t* tex = &(c->state.texture[0].surface);
+    const int32_t u = (c->state.texture[0].shade.is0>>16) + x;
+    const int32_t v = (c->state.texture[0].shade.it0>>16) + y;
+    uint8_t *src = reinterpret_cast<uint8_t*>(tex->data) +
+                            (u + (tex->stride * v)) * fp->size;
+
+    if (cb->stride == tex->stride && ct == size_t(cb->stride)) {
+        memcpy(dst, src, ct * fp->size * yc);
+    } else {
+        const size_t size = ct * fp->size;
+        const size_t dbpr = cb->stride  * fp->size;
+        const size_t sbpr = tex->stride * fp->size;
+        do {
+            memcpy(dst, src, size);
+            dst += dbpr;
+            src += sbpr;        
+        } while (--yc);
+    }
+}
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+using namespace android;
+extern "C" void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1)
+{
+#if ANDROID_ARM_CODEGEN
+    GGLContext* c;
+    gglInit(&c);
+    needs_t needs;
+    needs.n = n;
+    needs.p = p;
+    needs.t[0] = t0;
+    needs.t[1] = t1;
+    sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE));
+    GGLAssembler assembler( new ARMAssembler(a) );
+    int err = assembler.scanline(needs, (context_t*)c);
+    if (err != 0) {
+        printf("error %08x (%s)\n", err, strerror(-err));
+    }
+    gglUninit(c);
+#else
+    printf("This test runs only on ARM\n");
+#endif
+}
+
diff --git a/libpixelflinger/scanline.h b/libpixelflinger/scanline.h
new file mode 100644
index 0000000..b6f4d37
--- /dev/null
+++ b/libpixelflinger/scanline.h
@@ -0,0 +1,32 @@
+/* libs/pixelflinger/scanline.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_SCANLINE_H
+#define ANDROID_SCANLINE_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_scanline(context_t* c);
+void ggl_uninit_scanline(context_t* c);
+void ggl_pick_scanline(context_t* c);
+
+}; // namespace android
+
+#endif
diff --git a/libpixelflinger/t32cb16blend.S b/libpixelflinger/t32cb16blend.S
new file mode 100644
index 0000000..d4b2579
--- /dev/null
+++ b/libpixelflinger/t32cb16blend.S
@@ -0,0 +1,171 @@
+/* libs/pixelflinger/t32cb16blend.S
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+	.text
+	.align
+	
+	.global scanline_t32cb16blend_arm
+
+// uses r6, r7, lr
+
+.macro pixel,   DREG, SRC, FB, OFFSET
+
+    // SRC = AARRGGBB
+    mov     r7, \SRC, lsr #24           // sA
+    add     r7, r7, r7, lsr #7          // sA + (sA >> 7)
+    rsb     r7, r7, #0x100              // sA = 0x100 - (sA+(sA>>7))
+
+1:
+
+.if \OFFSET
+
+    // red
+    mov     lr, \DREG, lsr #(\OFFSET + 6 + 5)
+    smulbb  lr, r7, lr
+    mov     r6, \SRC, lsr #3
+    and     r6, r6, #0x1F
+    add     lr, r6, lr, lsr #8
+    orr     \FB, lr, lsl #(\OFFSET + 11)
+
+        // green
+        and     r6, \DREG, #(0x3F<<(\OFFSET + 5))
+        smulbt  r6, r7, r6
+        mov     lr, \SRC, lsr #(8+2)
+        and     lr, lr, #0x3F
+        add     r6, lr, r6, lsr #(5+8)
+        orr     \FB, \FB, r6, lsl #(\OFFSET + 5)
+
+            // blue
+            and     lr, \DREG, #(0x1F << \OFFSET)
+            smulbt  lr, r7, lr
+            mov     r6, \SRC, lsr #(8+8+3)
+            and     r6, r6, #0x1F
+            add     lr, r6, lr, lsr #8
+            orr     \FB, \FB, lr, lsl #\OFFSET
+
+.else
+
+    // red
+    mov     lr, \DREG, lsr #(6+5)
+    and     lr, lr, #0x1F
+    smulbb  lr, r7, lr
+    mov     r6, \SRC, lsr #3
+    and     r6, r6, #0x1F
+    add     lr, r6, lr, lsr #8
+    mov     \FB, lr, lsl #11
+
+        // green
+        and     r6, \DREG, #(0x3F<<5)
+        smulbb  r6, r7, r6
+        mov     lr, \SRC, lsr #(8+2)
+        and     lr, lr, #0x3F
+        add     r6, lr, r6, lsr #(5+8)
+        orr     \FB, \FB, r6, lsl #5
+
+            // blue
+            and     lr, \DREG, #0x1F
+            smulbb  lr, r7, lr
+            mov     r6, \SRC, lsr #(8+8+3)
+            and     r6, r6, #0x1F
+            add     lr, r6, lr, lsr #8
+            orr     \FB, \FB, lr
+
+.endif
+
+    .endm
+    
+
+// r0:  dst ptr
+// r1:  src ptr
+// r2:  count
+// r3:  d
+// r4:  s0
+// r5:  s1
+// r6:  pixel
+// r7:  pixel
+// r8:  free
+// r9:  free
+// r10: free
+// r11: free
+// r12: scratch
+// r14: pixel
+
+scanline_t32cb16blend_arm:
+    stmfd	sp!, {r4-r7, lr}
+
+    pld     [r0]
+    pld     [r1]
+
+    // align DST to 32 bits
+    tst     r0, #0x3
+    beq     aligned
+    subs    r2, r2, #1
+    ldmlofd	sp!, {r4-r7, lr}        // return
+    bxlo    lr
+
+last:
+    ldr     r4, [r1], #4
+    ldrh    r3, [r0]
+    pixel   r3, r4, r12, 0
+    strh    r12, [r0], #2
+
+aligned:
+    subs    r2, r2, #2
+    blo     9f
+
+    // The main loop is unrolled twice and process 4 pixels
+8:  ldmia   r1!, {r4, r5}
+    // stream the source
+    pld     [r1, #32]
+    add     r0, r0, #4
+    // it's all zero, skip this pixel
+    orrs    r3, r4, r5
+    beq     7f
+    
+    // load the destination
+    ldr     r3, [r0, #-4]
+    // stream the destination
+    pld     [r0, #32]
+    pixel   r3, r4, r12, 0
+    pixel   r3, r5, r12, 16
+    // effectively, we're getting write-combining by virtue of the
+    // cpu's write-back cache.
+    str     r12, [r0, #-4]
+
+    // 2nd iterration of the loop, don't stream anything
+    subs    r2, r2, #2
+    movlt   r4, r5
+    blt     9f
+    ldmia   r1!, {r4, r5}
+    add     r0, r0, #4
+    orrs    r3, r4, r5
+    beq     7f
+    ldr     r3, [r0, #-4]
+    pixel   r3, r4, r12, 0
+    pixel   r3, r5, r12, 16
+    str     r12, [r0, #-4]
+
+    
+7:  subs    r2, r2, #2
+    bhs     8b
+    mov     r4, r5
+
+9:  adds    r2, r2, #1
+    ldmlofd sp!, {r4-r7, lr}        // return
+    bxlo    lr
+    b       last
diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk
new file mode 100644
index 0000000..6571161
--- /dev/null
+++ b/libpixelflinger/tests/Android.mk
@@ -0,0 +1 @@
+include $(all-subdir-makefiles)
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
new file mode 100644
index 0000000..1bc4214
--- /dev/null
+++ b/libpixelflinger/tests/codegen/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	codegen.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libpixelflinger
+
+LOCAL_MODULE:= test-opengl-codegen
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
new file mode 100644
index 0000000..1865888
--- /dev/null
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdint.h>
+
+extern "C" void ggl_test_codegen(
+        uint32_t n, uint32_t p, uint32_t t0, uint32_t t1);
+
+
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        printf("usage: %s 00000117:03454504_00001501_00000000\n", argv[0]);
+        return 0;
+    }
+    uint32_t n;
+    uint32_t p;
+    uint32_t t0;
+    uint32_t t1;
+    sscanf(argv[1], "%08x:%08x_%08x_%08x", &p, &n, &t0, &t1);
+    ggl_test_codegen(n, p,  t0, t1);
+    return 0;
+}
diff --git a/libpixelflinger/tinyutils/KeyedVector.h b/libpixelflinger/tinyutils/KeyedVector.h
new file mode 100644
index 0000000..1be2094
--- /dev/null
+++ b/libpixelflinger/tinyutils/KeyedVector.h
@@ -0,0 +1,193 @@
+/*
+ *  keyed_vector.h
+ *  Android  
+ *
+ *  Created on 11/18/05.
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_KEYED_VECTOR_H
+#define ANDROID_KEYED_VECTOR_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "tinyutils/SortedVector.h"
+#include "tinyutils/TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+template <typename KEY, typename VALUE>
+class KeyedVector
+{
+public:
+    typedef KEY    key_type;
+    typedef VALUE  value_type;
+
+    inline                  KeyedVector();
+
+    /*
+     * empty the vector
+     */
+
+    inline  void            clear()                     { mVector.clear(); }
+
+    /*! 
+     * vector stats
+     */
+
+    //! returns number of items in the vector
+    inline  size_t          size() const                { return mVector.size(); }
+    //! returns wether or not the vector is empty
+    inline  bool            isEmpty() const             { return mVector.isEmpty(); }
+    //! returns how many items can be stored without reallocating the backing store
+    inline  size_t          capacity() const            { return mVector.capacity(); }
+    //! setst the capacity. capacity can never be reduced less than size()
+    inline ssize_t          setCapacity(size_t size)    { return mVector.setCapacity(size); }
+    
+    /*! 
+     * accessors
+     */
+            const VALUE&    valueFor(const KEY& key) const;
+            const VALUE&    valueAt(size_t index) const;
+            const KEY&      keyAt(size_t index) const;
+            ssize_t         indexOfKey(const KEY& key) const;
+
+    /*!
+     * modifing the array
+     */
+
+            VALUE&          editValueFor(const KEY& key);
+            VALUE&          editValueAt(size_t index);
+
+            /*! 
+             * add/insert/replace items
+             */
+             
+            ssize_t         add(const KEY& key, const VALUE& item);
+            ssize_t         replaceValueFor(const KEY& key, const VALUE& item);
+            ssize_t         replaceValueAt(size_t index, const VALUE& item);
+
+    /*!
+     * remove items
+     */
+
+            ssize_t         removeItem(const KEY& key);
+            ssize_t         removeItemsAt(size_t index, size_t count = 1);
+            
+private:
+            SortedVector< key_value_pair_t<KEY, VALUE> >    mVector;
+};
+
+// ---------------------------------------------------------------------------
+
+/**
+ * Variation of KeyedVector that holds a default value to return when
+ * valueFor() is called with a key that doesn't exist.
+ */
+template <typename KEY, typename VALUE>
+class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
+{
+public:
+    inline                  DefaultKeyedVector(const VALUE& defValue = VALUE());
+            const VALUE&    valueFor(const KEY& key) const;
+
+private:
+            VALUE                                           mDefault;
+};
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+KeyedVector<KEY,VALUE>::KeyedVector()
+{
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
+    return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+    ssize_t i = indexOfKey(key);
+    assert(i>=0);
+    return mVector.itemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
+    return mVector.itemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
+    return mVector.itemAt(index).key;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
+    ssize_t i = indexOfKey(key);
+    assert(i>=0);
+    return mVector.editItemAt(i).value;
+}
+
+template<typename KEY, typename VALUE> inline
+VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
+    return mVector.editItemAt(index).value;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
+    return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
+    key_value_pair_t<KEY,VALUE> pair(key, value);
+    mVector.remove(pair);
+    return mVector.add(pair);
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
+    if (index<size()) {
+        mVector.editValueAt(index).value = item;
+        return index;
+    }
+    return BAD_INDEX;
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
+    return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
+}
+
+template<typename KEY, typename VALUE> inline
+ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
+    return mVector.removeItemsAt(index, count);
+}
+
+// ---------------------------------------------------------------------------
+
+template<typename KEY, typename VALUE> inline
+DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
+    : mDefault(defValue)
+{
+}
+
+template<typename KEY, typename VALUE> inline
+const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
+    ssize_t i = indexOfKey(key);
+    return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_KEYED_VECTOR_H
diff --git a/libpixelflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/tinyutils/SharedBuffer.cpp
new file mode 100644
index 0000000..ef781a7
--- /dev/null
+++ b/libpixelflinger/tinyutils/SharedBuffer.cpp
@@ -0,0 +1,106 @@
+/*
+ *  SharedBuffer.cpp
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/atomic.h>
+
+#include "tinyutils/SharedBuffer.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+SharedBuffer* SharedBuffer::alloc(size_t size)
+{
+    SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
+    if (sb) {
+        sb->mRefs = 1;
+        sb->mSize = size;
+    }
+    return sb;
+}
+
+
+ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
+{
+    if (released->mRefs != 0) return -1; // XXX: invalid operation
+    free(const_cast<SharedBuffer*>(released));
+    return 0;
+}
+
+SharedBuffer* SharedBuffer::edit() const
+{
+    if (onlyOwner()) {
+        return const_cast<SharedBuffer*>(this);
+    }
+    SharedBuffer* sb = alloc(mSize);
+    if (sb) {
+        memcpy(sb->data(), data(), size());
+        release();
+    }
+    return sb;    
+}
+
+SharedBuffer* SharedBuffer::editResize(size_t newSize) const
+{
+    if (onlyOwner()) {
+        SharedBuffer* buf = const_cast<SharedBuffer*>(this);
+        if (buf->mSize == newSize) return buf;
+        buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
+        if (buf != NULL) {
+            buf->mSize = newSize;
+            return buf;
+        }
+    }
+    SharedBuffer* sb = alloc(newSize);
+    if (sb) {
+        const size_t mySize = mSize;
+        memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
+        release();
+    }
+    return sb;    
+}
+
+SharedBuffer* SharedBuffer::attemptEdit() const
+{
+    if (onlyOwner()) {
+        return const_cast<SharedBuffer*>(this);
+    }
+    return 0;
+}
+
+SharedBuffer* SharedBuffer::reset(size_t new_size) const
+{
+    // cheap-o-reset.
+    SharedBuffer* sb = alloc(new_size);
+    if (sb) {
+        release();
+    }
+    return sb;
+}
+
+void SharedBuffer::acquire() const {
+    android_atomic_inc(&mRefs);
+}
+
+int32_t SharedBuffer::release(uint32_t flags) const
+{
+    int32_t prev = 1;
+    if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
+        mRefs = 0;
+        if ((flags & eKeepStorage) == 0) {
+            free(const_cast<SharedBuffer*>(this));
+        }
+    }
+    return prev;
+}
+
+
+}; // namespace android
diff --git a/libpixelflinger/tinyutils/SharedBuffer.h b/libpixelflinger/tinyutils/SharedBuffer.h
new file mode 100644
index 0000000..9f63121
--- /dev/null
+++ b/libpixelflinger/tinyutils/SharedBuffer.h
@@ -0,0 +1,138 @@
+/*
+ *  SharedBuffer.h
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_SHARED_BUFFER_H
+#define ANDROID_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class SharedBuffer
+{
+public:
+
+    /* flags to use with release() */
+    enum {
+        eKeepStorage = 0x00000001
+    };
+
+    /*! allocate a buffer of size 'size' and acquire() it.
+     *  call release() to free it.
+     */
+    static          SharedBuffer*           alloc(size_t size);
+    
+    /*! free the memory associated with the SharedBuffer.
+     * Fails if there are any users associated with this SharedBuffer.
+     * In other words, the buffer must have been release by all its
+     * users.
+     */
+    static          ssize_t                 dealloc(const SharedBuffer* released);
+    
+    //! get the SharedBuffer from the data pointer
+    static  inline  const SharedBuffer*     sharedBuffer(const void* data);
+
+    //! access the data for read
+    inline          const void*             data() const;
+    
+    //! access the data for read/write
+    inline          void*                   data();
+
+    //! get size of the buffer
+    inline          size_t                  size() const;
+ 
+    //! get back a SharedBuffer object from its data
+    static  inline  SharedBuffer*           bufferFromData(void* data);
+    
+    //! get back a SharedBuffer object from its data
+    static  inline  const SharedBuffer*     bufferFromData(const void* data);
+
+    //! get the size of a SharedBuffer object from its data
+    static  inline  size_t                  sizeFromData(const void* data);
+    
+    //! edit the buffer (get a writtable, or non-const, version of it)
+                    SharedBuffer*           edit() const;
+
+    //! edit the buffer, resizing if needed
+                    SharedBuffer*           editResize(size_t size) const;
+
+    //! like edit() but fails if a copy is required
+                    SharedBuffer*           attemptEdit() const;
+    
+    //! resize and edit the buffer, loose it's content.
+                    SharedBuffer*           reset(size_t size) const;
+
+    //! acquire/release a reference on this buffer
+                    void                    acquire() const;
+                    
+    /*! release a reference on this buffer, with the option of not
+     * freeing the memory associated with it if it was the last reference
+     * returns the previous reference count
+     */     
+                    int32_t                 release(uint32_t flags = 0) const;
+    
+    //! returns wether or not we're the only owner
+    inline          bool                    onlyOwner() const;
+    
+
+private:
+        inline SharedBuffer() { }
+        inline ~SharedBuffer() { }
+        inline SharedBuffer(const SharedBuffer&);
+ 
+        // 16 bytes. must be sized to preserve correct alingment.
+        mutable int32_t        mRefs;
+                size_t         mSize;
+                uint32_t       mReserved[2];
+};
+
+// ---------------------------------------------------------------------------
+
+const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
+    return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
+}
+
+const void* SharedBuffer::data() const {
+    return this + 1;
+}
+
+void* SharedBuffer::data() {
+    return this + 1;
+}
+
+size_t SharedBuffer::size() const {
+    return mSize;
+}
+
+SharedBuffer* SharedBuffer::bufferFromData(void* data)
+{
+    return ((SharedBuffer*)data)-1;
+}
+    
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
+{
+    return ((const SharedBuffer*)data)-1;
+}
+
+size_t SharedBuffer::sizeFromData(const void* data)
+{
+    return (((const SharedBuffer*)data)-1)->mSize;
+}
+
+bool SharedBuffer::onlyOwner() const {
+    return (mRefs == 1);
+}
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/libpixelflinger/tinyutils/TypeHelpers.h b/libpixelflinger/tinyutils/TypeHelpers.h
new file mode 100644
index 0000000..9500c90
--- /dev/null
+++ b/libpixelflinger/tinyutils/TypeHelpers.h
@@ -0,0 +1,245 @@
+/*
+ *  TypeHelpers.h
+ *  
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_TYPE_HELPERS_H
+#define ANDROID_TYPE_HELPERS_H
+
+#include <new>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*
+ * Types traits
+ */
+    
+template <typename T> struct trait_trivial_ctor  { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor  { enum { value = false }; };
+template <typename T> struct trait_trivial_copy  { enum { value = false }; };
+template <typename T> struct trait_trivial_assign{ enum { value = false }; };
+
+template <typename T> struct trait_pointer     { enum { value = false }; };    
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+#define ANDROID_BASIC_TYPES_TRAITS( T )                                       \
+    template<> struct trait_trivial_ctor< T >  { enum { value = true }; };    \
+    template<> struct trait_trivial_dtor< T >  { enum { value = true }; };    \
+    template<> struct trait_trivial_copy< T >  { enum { value = true }; };    \
+    template<> struct trait_trivial_assign< T >{ enum { value = true }; }; 
+
+#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign )                    \
+    template<> struct trait_trivial_ctor< T >  { enum { value = ctor }; };    \
+    template<> struct trait_trivial_dtor< T >  { enum { value = dtor }; };    \
+    template<> struct trait_trivial_copy< T >  { enum { value = copy }; };    \
+    template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; 
+
+template <typename TYPE>
+struct traits {
+    enum {
+        is_pointer          = trait_pointer<TYPE>::value,
+        has_trivial_ctor    = is_pointer || trait_trivial_ctor<TYPE>::value,
+        has_trivial_dtor    = is_pointer || trait_trivial_dtor<TYPE>::value,
+        has_trivial_copy    = is_pointer || trait_trivial_copy<TYPE>::value,
+        has_trivial_assign  = is_pointer || trait_trivial_assign<TYPE>::value   
+    };
+};
+
+template <typename T, typename U>
+struct aggregate_traits {
+    enum {
+        is_pointer          = false,
+        has_trivial_ctor    = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+        has_trivial_dtor    = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+        has_trivial_copy    = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+        has_trivial_assign  = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
+    };
+};
+
+// ---------------------------------------------------------------------------
+
+/*
+ * basic types traits
+ */
+ 
+ANDROID_BASIC_TYPES_TRAITS( void );
+ANDROID_BASIC_TYPES_TRAITS( bool );
+ANDROID_BASIC_TYPES_TRAITS( char );
+ANDROID_BASIC_TYPES_TRAITS( unsigned char );
+ANDROID_BASIC_TYPES_TRAITS( short );
+ANDROID_BASIC_TYPES_TRAITS( unsigned short );
+ANDROID_BASIC_TYPES_TRAITS( int );
+ANDROID_BASIC_TYPES_TRAITS( unsigned int );
+ANDROID_BASIC_TYPES_TRAITS( long );
+ANDROID_BASIC_TYPES_TRAITS( unsigned long );
+ANDROID_BASIC_TYPES_TRAITS( long long );
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
+ANDROID_BASIC_TYPES_TRAITS( float );
+ANDROID_BASIC_TYPES_TRAITS( double );
+
+// ---------------------------------------------------------------------------
+
+    
+/*
+ * compare and order types
+ */
+
+template<typename TYPE> inline
+int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
+    return (lhs < rhs) ? 1 : 0;
+}
+
+template<typename TYPE> inline
+int compare_type(const TYPE& lhs, const TYPE& rhs) {
+    return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
+}
+
+/*
+ * create, destroy, copy and assign types...
+ */
+ 
+template<typename TYPE> inline
+void construct_type(TYPE* p, size_t n) {
+    if (!traits<TYPE>::has_trivial_ctor) {
+        while (n--) {
+            new(p++) TYPE;
+        }
+    }
+}
+
+template<typename TYPE> inline
+void destroy_type(TYPE* p, size_t n) {
+    if (!traits<TYPE>::has_trivial_dtor) {
+        while (n--) {
+            p->~TYPE();
+            p++;
+        }
+    }
+}
+
+template<typename TYPE> inline
+void copy_type(TYPE* d, const TYPE* s, size_t n) {
+    if (!traits<TYPE>::has_trivial_copy) {
+        while (n--) {
+            new(d) TYPE(*s);
+            d++, s++;
+        }
+    } else {
+        memcpy(d,s,n*sizeof(TYPE));
+    }
+}
+
+template<typename TYPE> inline
+void assign_type(TYPE* d, const TYPE* s, size_t n) {
+    if (!traits<TYPE>::has_trivial_assign) {
+        while (n--) {
+            *d++ = *s++;
+        }
+    } else {
+        memcpy(d,s,n*sizeof(TYPE));
+    }
+}
+
+template<typename TYPE> inline
+void splat_type(TYPE* where, const TYPE* what, size_t n) {
+    if (!traits<TYPE>::has_trivial_copy) {
+        while (n--) {
+            new(where) TYPE(*what);
+            where++;
+        }
+    } else {
+         while (n--) {
+             *where++ = *what;
+        }
+    }
+}
+
+template<typename TYPE> inline
+void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+    if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+        d += n;
+        s += n;
+        while (n--) {
+            --d, --s;
+            if (!traits<TYPE>::has_trivial_copy) {
+                new(d) TYPE(*s);
+            } else {
+                *d = *s;
+            }
+            if (!traits<TYPE>::has_trivial_dtor) {
+                s->~TYPE();
+            }
+        }
+    } else {
+        memmove(d,s,n*sizeof(TYPE));
+    }
+}
+
+template<typename TYPE> inline
+void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
+    if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+        while (n--) {
+            if (!traits<TYPE>::has_trivial_copy) {
+                new(d) TYPE(*s);
+            } else {
+                *d = *s;
+            }
+            if (!traits<TYPE>::has_trivial_dtor) {
+                s->~TYPE();
+            }
+            d++, s++;
+        }
+    } else {
+        memmove(d,s,n*sizeof(TYPE));
+    }
+}
+// ---------------------------------------------------------------------------
+
+/*
+ * a key/value pair
+ */
+
+template <typename KEY, typename VALUE>
+struct key_value_pair_t {
+    KEY     key;
+    VALUE   value;
+    key_value_pair_t() { }
+    key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
+    key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v)  { }
+    key_value_pair_t(const KEY& k) : key(k) { }
+    inline bool operator < (const key_value_pair_t& o) const {
+        return strictly_order_type(key, o.key);
+    }
+};
+
+template<>
+template <typename K, typename V>
+struct trait_trivial_ctor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
+template<> 
+template <typename K, typename V>
+struct trait_trivial_dtor< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
+template<> 
+template <typename K, typename V>
+struct trait_trivial_copy< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
+template<> 
+template <typename K, typename V>
+struct trait_trivial_assign< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_TYPE_HELPERS_H
diff --git a/libpixelflinger/tinyutils/Vector.h b/libpixelflinger/tinyutils/Vector.h
new file mode 100644
index 0000000..182bc7b
--- /dev/null
+++ b/libpixelflinger/tinyutils/Vector.h
@@ -0,0 +1,352 @@
+/*
+ *  vector.h
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_VECTOR_H
+#define ANDROID_VECTOR_H
+
+#include <new>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/log.h>
+
+#include "tinyutils/VectorImpl.h"
+#include "tinyutils/TypeHelpers.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*!
+ * The main templated vector class ensuring type safety
+ * while making use of VectorImpl.
+ * This is the class users want to use.
+ */
+
+template <class TYPE>
+class Vector : private VectorImpl
+{
+public:
+            typedef TYPE    value_type;
+    
+    /*! 
+     * Constructors and destructors
+     */
+    
+                            Vector();
+                            Vector(const Vector<TYPE>& rhs);
+    virtual                 ~Vector();
+
+    /*! copy operator */
+            const Vector<TYPE>&     operator = (const Vector<TYPE>& rhs) const;
+            Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);    
+
+    /*
+     * empty the vector
+     */
+
+    inline  void            clear()             { VectorImpl::clear(); }
+
+    /*! 
+     * vector stats
+     */
+
+    //! returns number of items in the vector
+    inline  size_t          size() const                { return VectorImpl::size(); }
+    //! returns wether or not the vector is empty
+    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }
+    //! returns how many items can be stored without reallocating the backing store
+    inline  size_t          capacity() const            { return VectorImpl::capacity(); }
+    //! setst the capacity. capacity can never be reduced less than size()
+    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }
+
+    /*! 
+     * C-style array access
+     */
+     
+    //! read-only C-style access 
+    inline  const TYPE*     array() const;
+    //! read-write C-style access
+            TYPE*           editArray();
+    
+    /*! 
+     * accessors
+     */
+
+    //! read-only access to an item at a given index
+    inline  const TYPE&     operator [] (size_t index) const;
+    //! alternate name for operator []
+    inline  const TYPE&     itemAt(size_t index) const;
+    //! stack-usage of the vector. returns the top of the stack (last element)
+            const TYPE&     top() const;
+    //! same as operator [], but allows to access the vector backward (from the end) with a negative index
+            const TYPE&     mirrorItemAt(ssize_t index) const;
+
+    /*!
+     * modifing the array
+     */
+
+    //! copy-on write support, grants write access to an item
+            TYPE&           editItemAt(size_t index);
+    //! grants right acces to the top of the stack (last element)
+            TYPE&           editTop();
+
+            /*! 
+             * append/insert another vector
+             */
+            
+    //! insert another vector at a given index
+            ssize_t         insertVectorAt(const Vector<TYPE>& vector, size_t index);
+
+    //! append another vector at the end of this one
+            ssize_t         appendVector(const Vector<TYPE>& vector);
+
+
+            /*! 
+             * add/insert/replace items
+             */
+             
+    //! insert one or several items initialized with their default constructor
+    inline  ssize_t         insertAt(size_t index, size_t numItems = 1);
+    //! insert on onr several items initialized from a prototype item
+            ssize_t         insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
+    //! pop the top of the stack (removes the last element). No-op if the stack's empty
+    inline  void            pop();
+    //! pushes an item initialized with its default constructor
+    inline  void            push();
+    //! pushes an item on the top of the stack
+            void            push(const TYPE& item);
+    //! same as push() but returns the index the item was added at (or an error)
+    inline  ssize_t         add();
+    //! same as push() but returns the index the item was added at (or an error)
+            ssize_t         add(const TYPE& item);            
+    //! replace an item with a new one initialized with its default constructor
+    inline  ssize_t         replaceAt(size_t index);
+    //! replace an item with a new one
+            ssize_t         replaceAt(const TYPE& item, size_t index);
+
+    /*!
+     * remove items
+     */
+
+    //! remove several items
+    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);
+    //! remove one item
+    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }
+
+    /*!
+     * sort (stable) the array
+     */
+     
+     typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
+     typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
+     
+     inline status_t        sort(compar_t cmp);
+     inline status_t        sort(compar_r_t cmp, void* state);
+
+protected:
+    virtual void    do_construct(void* storage, size_t num) const;
+    virtual void    do_destroy(void* storage, size_t num) const;
+    virtual void    do_copy(void* dest, const void* from, size_t num) const;
+    virtual void    do_splat(void* dest, const void* item, size_t num) const;
+    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;
+    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;
+};
+
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts from here...
+// ---------------------------------------------------------------------------
+
+template<class TYPE> inline
+Vector<TYPE>::Vector()
+    : VectorImpl(sizeof(TYPE),
+                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)
+                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)
+                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0)
+                |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+                )
+{
+}
+
+template<class TYPE> inline
+Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
+    : VectorImpl(rhs) {
+}
+
+template<class TYPE> inline
+Vector<TYPE>::~Vector() {
+    finish_vector();
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
+    VectorImpl::operator = (rhs);
+    return *this; 
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+    VectorImpl::operator = (rhs);
+    return *this; 
+}
+
+template<class TYPE> inline
+const TYPE* Vector<TYPE>::array() const {
+    return static_cast<const TYPE *>(arrayImpl());
+}
+
+template<class TYPE> inline
+TYPE* Vector<TYPE>::editArray() {
+    return static_cast<TYPE *>(editArrayImpl());
+}
+
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::operator[](size_t index) const {
+    LOG_FATAL_IF( index>=size(),
+                  "itemAt: index %d is past size %d", (int)index, (int)size() );
+    return *(array() + index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::itemAt(size_t index) const {
+    return operator[](index);
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
+    LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
+                  "mirrorItemAt: index %d is past size %d",
+                  (int)index, (int)size() );
+    return *(array() + ((index<0) ? (size()-index) : index));
+}
+
+template<class TYPE> inline
+const TYPE& Vector<TYPE>::top() const {
+    return *(array() + size() - 1);
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editItemAt(size_t index) {
+    return *( static_cast<TYPE *>(editItemLocation(index)) );
+}
+
+template<class TYPE> inline
+TYPE& Vector<TYPE>::editTop() {
+    return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
+    return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
+    return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
+    return VectorImpl::insertAt(&item, index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push(const TYPE& item) {
+    return VectorImpl::push(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add(const TYPE& item) {
+    return VectorImpl::add(&item);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
+    return VectorImpl::replaceAt(&item, index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
+    return VectorImpl::insertAt(index, numItems);
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::pop() {
+    VectorImpl::pop();
+}
+
+template<class TYPE> inline
+void Vector<TYPE>::push() {
+    VectorImpl::push();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::add() {
+    return VectorImpl::add();
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::replaceAt(size_t index) {
+    return VectorImpl::replaceAt(index);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
+    return VectorImpl::removeItemsAt(index, count);
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {
+    return VectorImpl::sort((VectorImpl::compar_t)cmp);
+}
+
+template<class TYPE> inline
+status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {
+    return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state);
+}
+
+// ---------------------------------------------------------------------------
+
+template<class TYPE>
+void Vector<TYPE>::do_construct(void* storage, size_t num) const {
+    construct_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
+    destroy_type( reinterpret_cast<TYPE*>(storage), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+template<class TYPE>
+void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
+}
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_H
diff --git a/libpixelflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/tinyutils/VectorImpl.cpp
new file mode 100644
index 0000000..a049706
--- /dev/null
+++ b/libpixelflinger/tinyutils/VectorImpl.cpp
@@ -0,0 +1,552 @@
+/*
+ *  vector_impl.cpp
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#define LOG_TAG "Vector"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include "tinyutils/SharedBuffer.h"
+#include "tinyutils/VectorImpl.h"
+
+/*****************************************************************************/
+
+
+namespace android {
+
+enum {
+    NO_ERROR          = 0,    // No errors.
+    NO_MEMORY           = -ENOMEM,
+    BAD_VALUE           = -EINVAL,
+    BAD_INDEX           = -EOVERFLOW,
+    NAME_NOT_FOUND      = -ENOENT,
+};
+
+// ----------------------------------------------------------------------------
+
+const size_t kMinVectorCapacity = 4;
+
+static inline size_t max(size_t a, size_t b) {
+    return a>b ? a : b;
+}
+
+// ----------------------------------------------------------------------------
+
+VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
+    : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
+{
+}
+
+VectorImpl::VectorImpl(const VectorImpl& rhs)
+    :   mStorage(rhs.mStorage), mCount(rhs.mCount),
+        mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
+{
+    if (mStorage) {
+        SharedBuffer::sharedBuffer(mStorage)->acquire();
+    }
+}
+
+VectorImpl::~VectorImpl()
+{
+    LOG_ASSERT(!mCount,
+        "[%p] "
+        "subclasses of VectorImpl must call finish_vector()"
+        " in their destructor. Leaking %d bytes.",
+        this, (int)(mCount*mItemSize));
+    // We can't call _do_destroy() here because the vtable is already gone. 
+}
+
+VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
+{
+    LOG_ASSERT(mItemSize == rhs.mItemSize,
+        "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
+    if (this != &rhs) {
+        release_storage();
+        if (rhs.mCount) {
+            mStorage = rhs.mStorage;
+            mCount = rhs.mCount;
+            SharedBuffer::sharedBuffer(mStorage)->acquire();
+        } else {
+            mStorage = 0;
+            mCount = 0;
+        }
+    }
+    return *this;
+}
+
+void* VectorImpl::editArrayImpl()
+{
+    if (mStorage) {
+        SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
+        if (sb == 0) {
+            sb = SharedBuffer::alloc(capacity() * mItemSize);
+            if (sb) {
+                _do_copy(sb->data(), mStorage, mCount);
+                release_storage();
+                mStorage = sb->data();
+            }
+        }
+    }
+    return mStorage;
+}
+
+size_t VectorImpl::capacity() const
+{
+    if (mStorage) {
+        return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
+    }
+    return 0;
+}
+
+ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
+{
+    if (index > size())
+        return BAD_INDEX;
+    void* where = _grow(index, vector.size());
+    if (where) {
+        _do_copy(where, vector.arrayImpl(), vector.size());
+    }
+    return where ? index : (ssize_t)NO_MEMORY;
+}
+
+ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+{
+    return insertVectorAt(vector, size());
+}
+
+ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
+{
+    return insertAt(0, index, numItems);
+}
+
+ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
+{
+    if (index > size())
+        return BAD_INDEX;
+    void* where = _grow(index, numItems);
+    if (where) {
+        if (item) {
+            _do_splat(where, item, numItems);
+        } else {
+            _do_construct(where, numItems);
+        }
+    }
+    return where ? index : (ssize_t)NO_MEMORY;
+}
+
+void VectorImpl::pop()
+{
+    if (size())
+        removeItemsAt(size()-1, 1);
+}
+
+void VectorImpl::push()
+{
+    push(0);
+}
+
+void VectorImpl::push(const void* item)
+{
+    insertAt(item, size());
+}
+
+ssize_t VectorImpl::add()
+{
+    return add(0);
+}
+
+ssize_t VectorImpl::add(const void* item)
+{
+    return insertAt(item, size());
+}
+
+ssize_t VectorImpl::replaceAt(size_t index)
+{
+    return replaceAt(0, index);
+}
+
+ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
+{
+    LOG_ASSERT(index<size(),
+        "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
+
+    void* item = editItemLocation(index);
+    if (item == 0)
+        return NO_MEMORY;
+    _do_destroy(item, 1);
+    if (prototype == 0) {
+        _do_construct(item, 1);
+    } else {
+        _do_copy(item, prototype, 1);
+    }
+    return ssize_t(index);
+}
+
+ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
+{
+    LOG_ASSERT((index+count)<=size(),
+        "[%p] remove: index=%d, count=%d, size=%d",
+               this, (int)index, (int)count, (int)size());
+
+    if ((index+count) > size())
+        return BAD_VALUE;
+   _shrink(index, count);
+   return index;
+}
+
+void VectorImpl::finish_vector()
+{
+    release_storage();
+    mStorage = 0;
+    mCount = 0;
+}
+
+void VectorImpl::clear()
+{
+    _shrink(0, mCount);
+}
+
+void* VectorImpl::editItemLocation(size_t index)
+{
+    LOG_ASSERT(index<capacity(),
+        "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+        this, (int)index, (int)capacity(), (int)mCount);
+            
+    void* buffer = editArrayImpl();
+    if (buffer)
+        return reinterpret_cast<char*>(buffer) + index*mItemSize;
+    return 0;
+}
+
+const void* VectorImpl::itemLocation(size_t index) const
+{
+    LOG_ASSERT(index<capacity(),
+        "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+        this, (int)index, (int)capacity(), (int)mCount);
+
+    const  void* buffer = arrayImpl();
+    if (buffer)
+        return reinterpret_cast<const char*>(buffer) + index*mItemSize;
+    return 0;
+}
+
+ssize_t VectorImpl::setCapacity(size_t new_capacity)
+{
+    size_t current_capacity = capacity();
+    ssize_t amount = new_capacity - size();
+    if (amount <= 0) {
+        // we can't reduce the capacity
+        return current_capacity;
+    } 
+    SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+    if (sb) {
+        void* array = sb->data();
+        _do_copy(array, mStorage, size());
+        release_storage();
+        mStorage = const_cast<void*>(array);
+    } else {
+        return NO_MEMORY;
+    }
+    return new_capacity;
+}
+
+void VectorImpl::release_storage()
+{
+    if (mStorage) {
+        const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
+        if (sb->release(SharedBuffer::eKeepStorage) == 1) {
+            _do_destroy(mStorage, mCount);
+            SharedBuffer::dealloc(sb);
+        } 
+    }
+}
+
+void* VectorImpl::_grow(size_t where, size_t amount)
+{
+//    LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+//        this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+    if (where > mCount)
+        where = mCount;
+      
+    const size_t new_size = mCount + amount;
+    if (capacity() < new_size) {
+        const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+//        LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+        if ((mStorage) &&
+            (mCount==where) &&
+            (mFlags & HAS_TRIVIAL_COPY) &&
+            (mFlags & HAS_TRIVIAL_DTOR))
+        {
+            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+            mStorage = sb->data();
+        } else {
+            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+            if (sb) {
+                void* array = sb->data();
+                if (where>0) {
+                    _do_copy(array, mStorage, where);
+                }
+                if (mCount>where) {
+                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
+                    void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+                    _do_copy(dest, from, mCount-where);
+                }
+                release_storage();
+                mStorage = const_cast<void*>(array);
+            }
+        }
+    } else {
+        ssize_t s = mCount-where;
+        if (s>0) {
+            void* array = editArrayImpl();    
+            void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+            const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
+            _do_move_forward(to, from, s);
+        }
+    }
+    mCount += amount;
+    void* free_space = const_cast<void*>(itemLocation(where));
+    return free_space;
+}
+
+void VectorImpl::_shrink(size_t where, size_t amount)
+{
+    if (!mStorage)
+        return;
+
+//    LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
+//        this, (int)where, (int)amount, (int)mCount, (int)capacity());
+
+    if (where >= mCount)
+        where = mCount - amount;
+
+    const size_t new_size = mCount - amount;
+    if (new_size*3 < capacity()) {
+        const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
+//        LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+        if ((where == mCount-amount) &&
+            (mFlags & HAS_TRIVIAL_COPY) &&
+            (mFlags & HAS_TRIVIAL_DTOR))
+        {
+            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+            mStorage = sb->data();
+        } else {
+            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+            if (sb) {
+                void* array = sb->data();
+                if (where>0) {
+                    _do_copy(array, mStorage, where);
+                }
+                if (mCount > where+amount) {
+                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
+                    void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+                    _do_copy(dest, from, mCount-(where+amount));
+                }
+                release_storage();
+                mStorage = const_cast<void*>(array);
+            }
+        }
+    } else {
+        void* array = editArrayImpl();    
+        void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
+        _do_destroy(to, amount);
+        ssize_t s = mCount-(where+amount);
+        if (s>0) {
+            const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+            _do_move_backward(to, from, s);
+        }
+    }
+
+    // adjust the number of items...
+    mCount -= amount;
+}
+
+size_t VectorImpl::itemSize() const {
+    return mItemSize;
+}
+
+void VectorImpl::_do_construct(void* storage, size_t num) const
+{
+    if (!(mFlags & HAS_TRIVIAL_CTOR)) {
+        do_construct(storage, num);
+    }
+}
+
+void VectorImpl::_do_destroy(void* storage, size_t num) const
+{
+    if (!(mFlags & HAS_TRIVIAL_DTOR)) {
+        do_destroy(storage, num);
+    }
+}
+
+void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
+{
+    if (!(mFlags & HAS_TRIVIAL_COPY)) {
+        do_copy(dest, from, num);
+    } else {
+        memcpy(dest, from, num*itemSize());
+    }
+}
+
+void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
+    do_splat(dest, item, num);
+}
+
+void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
+    do_move_forward(dest, from, num);
+}
+
+void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
+    do_move_backward(dest, from, num);
+}
+
+void VectorImpl::reservedVectorImpl1() { }
+void VectorImpl::reservedVectorImpl2() { }
+void VectorImpl::reservedVectorImpl3() { }
+void VectorImpl::reservedVectorImpl4() { }
+void VectorImpl::reservedVectorImpl5() { }
+void VectorImpl::reservedVectorImpl6() { }
+void VectorImpl::reservedVectorImpl7() { }
+void VectorImpl::reservedVectorImpl8() { }
+
+/*****************************************************************************/
+
+SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
+    : VectorImpl(itemSize, flags)
+{
+}
+
+SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
+: VectorImpl(rhs)
+{
+}
+
+SortedVectorImpl::~SortedVectorImpl()
+{
+}
+
+SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
+{
+    return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
+}
+
+ssize_t SortedVectorImpl::indexOf(const void* item) const
+{
+    return _indexOrderOf(item);
+}
+
+size_t SortedVectorImpl::orderOf(const void* item) const
+{
+    size_t o;
+    _indexOrderOf(item, &o);
+    return o;
+}
+
+ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
+{
+    // binary search
+    ssize_t err = NAME_NOT_FOUND;
+    ssize_t l = 0;
+    ssize_t h = size()-1;
+    ssize_t mid;
+    const void* a = arrayImpl();
+    const size_t s = itemSize();
+    while (l <= h) {
+        mid = l + (h - l)/2;
+        const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
+        const int c = do_compare(curr, item);
+        if (c == 0) {
+            err = l = mid;
+            break;
+        } else if (c < 0) {
+            l = mid + 1;
+        } else {
+            h = mid - 1;
+        }
+    }
+    if (order) *order = l;
+    return err;
+}
+
+ssize_t SortedVectorImpl::add(const void* item)
+{
+    size_t order;
+    ssize_t index = _indexOrderOf(item, &order);
+    if (index < 0) {
+        index = VectorImpl::insertAt(item, order, 1);
+    } else {
+        index = VectorImpl::replaceAt(item, index);
+    }
+    return index;
+}
+
+ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
+{
+    // naive merge...
+    if (!vector.isEmpty()) {
+        const void* buffer = vector.arrayImpl();
+        const size_t is = itemSize();
+        size_t s = vector.size();
+        for (size_t i=0 ; i<s ; i++) {
+            ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
+            if (err<0) {
+                return err;
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
+{
+    // we've merging a sorted vector... nice!
+    ssize_t err = NO_ERROR;
+    if (!vector.isEmpty()) {
+        // first take care of the case where the vectors are sorted together
+        if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
+            err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
+        } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
+            err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
+        } else {
+            // this could be made a little better
+            err = merge(static_cast<const VectorImpl&>(vector));
+        }
+    }
+    return err;
+}
+
+ssize_t SortedVectorImpl::remove(const void* item)
+{
+    ssize_t i = indexOf(item);
+    if (i>=0) {
+        VectorImpl::removeItemsAt(i, 1);
+    }
+    return i;
+}
+
+void SortedVectorImpl::reservedSortedVectorImpl1() { };
+void SortedVectorImpl::reservedSortedVectorImpl2() { };
+void SortedVectorImpl::reservedSortedVectorImpl3() { };
+void SortedVectorImpl::reservedSortedVectorImpl4() { };
+void SortedVectorImpl::reservedSortedVectorImpl5() { };
+void SortedVectorImpl::reservedSortedVectorImpl6() { };
+void SortedVectorImpl::reservedSortedVectorImpl7() { };
+void SortedVectorImpl::reservedSortedVectorImpl8() { };
+
+
+/*****************************************************************************/
+
+}; // namespace android
+
diff --git a/libpixelflinger/tinyutils/VectorImpl.h b/libpixelflinger/tinyutils/VectorImpl.h
new file mode 100644
index 0000000..e868eca
--- /dev/null
+++ b/libpixelflinger/tinyutils/VectorImpl.h
@@ -0,0 +1,185 @@
+/*
+ *  vector_impl.h
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_VECTOR_IMPL_H
+#define ANDROID_VECTOR_IMPL_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts in here...
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+/*!
+ * Implementation of the guts of the vector<> class
+ * this ensures backward binary compatibility and
+ * reduces code size.
+ * For performance reasons, we expose mStorage and mCount
+ * so these fields are set in stone.
+ *
+ */
+
+class VectorImpl
+{
+public:
+    enum { // flags passed to the ctor
+        HAS_TRIVIAL_CTOR    = 0x00000001,
+        HAS_TRIVIAL_DTOR    = 0x00000002,
+        HAS_TRIVIAL_COPY    = 0x00000004,
+        HAS_TRIVIAL_ASSIGN  = 0x00000008
+    };
+
+                            VectorImpl(size_t itemSize, uint32_t flags);
+                            VectorImpl(const VectorImpl& rhs);
+    virtual                 ~VectorImpl();
+
+    /*! must be called from subclasses destructor */
+            void            finish_vector();
+
+            VectorImpl&     operator = (const VectorImpl& rhs);    
+            
+    /*! C-style array access */
+    inline  const void*     arrayImpl() const       { return mStorage; }
+            void*           editArrayImpl();
+            
+    /*! vector stats */
+    inline  size_t          size() const        { return mCount; }
+    inline  bool            isEmpty() const     { return mCount == 0; }
+            size_t          capacity() const;
+            ssize_t         setCapacity(size_t size);
+
+            /*! append/insert another vector */
+            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
+            ssize_t         appendVector(const VectorImpl& vector);
+            
+            /*! add/insert/replace items */
+            ssize_t         insertAt(size_t where, size_t numItems = 1);
+            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
+            void            pop();
+            void            push();
+            void            push(const void* item);
+            ssize_t         add();
+            ssize_t         add(const void* item);
+            ssize_t         replaceAt(size_t index);
+            ssize_t         replaceAt(const void* item, size_t index);
+
+            /*! remove items */
+            ssize_t         removeItemsAt(size_t index, size_t count = 1);
+            void            clear();
+
+            const void*     itemLocation(size_t index) const;
+            void*           editItemLocation(size_t index);
+
+protected:
+            size_t          itemSize() const;
+            void            release_storage();
+
+    virtual void            do_construct(void* storage, size_t num) const = 0;
+    virtual void            do_destroy(void* storage, size_t num) const = 0;
+    virtual void            do_copy(void* dest, const void* from, size_t num) const = 0;
+    virtual void            do_splat(void* dest, const void* item, size_t num) const = 0;
+    virtual void            do_move_forward(void* dest, const void* from, size_t num) const = 0;
+    virtual void            do_move_backward(void* dest, const void* from, size_t num) const = 0;
+
+    // take care of FBC...
+    virtual void            reservedVectorImpl1();
+    virtual void            reservedVectorImpl2();
+    virtual void            reservedVectorImpl3();
+    virtual void            reservedVectorImpl4();
+    virtual void            reservedVectorImpl5();
+    virtual void            reservedVectorImpl6();
+    virtual void            reservedVectorImpl7();
+    virtual void            reservedVectorImpl8();
+    
+private:
+        void* _grow(size_t where, size_t amount);
+        void  _shrink(size_t where, size_t amount);
+
+        inline void _do_construct(void* storage, size_t num) const;
+        inline void _do_destroy(void* storage, size_t num) const;
+        inline void _do_copy(void* dest, const void* from, size_t num) const;
+        inline void _do_splat(void* dest, const void* item, size_t num) const;
+        inline void _do_move_forward(void* dest, const void* from, size_t num) const;
+        inline void _do_move_backward(void* dest, const void* from, size_t num) const;
+
+            // These 2 fields are exposed in the inlines below,
+            // so they're set in stone.
+            void *      mStorage;   // base address of the vector
+            size_t      mCount;     // number of items
+
+    const   uint32_t    mFlags;
+    const   size_t      mItemSize;
+};
+
+
+
+class SortedVectorImpl : public VectorImpl
+{
+public:
+                            SortedVectorImpl(size_t itemSize, uint32_t flags);
+                            SortedVectorImpl(const VectorImpl& rhs);
+    virtual                 ~SortedVectorImpl();
+    
+    SortedVectorImpl&     operator = (const SortedVectorImpl& rhs);    
+
+    //! finds the index of an item
+            ssize_t         indexOf(const void* item) const;
+
+    //! finds where this item should be inserted
+            size_t          orderOf(const void* item) const;
+
+    //! add an item in the right place (or replaces it if there is one)
+            ssize_t         add(const void* item);
+
+    //! merges a vector into this one
+            ssize_t         merge(const VectorImpl& vector);
+            ssize_t         merge(const SortedVectorImpl& vector);
+             
+    //! removes an item
+            ssize_t         remove(const void* item);
+        
+protected:
+    virtual int             do_compare(const void* lhs, const void* rhs) const = 0;
+
+    // take care of FBC...
+    virtual void            reservedSortedVectorImpl1();
+    virtual void            reservedSortedVectorImpl2();
+    virtual void            reservedSortedVectorImpl3();
+    virtual void            reservedSortedVectorImpl4();
+    virtual void            reservedSortedVectorImpl5();
+    virtual void            reservedSortedVectorImpl6();
+    virtual void            reservedSortedVectorImpl7();
+    virtual void            reservedSortedVectorImpl8();
+
+private:
+            ssize_t         _indexOrderOf(const void* item, size_t* order = 0) const;
+
+            // these are made private, because they can't be used on a SortedVector
+            // (they don't have an implementation either)
+            ssize_t         add();
+            void            pop();
+            void            push();
+            void            push(const void* item);
+            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
+            ssize_t         appendVector(const VectorImpl& vector);
+            ssize_t         insertAt(size_t where, size_t numItems = 1);
+            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
+            ssize_t         replaceAt(size_t index);
+            ssize_t         replaceAt(const void* item, size_t index);
+};
+
+}; // namespace android
+
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_VECTOR_IMPL_H
diff --git a/libpixelflinger/tinyutils/smartpointer.h b/libpixelflinger/tinyutils/smartpointer.h
new file mode 100644
index 0000000..88032d7
--- /dev/null
+++ b/libpixelflinger/tinyutils/smartpointer.h
@@ -0,0 +1,170 @@
+/*
+ *  smartpointer.h
+ *  Android  
+ *
+ *  Copyright 2005 The Android Open Source Project
+ *
+ */
+
+#ifndef ANDROID_SMART_POINTER_H
+#define ANDROID_SMART_POINTER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+#define COMPARE(_op_)                                           \
+inline bool operator _op_ (const sp<T>& o) const {              \
+    return m_ptr _op_ o.m_ptr;                                  \
+}                                                               \
+inline bool operator _op_ (const T* o) const {                  \
+    return m_ptr _op_ o;                                        \
+}                                                               \
+template<typename U>                                            \
+inline bool operator _op_ (const sp<U>& o) const {              \
+    return m_ptr _op_ o.m_ptr;                                  \
+}                                                               \
+template<typename U>                                            \
+inline bool operator _op_ (const U* o) const {                  \
+    return m_ptr _op_ o;                                        \
+}
+
+// ---------------------------------------------------------------------------
+
+template <typename T>
+class sp
+{
+public:
+    inline sp() : m_ptr(0) { }
+
+    sp(T* other);
+    sp(const sp<T>& other);
+    template<typename U> sp(U* other);
+    template<typename U> sp(const sp<U>& other);
+
+    ~sp();
+    
+    // Assignment
+
+    sp& operator = (T* other);
+    sp& operator = (const sp<T>& other);
+    
+    template<typename U> sp& operator = (const sp<U>& other);
+    template<typename U> sp& operator = (U* other);
+    
+    // Reset
+    void clear();
+    
+    // Accessors
+
+    inline  T&      operator* () const  { return *m_ptr; }
+    inline  T*      operator-> () const { return m_ptr;  }
+    inline  T*      get() const         { return m_ptr; }
+
+    // Operators
+        
+    COMPARE(==)
+    COMPARE(!=)
+    COMPARE(>)
+    COMPARE(<)
+    COMPARE(<=)
+    COMPARE(>=)
+
+private:    
+    template<typename Y> friend class sp;
+
+    T*              m_ptr;
+};
+
+// ---------------------------------------------------------------------------
+// No user serviceable parts below here.
+
+template<typename T>
+sp<T>::sp(T* other)
+    : m_ptr(other)
+{
+    if (other) other->incStrong(this);
+}
+
+template<typename T>
+sp<T>::sp(const sp<T>& other)
+    : m_ptr(other.m_ptr)
+{
+    if (m_ptr) m_ptr->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(U* other) : m_ptr(other)
+{
+    if (other) other->incStrong(this);
+}
+
+template<typename T> template<typename U>
+sp<T>::sp(const sp<U>& other)
+    : m_ptr(other.m_ptr)
+{
+    if (m_ptr) m_ptr->incStrong(this);
+}
+
+template<typename T>
+sp<T>::~sp()
+{
+    if (m_ptr) m_ptr->decStrong(this);
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (const sp<T>& other) {
+    if (other.m_ptr) other.m_ptr->incStrong(this);
+    if (m_ptr) m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    return *this;
+}
+
+template<typename T>
+sp<T>& sp<T>::operator = (T* other)
+{
+    if (other) other->incStrong(this);
+    if (m_ptr) m_ptr->decStrong(this);
+    m_ptr = other;
+    return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (const sp<U>& other)
+{
+    if (other.m_ptr) other.m_ptr->incStrong(this);
+    if (m_ptr) m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    return *this;
+}
+
+template<typename T> template<typename U>
+sp<T>& sp<T>::operator = (U* other)
+{
+    if (other) other->incStrong(this);
+    if (m_ptr) m_ptr->decStrong(this);
+    m_ptr = other;
+    return *this;
+}
+
+template<typename T>
+void sp<T>::clear()
+{
+    if (m_ptr) {
+        m_ptr->decStrong(this);
+        m_ptr = 0;
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_SMART_POINTER_H
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
new file mode 100644
index 0000000..30b633f
--- /dev/null
+++ b/libpixelflinger/trap.cpp
@@ -0,0 +1,1173 @@
+/* libs/pixelflinger/trap.cpp
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "trap.h"
+#include "picker.h"
+
+#include <cutils/log.h>
+#include <cutils/memory.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// enable to see triangles edges
+#define DEBUG_TRANGLES  0
+
+// ----------------------------------------------------------------------------
+
+static void pointx_validate(void *con, const GGLcoord* c, GGLcoord r);
+static void pointx(void *con, const GGLcoord* c, GGLcoord r);
+static void aa_pointx(void *con, const GGLcoord* c, GGLcoord r);
+static void aa_nice_pointx(void *con, const GGLcoord* c, GGLcoord r);
+
+static void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w);
+
+static void recti_validate(void* c, GGLint l, GGLint t, GGLint r, GGLint b); 
+static void recti(void* c, GGLint l, GGLint t, GGLint r, GGLint b); 
+
+static void trianglex_validate(void*,
+        const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_small(void*,
+        const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_big(void*,
+        const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void aa_trianglex(void*,
+        const GGLcoord*, const GGLcoord*, const GGLcoord*);
+static void trianglex_debug(void* con,
+        const GGLcoord*, const GGLcoord*, const GGLcoord*);
+
+static void aapolyx(void* con,
+        const GGLcoord* pts, int count);
+
+static inline int min(int a, int b) CONST;
+static inline int max(int a, int b) CONST;
+static inline int min(int a, int b, int c) CONST;
+static inline int max(int a, int b, int c) CONST;
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Tools
+#endif
+
+inline int min(int a, int b) {
+    return a<b ? a : b;
+}
+inline int max(int a, int b) {
+    return a<b ? b : a;
+}
+inline int min(int a, int b, int c) {
+    return min(a,min(b,c));
+}
+inline int max(int a, int b, int c) {
+    return max(a,max(b,c));
+}
+
+template <typename T>
+static inline void swap(T& a, T& b) {
+    T t(a);
+    a = b;
+    b = t;
+}
+
+static void
+triangle_dump_points( const GGLcoord*  v0,
+                      const GGLcoord*  v1,
+				 	  const GGLcoord*  v2 )
+{
+    float tri = 1.0f / TRI_ONE;
+  LOGD(     "  P0=(%.3f, %.3f)  [%08x, %08x]\n"
+            "  P1=(%.3f, %.3f)  [%08x, %08x]\n"
+            "  P2=(%.3f, %.3f)  [%08x, %08x]\n",
+		v0[0]*tri, v0[1]*tri, v0[0], v0[1],
+		v1[0]*tri, v1[1]*tri, v1[0], v1[1],
+		v2[0]*tri, v2[1]*tri, v2[0], v2[1] );
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Misc
+#endif
+
+void ggl_init_trap(context_t* c)
+{
+    ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE|GGL_TMU_STATE|GGL_CB_STATE);
+}
+
+void ggl_state_changed(context_t* c, int flags)
+{
+    if (ggl_likely(!c->dirty)) {
+        c->procs.pointx     = pointx_validate;
+        c->procs.linex      = linex_validate;
+        c->procs.recti      = recti_validate;
+        c->procs.trianglex  = trianglex_validate;
+    }
+    c->dirty |= uint32_t(flags);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Point
+#endif
+
+void pointx_validate(void *con, const GGLcoord* v, GGLcoord rad)
+{
+    GGL_CONTEXT(c, con);
+    ggl_pick(c);
+    if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+        if (c->state.enables & GGL_ENABLE_POINT_AA_NICE) {
+            c->procs.pointx = aa_nice_pointx;
+        } else {
+            c->procs.pointx = aa_pointx;
+        }
+    } else {
+        c->procs.pointx = pointx;
+    }
+    c->procs.pointx(con, v, rad);
+}
+
+void pointx(void *con, const GGLcoord* v, GGLcoord rad)
+{
+    GGL_CONTEXT(c, con);
+    GGLcoord halfSize = TRI_ROUND(rad) >> 1;
+    if (halfSize == 0)
+        halfSize = TRI_HALF;
+    GGLcoord xc = v[0]; 
+    GGLcoord yc = v[1];
+    if (halfSize & TRI_HALF) { // size odd
+        xc = TRI_FLOOR(xc) + TRI_HALF;
+        yc = TRI_FLOOR(yc) + TRI_HALF;
+    } else { // size even
+        xc = TRI_ROUND(xc);
+        yc = TRI_ROUND(yc);
+    }
+    GGLint l = (xc - halfSize) >> TRI_FRACTION_BITS;
+    GGLint t = (yc - halfSize) >> TRI_FRACTION_BITS;
+    GGLint r = (xc + halfSize) >> TRI_FRACTION_BITS;
+    GGLint b = (yc + halfSize) >> TRI_FRACTION_BITS;
+    recti(c, l, t, r, b);
+}
+
+// This way of computing the coverage factor, is more accurate and gives
+// better results for small circles, but it is also a lot slower.
+// Here we use super-sampling.
+static int32_t coverageNice(GGLcoord x, GGLcoord y, 
+        GGLcoord rmin, GGLcoord rmax, GGLcoord rr)
+{
+    const GGLcoord d2 = x*x + y*y;
+    if (d2 >= rmax) return 0;
+    if (d2 < rmin)  return 0x7FFF;
+
+    const int kSamples              =  4;
+    const int kInc                  =  4;    // 1/4 = 0.25
+    const int kCoverageUnit         =  1;    // 1/(4^2) = 0.0625
+    const GGLcoord kCoordOffset     = -6;    // -0.375
+
+    int hits = 0;
+    int x_sample = x + kCoordOffset;
+    for (int i=0 ; i<kSamples ; i++, x_sample += kInc) {
+        const int xval = rr - (x_sample * x_sample);
+        int y_sample = y + kCoordOffset;
+        for (int j=0 ; j<kSamples ; j++, y_sample += kInc) {
+            if (xval - (y_sample * y_sample) > 0)
+                hits += kCoverageUnit;
+        }
+    }
+    return min(0x7FFF, hits << (15 - kSamples));
+}
+
+
+void aa_nice_pointx(void *con, const GGLcoord* v, GGLcoord size)
+{
+    GGL_CONTEXT(c, con);
+
+    GGLcoord rad = ((size + 1)>>1);
+    GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS;
+    GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS;
+    GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+    GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+    GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; 
+    GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; 
+
+    // scissor...
+    if (l < GGLint(c->state.scissor.left)) {
+        xstart += TRI_FROM_INT(c->state.scissor.left-l);
+        l = GGLint(c->state.scissor.left);
+    }
+    if (t < GGLint(c->state.scissor.top)) {
+        ystart += TRI_FROM_INT(c->state.scissor.top-t);
+        t = GGLint(c->state.scissor.top);
+    }
+    if (r > GGLint(c->state.scissor.right)) {
+        r = GGLint(c->state.scissor.right);
+    }
+    if (b > GGLint(c->state.scissor.bottom)) {
+        b = GGLint(c->state.scissor.bottom);
+    }
+
+    int xc = r - l;
+    int yc = b - t;
+    if (xc>0 && yc>0) {
+        int16_t* covPtr = c->state.buffers.coverage;
+        const int32_t sqr2Over2 = 0xC; // rounded up
+        GGLcoord rr = rad*rad;
+        GGLcoord rmin = (rad - sqr2Over2)*(rad - sqr2Over2);
+        GGLcoord rmax = (rad + sqr2Over2)*(rad + sqr2Over2);
+        GGLcoord y = ystart;
+        c->iterators.xl = l;
+        c->iterators.xr = r;
+        c->init_y(c, t);
+        do {
+            // compute coverage factors for each pixel
+            GGLcoord x = xstart;
+            for (int i=l ; i<r ; i++) {
+                covPtr[i] = coverageNice(x, y, rmin, rmax, rr);
+                x += TRI_ONE;
+            }
+            y += TRI_ONE;
+            c->scanline(c);
+            c->step_y(c);
+        } while (--yc);
+    }
+}
+
+// This is a cheap way of computing the coverage factor for a circle.
+// We just lerp between the circles of radii r-sqrt(2)/2 and r+sqrt(2)/2
+static inline int32_t coverageFast(GGLcoord x, GGLcoord y,
+        GGLcoord rmin, GGLcoord rmax, GGLcoord scale)
+{
+    const GGLcoord d2 = x*x + y*y;
+    if (d2 >= rmax) return 0;
+    if (d2 < rmin)  return 0x7FFF;
+    return 0x7FFF - (d2-rmin)*scale;
+}
+
+void aa_pointx(void *con, const GGLcoord* v, GGLcoord size)
+{
+    GGL_CONTEXT(c, con);
+
+    GGLcoord rad = ((size + 1)>>1);
+    GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS;
+    GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS;
+    GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+    GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS;
+    GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; 
+    GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; 
+
+    // scissor...
+    if (l < GGLint(c->state.scissor.left)) {
+        xstart += TRI_FROM_INT(c->state.scissor.left-l);
+        l = GGLint(c->state.scissor.left);
+    }
+    if (t < GGLint(c->state.scissor.top)) {
+        ystart += TRI_FROM_INT(c->state.scissor.top-t);
+        t = GGLint(c->state.scissor.top);
+    }
+    if (r > GGLint(c->state.scissor.right)) {
+        r = GGLint(c->state.scissor.right);
+    }
+    if (b > GGLint(c->state.scissor.bottom)) {
+        b = GGLint(c->state.scissor.bottom);
+    }
+
+    int xc = r - l;
+    int yc = b - t;
+    if (xc>0 && yc>0) {
+        int16_t* covPtr = c->state.buffers.coverage;
+        rad <<= 4;
+        const int32_t sqr2Over2 = 0xB5;    // fixed-point 24.8
+        GGLcoord rmin = rad - sqr2Over2;
+        GGLcoord rmax = rad + sqr2Over2;
+        GGLcoord scale;
+        rmin *= rmin;
+        rmax *= rmax;
+        scale = 0x800000 / (rmax - rmin);
+        rmin >>= 8;
+        rmax >>= 8;
+
+        GGLcoord y = ystart;
+        c->iterators.xl = l;
+        c->iterators.xr = r;
+        c->init_y(c, t);
+
+        do {
+            // compute coverage factors for each pixel
+            GGLcoord x = xstart;
+            for (int i=l ; i<r ; i++) {
+                covPtr[i] = coverageFast(x, y, rmin, rmax, scale);
+                x += TRI_ONE;
+            }
+            y += TRI_ONE;
+            c->scanline(c);
+            c->step_y(c);
+        } while (--yc);
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Line
+#endif
+
+void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w)
+{
+    GGL_CONTEXT(c, con);
+    ggl_pick(c);
+    if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+        c->procs.linex = aa_linex;
+    } else {
+        c->procs.linex = linex;
+    }
+    c->procs.linex(con, v0, v1, w);
+}
+
+static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
+{
+    GGL_CONTEXT(c, con);
+    GGLcoord v[4][2];
+    v[0][0] = v0[0];    v[0][1] = v0[1];
+    v[1][0] = v1[0];    v[1][1] = v1[1];
+    v0 = v[0];
+    v1 = v[1];
+    const GGLcoord dx = abs(v0[0] - v1[0]);
+    const GGLcoord dy = abs(v0[1] - v1[1]);
+    GGLcoord nx, ny;
+    nx = ny = 0;
+
+    GGLcoord halfWidth = TRI_ROUND(width) >> 1;
+    if (halfWidth == 0)
+        halfWidth = TRI_HALF;
+
+    ((dx > dy) ? ny : nx) = halfWidth;
+    v[2][0] = v1[0];    v[2][1] = v1[1];
+    v[3][0] = v0[0];    v[3][1] = v0[1];
+    v[0][0] += nx;      v[0][1] += ny;
+    v[1][0] += nx;      v[1][1] += ny;
+    v[2][0] -= nx;      v[2][1] -= ny;
+    v[3][0] -= nx;      v[3][1] -= ny;
+    trianglex_big(con, v[0], v[1], v[2]);
+    trianglex_big(con, v[0], v[2], v[3]);
+}
+
+static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width)
+{
+    GGL_CONTEXT(c, con);
+    GGLcoord v[4][2];
+    v[0][0] = v0[0];    v[0][1] = v0[1];
+    v[1][0] = v1[0];    v[1][1] = v1[1];
+    v0 = v[0];
+    v1 = v[1];
+    
+    const GGLcoord dx = v0[0] - v1[0];
+    const GGLcoord dy = v0[1] - v1[1];
+    GGLcoord nx = -dy;
+    GGLcoord ny =  dx;
+
+    // generally, this will be well below 1.0
+    const GGLfixed norm = gglMulx(width, gglSqrtRecipx(nx*nx+ny*ny), 4);
+    nx = gglMulx(nx, norm, 21);
+    ny = gglMulx(ny, norm, 21);
+    
+    v[2][0] = v1[0];    v[2][1] = v1[1];
+    v[3][0] = v0[0];    v[3][1] = v0[1];
+    v[0][0] += nx;      v[0][1] += ny;
+    v[1][0] += nx;      v[1][1] += ny;
+    v[2][0] -= nx;      v[2][1] -= ny;
+    v[3][0] -= nx;      v[3][1] -= ny;
+    aapolyx(con, v[0], 4);        
+}
+
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Rect
+#endif
+
+void recti_validate(void *con, GGLint l, GGLint t, GGLint r, GGLint b)
+{
+    GGL_CONTEXT(c, con);
+    ggl_pick(c);
+    c->procs.recti = recti;
+    c->procs.recti(con, l, t, r, b);
+}
+
+void recti(void* con, GGLint l, GGLint t, GGLint r, GGLint b)
+{
+    GGL_CONTEXT(c, con);
+
+    // scissor...
+    if (l < GGLint(c->state.scissor.left))
+        l = GGLint(c->state.scissor.left);
+    if (t < GGLint(c->state.scissor.top))
+        t = GGLint(c->state.scissor.top);
+    if (r > GGLint(c->state.scissor.right))
+        r = GGLint(c->state.scissor.right);
+    if (b > GGLint(c->state.scissor.bottom))
+        b = GGLint(c->state.scissor.bottom);
+
+    int xc = r - l;
+    int yc = b - t;
+    if (xc>0 && yc>0) {
+        c->iterators.xl = l;
+        c->iterators.xr = r;
+        c->init_y(c, t);
+        c->rect(c, yc);
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Triangle / Debugging
+#endif
+
+static void scanline_set(context_t* c)
+{
+    int32_t x = c->iterators.xl;
+    size_t ct = c->iterators.xr - x;
+    int32_t y = c->iterators.y;
+    surface_t* cb = &(c->state.buffers.color);
+    const GGLFormat* fp = &(c->formats[cb->format]);
+    uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) +
+                            (x + (cb->stride * y)) * fp->size;
+    const size_t size = ct * fp->size;
+    memset(dst, 0xFF, size);
+}
+
+static void trianglex_debug(void* con,
+        const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+    GGL_CONTEXT(c, con);
+    if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+        aa_trianglex(con,v0,v1,v2);
+    } else {
+        trianglex_big(con,v0,v1,v2);
+    }
+	void (*save_scanline)(context_t*)  = c->scanline;
+    c->scanline = scanline_set;
+    linex(con, v0, v1, TRI_ONE);
+    linex(con, v1, v2, TRI_ONE);
+    linex(con, v2, v0, TRI_ONE);
+    c->scanline = save_scanline;
+}
+
+static void trianglex_xor(void* con,
+        const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+    trianglex_big(con,v0,v1,v2);
+    trianglex_small(con,v0,v1,v2);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Triangle
+#endif
+
+void trianglex_validate(void *con,
+        const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+    GGL_CONTEXT(c, con);
+    ggl_pick(c);
+    if (c->state.needs.p & GGL_NEED_MASK(P_AA)) {
+        c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : aa_trianglex;
+    } else {
+        c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : trianglex_big;
+    }
+    c->procs.trianglex(con, v0, v1, v2);
+}
+
+// ----------------------------------------------------------------------------
+
+void trianglex_small(void* con,
+        const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+    GGL_CONTEXT(c, con);
+
+    // vertices are in 28.4 fixed point, which allows
+    // us to use 32 bits multiplies below.
+    int32_t x0 = v0[0];
+    int32_t y0 = v0[1];
+    int32_t x1 = v1[0];
+    int32_t y1 = v1[1];
+    int32_t x2 = v2[0];
+    int32_t y2 = v2[1];
+
+    int32_t dx01 = x0 - x1;
+    int32_t dy20 = y2 - y0;
+    int32_t dy01 = y0 - y1;
+    int32_t dx20 = x2 - x0;
+
+    // The code below works only with CCW triangles
+    // so if we get a CW triangle, we need to swap two of its vertices
+    if (dx01*dy20 < dy01*dx20) {
+        swap(x0, x1);
+        swap(y0, y1);
+        dx01 = x0 - x1;
+        dy01 = y0 - y1;
+        dx20 = x2 - x0;
+        dy20 = y2 - y0;
+    }
+    int32_t dx12 = x1 - x2;
+    int32_t dy12 = y1 - y2;
+
+    // bounding box & scissor
+    const int32_t bminx = TRI_FLOOR(min(x0, x1, x2)) >> TRI_FRACTION_BITS;
+    const int32_t bminy = TRI_FLOOR(min(y0, y1, y2)) >> TRI_FRACTION_BITS;
+    const int32_t bmaxx = TRI_CEIL( max(x0, x1, x2)) >> TRI_FRACTION_BITS;
+    const int32_t bmaxy = TRI_CEIL( max(y0, y1, y2)) >> TRI_FRACTION_BITS;
+    const int32_t minx = max(bminx, c->state.scissor.left);
+    const int32_t miny = max(bminy, c->state.scissor.top);
+    const int32_t maxx = min(bmaxx, c->state.scissor.right);
+    const int32_t maxy = min(bmaxy, c->state.scissor.bottom);
+    if ((minx >= maxx) || (miny >= maxy))
+        return; // too small or clipped out...
+
+    // step equations to the bounding box and snap to pixel center
+    const int32_t my = (miny << TRI_FRACTION_BITS) + TRI_HALF;
+    const int32_t mx = (minx << TRI_FRACTION_BITS) + TRI_HALF;
+    int32_t ey0 = dy01 * (x0 - mx) - dx01 * (y0 - my);
+    int32_t ey1 = dy12 * (x1 - mx) - dx12 * (y1 - my);
+    int32_t ey2 = dy20 * (x2 - mx) - dx20 * (y2 - my);
+
+    // right-exclusive fill rule, to avoid rare cases
+    // of over drawing
+    if (dy01<0 || (dy01 == 0 && dx01>0)) ey0++;
+    if (dy12<0 || (dy12 == 0 && dx12>0)) ey1++;
+    if (dy20<0 || (dy20 == 0 && dx20>0)) ey2++;
+    
+    c->init_y(c, miny);
+    for (int32_t y = miny; y < maxy; y++) {
+        register int32_t ex0 = ey0;
+        register int32_t ex1 = ey1;
+        register int32_t ex2 = ey2;    
+        register int32_t xl, xr;
+        for (xl=minx ; xl<maxx ; xl++) {
+            if (ex0>0 && ex1>0 && ex2>0)
+                break; // all strictly positive
+            ex0 -= dy01 << TRI_FRACTION_BITS;
+            ex1 -= dy12 << TRI_FRACTION_BITS;
+            ex2 -= dy20 << TRI_FRACTION_BITS;
+        }
+        xr = xl;
+        for ( ; xr<maxx ; xr++) {
+            if (!(ex0>0 && ex1>0 && ex2>0))
+                break; // not all strictly positive
+            ex0 -= dy01 << TRI_FRACTION_BITS;
+            ex1 -= dy12 << TRI_FRACTION_BITS;
+            ex2 -= dy20 << TRI_FRACTION_BITS;
+        }
+
+        if (xl < xr) {
+            c->iterators.xl = xl;
+            c->iterators.xr = xr;
+            c->scanline(c);
+        }
+        c->step_y(c);
+
+        ey0 += dx01 << TRI_FRACTION_BITS;
+        ey1 += dx12 << TRI_FRACTION_BITS;
+        ey2 += dx20 << TRI_FRACTION_BITS;
+    }
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+// the following routine fills a triangle via edge stepping, which
+// unfortunately requires divisions in the setup phase to get right,
+// it should probably only be used for relatively large trianges
+
+
+// x = y*DX/DY    (ou DX and DY are constants, DY > 0, et y >= 0)
+// 
+// for an equation of the type:
+//      x' = y*K/2^p     (with K and p constants "carefully chosen")
+// 
+// We can now do a DDA without precision loss. We define 'e' by:
+//      x' - x = y*(DX/DY - K/2^p) = y*e
+// 
+// If we choose K = round(DX*2^p/DY) then,
+//      abs(e) <= 1/2^(p+1) by construction
+// 
+// therefore abs(x'-x) = y*abs(e) <= y/2^(p+1) <= DY/2^(p+1) <= DMAX/2^(p+1)
+// 
+// which means that if DMAX <= 2^p, therefore abs(x-x') <= 1/2, including
+// at the last line. In fact, it's even a strict inequality except in one
+// extrem case (DY == DMAX et e = +/- 1/2)
+// 
+// Applying that to our coordinates, we need 2^p >= 4096*16 = 65536
+// so p = 16 is enough, we're so lucky!
+
+const int TRI_ITERATORS_BITS = 16;
+
+struct Edge
+{
+  int32_t  x;      // edge position in 16.16 coordinates
+  int32_t  x_incr; // on each step, increment x by that amount
+  int32_t  y_top;  // starting scanline, 16.4 format
+  int32_t  y_bot;
+};
+
+static void
+edge_dump( Edge*  edge )
+{
+  LOGI( "  top=%d (%.3f)  bot=%d (%.3f)  x=%d (%.3f)  ix=%d (%.3f)",
+        edge->y_top, edge->y_top/float(TRI_ONE),
+		edge->y_bot, edge->y_bot/float(TRI_ONE),
+		edge->x, edge->x/float(FIXED_ONE),
+		edge->x_incr, edge->x_incr/float(FIXED_ONE) );
+}
+
+static void
+triangle_dump_edges( Edge*  edges,
+                     int            count )
+{ 
+    LOGI( "%d edge%s:\n", count, count == 1 ? "" : "s" );
+	for ( ; count > 0; count--, edges++ )
+	  edge_dump( edges );
+}
+
+// the following function sets up an edge, it assumes
+// that ymin and ymax are in already in the 'reduced'
+// format
+static __attribute__((noinline))
+void edge_setup(
+        Edge*           edges,
+        int*            pcount,
+        const GGLcoord* p1,
+        const GGLcoord* p2,
+        int32_t         ymin,
+        int32_t         ymax )
+{
+	const GGLfixed*  top = p1;
+	const GGLfixed*  bot = p2;
+	Edge*    edge = edges + *pcount;
+
+	if (top[1] > bot[1]) {
+        swap(top, bot);
+	}
+
+	int  y1 = top[1] | 1;
+	int  y2 = bot[1] | 1;
+	int  dy = y2 - y1;
+
+	if ( dy == 0 || y1 > ymax || y2 < ymin )
+		return;
+
+	if ( y1 > ymin )
+		ymin = TRI_SNAP_NEXT_HALF(y1);
+	
+	if ( y2 < ymax )
+		ymax = TRI_SNAP_PREV_HALF(y2);
+
+	if ( ymin > ymax )  // when the edge doesn't cross any scanline
+	  return;
+
+	const int x1 = top[0];
+	const int dx = bot[0] - x1;
+    const int shift = TRI_ITERATORS_BITS - TRI_FRACTION_BITS;
+
+	// setup edge fields
+    // We add 0.5 to edge->x here because it simplifies the rounding
+    // in triangle_sweep_edges() -- this doesn't change the ordering of 'x'
+	edge->x      = (x1 << shift) + (1LU << (TRI_ITERATORS_BITS-1));
+	edge->x_incr = 0;
+	edge->y_top  = ymin;
+	edge->y_bot  = ymax;
+
+	if (ggl_likely(ymin <= ymax && dx)) {
+        edge->x_incr = gglDivQ16(dx, dy);
+    }
+    if (ggl_likely(y1 < ymin)) {
+        int32_t xadjust = (edge->x_incr * (ymin-y1)) >> TRI_FRACTION_BITS;
+        edge->x += xadjust;
+    }
+  
+	++*pcount;
+}
+
+
+static void
+triangle_sweep_edges( Edge*  left,
+                      Edge*  right,
+					  int            ytop,
+					  int            ybot,
+					  context_t*     c )
+{
+    int count = ((ybot - ytop)>>TRI_FRACTION_BITS) + 1;
+    if (count<=0) return;
+
+    // sort the edges horizontally
+    if ((left->x > right->x) || 
+        ((left->x == right->x) && (left->x_incr > right->x_incr))) {
+        swap(left, right);
+    }
+
+    int left_x = left->x;
+    int right_x = right->x;
+    const int left_xi = left->x_incr;
+    const int right_xi  = right->x_incr;
+    left->x  += left_xi * count;
+    right->x += right_xi * count;
+
+	const int xmin = c->state.scissor.left;
+	const int xmax = c->state.scissor.right;
+    do {
+        // horizontal scissoring
+        const int32_t xl = max(left_x  >> TRI_ITERATORS_BITS, xmin);
+        const int32_t xr = min(right_x >> TRI_ITERATORS_BITS, xmax);
+        left_x  += left_xi;
+        right_x += right_xi;
+        // invoke the scanline rasterizer
+        if (ggl_likely(xl < xr)) {
+            c->iterators.xl = xl;
+            c->iterators.xr = xr;
+            c->scanline(c);
+        }
+		c->step_y(c);
+	} while (--count);
+}
+
+
+void trianglex_big(void* con,
+        const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2)
+{
+    GGL_CONTEXT(c, con);
+
+    Edge edges[3];
+	int num_edges = 0;
+	int32_t ymin = TRI_FROM_INT(c->state.scissor.top)    + TRI_HALF;
+	int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom) - TRI_HALF;
+	    
+	edge_setup( edges, &num_edges, v0, v1, ymin, ymax );
+	edge_setup( edges, &num_edges, v0, v2, ymin, ymax );
+	edge_setup( edges, &num_edges, v1, v2, ymin, ymax );
+
+    if (ggl_unlikely(num_edges<2))  // for really tiny triangles that don't
+		return;                     // cross any scanline centers
+
+    Edge* left  = &edges[0];
+    Edge* right = &edges[1];
+    Edge* other = &edges[2];
+    int32_t y_top = min(left->y_top, right->y_top);
+    int32_t y_bot = max(left->y_bot, right->y_bot);
+
+	if (ggl_likely(num_edges==3)) {
+        y_top = min(y_top, edges[2].y_top);
+        y_bot = max(y_bot, edges[2].y_bot);
+		if (edges[0].y_top > y_top) {
+            other = &edges[0];
+            left  = &edges[2];
+		} else if (edges[1].y_top > y_top) {
+            other = &edges[1];
+            right = &edges[2];
+		}
+    }
+
+    c->init_y(c, y_top >> TRI_FRACTION_BITS);
+
+    int32_t y_mid = min(left->y_bot, right->y_bot);
+    triangle_sweep_edges( left, right, y_top, y_mid, c );
+
+    // second scanline sweep loop, if necessary
+    y_mid += TRI_ONE;
+    if (y_mid <= y_bot) {
+        ((left->y_bot == y_bot) ? right : left) = other;
+        if (other->y_top < y_mid) {
+            other->x += other->x_incr;
+        }
+        triangle_sweep_edges( left, right, y_mid, y_bot, c );
+    }
+}
+
+void aa_trianglex(void* con,
+        const GGLcoord* a, const GGLcoord* b, const GGLcoord* c)
+{
+    GGLcoord pts[6] = { a[0], a[1], b[0], b[1], c[0], c[1] };
+    aapolyx(con, pts, 3);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+struct AAEdge
+{
+    GGLfixed x;         // edge position in 12.16 coordinates
+    GGLfixed x_incr;    // on each y step, increment x by that amount
+    GGLfixed y_incr;    // on each x step, increment y by that amount
+    int16_t y_top;      // starting scanline, 12.4 format
+    int16_t y_bot;      // starting scanline, 12.4 format
+    void dump();
+};
+
+void AAEdge::dump()
+{
+    float tri  = 1.0f / TRI_ONE;
+    float iter = 1.0f / (1<<TRI_ITERATORS_BITS);
+    float fix  = 1.0f / FIXED_ONE;
+    LOGD(   "x=%08x (%.3f), "
+            "x_incr=%08x (%.3f), y_incr=%08x (%.3f), "
+            "y_top=%08x (%.3f), y_bot=%08x (%.3f) ",
+        x, x*fix,
+        x_incr, x_incr*iter,
+        y_incr, y_incr*iter,
+        y_top, y_top*tri,
+        y_bot, y_bot*tri );
+}
+
+// the following function sets up an edge, it assumes
+// that ymin and ymax are in already in the 'reduced'
+// format
+static __attribute__((noinline))
+void aa_edge_setup(
+        AAEdge*         edges,
+        int*            pcount,
+        const GGLcoord* p1,
+        const GGLcoord* p2,
+        int32_t         ymin,
+        int32_t         ymax )
+{
+    const GGLfixed*  top = p1;
+    const GGLfixed*  bot = p2;
+    AAEdge* edge = edges + *pcount;
+
+    if (top[1] > bot[1])
+        swap(top, bot);
+
+    int  y1 = top[1];
+    int  y2 = bot[1];
+    int  dy = y2 - y1;
+
+    if (dy==0 || y1>ymax || y2<ymin)
+        return;
+
+    if (y1 > ymin)
+        ymin = y1;
+    
+    if (y2 < ymax)
+        ymax = y2;
+
+    const int x1 = top[0];
+    const int dx = bot[0] - x1;
+    const int shift = FIXED_BITS - TRI_FRACTION_BITS;
+
+    // setup edge fields
+    edge->x      = x1 << shift;
+    edge->x_incr = 0;
+    edge->y_top  = ymin;
+    edge->y_bot  = ymax;
+    edge->y_incr = 0x7FFFFFFF;
+
+    if (ggl_likely(ymin <= ymax && dx)) {
+        edge->x_incr = gglDivQ16(dx, dy);
+        if (dx != 0) {
+            edge->y_incr = abs(gglDivQ16(dy, dx));
+        }
+    }
+    if (ggl_likely(y1 < ymin)) {
+        int32_t xadjust = (edge->x_incr * (ymin-y1))
+                >> (TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS);
+        edge->x += xadjust;
+    }
+  
+    ++*pcount;
+}
+
+
+typedef int (*compar_t)(const void*, const void*);
+static int compare_edges(const AAEdge *e0, const AAEdge *e1) {
+    if (e0->y_top > e1->y_top)      return 1;
+    if (e0->y_top < e1->y_top)      return -1;
+    if (e0->x > e1->x)              return 1;
+    if (e0->x < e1->x)              return -1;
+    if (e0->x_incr > e1->x_incr)    return 1;
+    if (e0->x_incr < e1->x_incr)    return -1;
+    return 0; // same edges, should never happen
+}
+
+static inline 
+void SET_COVERAGE(int16_t*& p, int32_t value, ssize_t n)
+{
+    android_memset16((uint16_t*)p, value, n*2);
+    p += n;
+}
+
+static inline 
+void ADD_COVERAGE(int16_t*& p, int32_t value)
+{
+    value = *p + value;
+    if (value >= 0x8000)
+        value = 0x7FFF;
+    *p++ = value;
+}
+
+static inline
+void SUB_COVERAGE(int16_t*& p, int32_t value)
+{
+    value = *p - value;
+    value &= ~(value>>31);
+    *p++ = value;
+}
+
+void aapolyx(void* con,
+        const GGLcoord* pts, int count)
+{
+    /*
+     * NOTE: This routine assumes that the polygon has been clipped to the
+     * viewport already, that is, no vertex lies outside of the framebuffer.
+     * If this happens, the code below won't corrupt memory but the 
+     * coverage values may not be correct.
+     */
+    
+    GGL_CONTEXT(c, con);
+
+    // we do only quads for now (it's used for thick lines)
+    if ((count>4) || (count<2)) return;
+
+    // take scissor into account
+    const int xmin = c->state.scissor.left;
+    const int xmax = c->state.scissor.right;
+    if (xmin >= xmax) return;
+
+    // generate edges from the vertices
+    int32_t ymin = TRI_FROM_INT(c->state.scissor.top);
+    int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom);
+    if (ymin >= ymax) return;
+
+    AAEdge edges[4];
+    int num_edges = 0;
+    GGLcoord const * p = pts;
+    for (int i=0 ; i<count-1 ; i++, p+=2) {
+        aa_edge_setup(edges, &num_edges, p, p+2, ymin, ymax);
+    }
+    aa_edge_setup(edges, &num_edges, p, pts, ymin, ymax );
+    if (ggl_unlikely(num_edges<2))
+        return;
+
+    // sort the edge list top to bottom, left to right.
+    qsort(edges, num_edges, sizeof(AAEdge), (compar_t)compare_edges);
+
+    int16_t* const covPtr = c->state.buffers.coverage;
+    memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr));
+
+    // now, sweep all edges in order
+    // start with the 2 first edges. We know that they share their top
+    // vertex, by construction.
+    int i = 2;
+    AAEdge* left  = &edges[0];
+    AAEdge* right = &edges[1];
+    int32_t yt = left->y_top;
+    GGLfixed l = left->x;
+    GGLfixed r = right->x;
+    int retire = 0;
+    int16_t* coverage;
+
+    // at this point we can initialize the rasterizer    
+    c->init_y(c, yt>>TRI_FRACTION_BITS);
+    c->iterators.xl = xmax;
+    c->iterators.xr = xmin;
+
+    do {
+        int32_t y = min(min(left->y_bot, right->y_bot), TRI_FLOOR(yt + TRI_ONE));
+        const int32_t shift = TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS;
+        const int cf_shift = (1 + TRI_FRACTION_BITS*2 + TRI_ITERATORS_BITS - 15);
+
+        // compute xmin and xmax for the left edge
+        GGLfixed l_min = gglMulAddx(left->x_incr, y - left->y_top, left->x, shift);
+        GGLfixed l_max = l;
+        l = l_min;
+        if (l_min > l_max)
+            swap(l_min, l_max);
+
+        // compute xmin and xmax for the right edge
+        GGLfixed r_min = gglMulAddx(right->x_incr, y - right->y_top, right->x, shift);
+        GGLfixed r_max = r;
+        r = r_min;
+        if (r_min > r_max)
+            swap(r_min, r_max);
+
+        // make sure we're not touching coverage values outside of the
+        // framebuffer
+        l_min &= ~(l_min>>31);
+        r_min &= ~(r_min>>31);
+        l_max &= ~(l_max>>31);
+        r_max &= ~(r_max>>31);
+        if (gglFixedToIntFloor(l_min) >= xmax) l_min = gglIntToFixed(xmax)-1;
+        if (gglFixedToIntFloor(r_min) >= xmax) r_min = gglIntToFixed(xmax)-1;
+        if (gglFixedToIntCeil(l_max) >= xmax)  l_max = gglIntToFixed(xmax)-1;
+        if (gglFixedToIntCeil(r_max) >= xmax)  r_max = gglIntToFixed(xmax)-1;
+
+        // compute the integer versions of the above
+        const GGLfixed l_min_i = gglFloorx(l_min);
+        const GGLfixed l_max_i = gglCeilx (l_max);
+        const GGLfixed r_min_i = gglFloorx(r_min);
+        const GGLfixed r_max_i = gglCeilx (r_max);
+
+        // clip horizontally using the scissor
+        const int xml = max(xmin, gglFixedToIntFloor(l_min_i));
+        const int xmr = min(xmax, gglFixedToIntFloor(r_max_i));
+
+        // if we just stepped to a new scanline, render the previous one.
+        // and clear the coverage buffer
+        if (retire) {
+            if (c->iterators.xl < c->iterators.xr)
+                c->scanline(c);
+            c->step_y(c);
+            memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr));
+            c->iterators.xl = xml;
+            c->iterators.xr = xmr;
+        } else {
+            // update the horizontal range of this scanline
+            c->iterators.xl = min(c->iterators.xl, xml);
+            c->iterators.xr = max(c->iterators.xr, xmr);
+        }
+
+        coverage = covPtr + gglFixedToIntFloor(l_min_i);
+        if (l_min_i == gglFloorx(l_max)) {
+            
+            /*
+             *  fully traverse this pixel vertically
+             *       l_max
+             *  +-----/--+  yt
+             *  |    /   |  
+             *  |   /    |
+             *  |  /     |
+             *  +-/------+  y
+             *   l_min  (l_min_i + TRI_ONE)
+             */
+              
+            GGLfixed dx = l_max - l_min;
+            int32_t dy = y - yt;
+            int cf = gglMulx((dx >> 1) + (l_min_i + FIXED_ONE - l_max), dy,
+                FIXED_BITS + TRI_FRACTION_BITS - 15);
+            ADD_COVERAGE(coverage, cf);
+            // all pixels on the right have cf = 1.0
+        } else {
+            /*
+             *  spans several pixels in one scanline
+             *            l_max
+             *  +--------+--/-----+  yt
+             *  |        |/       |
+             *  |       /|        |
+             *  |     /  |        |
+             *  +---/----+--------+  y
+             *   l_min (l_min_i + TRI_ONE)
+             */
+
+            // handle the first pixel separately...
+            const int32_t y_incr = left->y_incr;
+            int32_t dx = TRI_FROM_FIXED(l_min_i - l_min) + TRI_ONE;
+            int32_t cf = (dx * dx * y_incr) >> cf_shift;
+            ADD_COVERAGE(coverage, cf);
+
+            // following pixels get covered by y_incr, but we need
+            // to fix-up the cf to account for previous partial pixel
+            dx = TRI_FROM_FIXED(l_min - l_min_i);
+            cf -= (dx * dx * y_incr) >> cf_shift;
+            for (int x = l_min_i+FIXED_ONE ; x < l_max_i-FIXED_ONE ; x += FIXED_ONE) {
+                cf += y_incr >> (TRI_ITERATORS_BITS-15);
+                ADD_COVERAGE(coverage, cf);
+            }
+            
+            // and the last pixel
+            dx = TRI_FROM_FIXED(l_max - l_max_i) - TRI_ONE;
+            cf += (dx * dx * y_incr) >> cf_shift;
+            ADD_COVERAGE(coverage, cf);
+        }
+        
+        // now, fill up all fully covered pixels
+        coverage = covPtr + gglFixedToIntFloor(l_max_i);
+        int cf = ((y - yt) << (15 - TRI_FRACTION_BITS));
+        if (ggl_likely(cf >= 0x8000)) {
+            SET_COVERAGE(coverage, 0x7FFF, ((r_max - l_max_i)>>FIXED_BITS)+1);
+        } else {
+            for (int x=l_max_i ; x<r_max ; x+=FIXED_ONE) {
+                ADD_COVERAGE(coverage, cf);
+            }
+        }
+        
+        // subtract the coverage of the right edge
+        coverage = covPtr + gglFixedToIntFloor(r_min_i); 
+        if (r_min_i == gglFloorx(r_max)) {
+            GGLfixed dx = r_max - r_min;
+            int32_t dy = y - yt;
+            int cf = gglMulx((dx >> 1) + (r_min_i + FIXED_ONE - r_max), dy,
+                FIXED_BITS + TRI_FRACTION_BITS - 15);
+            SUB_COVERAGE(coverage, cf);
+            // all pixels on the right have cf = 1.0
+        } else {
+            // handle the first pixel separately...
+            const int32_t y_incr = right->y_incr;
+            int32_t dx = TRI_FROM_FIXED(r_min_i - r_min) + TRI_ONE;
+            int32_t cf = (dx * dx * y_incr) >> cf_shift;
+            SUB_COVERAGE(coverage, cf);
+            
+            // following pixels get covered by y_incr, but we need
+            // to fix-up the cf to account for previous partial pixel
+            dx = TRI_FROM_FIXED(r_min - r_min_i);
+            cf -= (dx * dx * y_incr) >> cf_shift;
+            for (int x = r_min_i+FIXED_ONE ; x < r_max_i-FIXED_ONE ; x += FIXED_ONE) {
+                cf += y_incr >> (TRI_ITERATORS_BITS-15);
+                SUB_COVERAGE(coverage, cf);
+            }
+            
+            // and the last pixel
+            dx = TRI_FROM_FIXED(r_max - r_max_i) - TRI_ONE;
+            cf += (dx * dx * y_incr) >> cf_shift;
+            SUB_COVERAGE(coverage, cf);
+        }
+
+        // did we reach the end of an edge? if so, get a new one.
+        if (y == left->y_bot || y == right->y_bot) {
+            // bail out if we're done
+            if (i>=num_edges)
+                break;
+            if (y == left->y_bot)
+                left = &edges[i++];
+            if (y == right->y_bot)
+                right = &edges[i++];
+        }
+
+        // next scanline
+        yt = y;
+        
+        // did we just finish a scanline?        
+        retire = (y << (32-TRI_FRACTION_BITS)) == 0;
+    } while (true);
+
+    // render the last scanline
+    if (c->iterators.xl < c->iterators.xr)
+        c->scanline(c);
+}
+
+}; // namespace android
diff --git a/libpixelflinger/trap.h b/libpixelflinger/trap.h
new file mode 100644
index 0000000..7cce7b3
--- /dev/null
+++ b/libpixelflinger/trap.h
@@ -0,0 +1,31 @@
+/* libs/pixelflinger/trap.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+
+#ifndef ANDROID_TRAP_H
+#define ANDROID_TRAP_H
+
+#include <private/pixelflinger/ggl_context.h>
+
+namespace android {
+
+void ggl_init_trap(context_t* c);
+void ggl_state_changed(context_t* c, int flags);
+
+}; // namespace android
+
+#endif
diff --git a/libzipfile/Android.mk b/libzipfile/Android.mk
new file mode 100644
index 0000000..d2d758c
--- /dev/null
+++ b/libzipfile/Android.mk
@@ -0,0 +1,48 @@
+LOCAL_PATH:= $(call my-dir)
+
+# build host static library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	centraldir.c \
+	zipfile.c
+
+LOCAL_STATIC_LIBRARIES := \
+	libunz
+
+LOCAL_MODULE:= libzipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# build device static library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	centraldir.c \
+	zipfile.c
+
+LOCAL_STATIC_LIBRARIES := \
+	libunz
+
+LOCAL_MODULE:= libzipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+# build test_zipfile
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	test_zipfile.c
+
+LOCAL_STATIC_LIBRARIES := libzipfile libunz
+
+LOCAL_MODULE := test_zipfile
+
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/libzipfile/MODULE_LICENSE_APACHE2 b/libzipfile/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libzipfile/MODULE_LICENSE_APACHE2
diff --git a/libzipfile/NOTICE b/libzipfile/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libzipfile/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c
new file mode 100644
index 0000000..4387ceb
--- /dev/null
+++ b/libzipfile/centraldir.c
@@ -0,0 +1,256 @@
+#include "private.h"

+#include <stdio.h>

+#include <string.h>

+#include <stdlib.h>

+

+enum {

+    // finding the directory

+    CD_SIGNATURE = 0x06054b50,

+    EOCD_LEN     = 22,        // EndOfCentralDir len, excl. comment

+    MAX_COMMENT_LEN = 65535,

+    MAX_EOCD_SEARCH = MAX_COMMENT_LEN + EOCD_LEN,

+

+    // central directory entries

+    ENTRY_SIGNATURE = 0x02014b50,

+    ENTRY_LEN = 46,          // CentralDirEnt len, excl. var fields

+    

+    // local file header

+    LFH_SIZE = 30,

+};

+

+unsigned int

+read_le_int(const unsigned char* buf)

+{

+    return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);

+}

+

+unsigned int

+read_le_short(const unsigned char* buf)

+{

+    return buf[0] | (buf[1] << 8);

+}

+

+static int

+read_central_dir_values(Zipfile* file, const unsigned char* buf, int len)

+{

+    if (len < EOCD_LEN) {

+        // looks like ZIP file got truncated

+        fprintf(stderr, " Zip EOCD: expected >= %d bytes, found %d\n",

+                EOCD_LEN, len);

+        return -1;

+    }

+

+    file->disknum = read_le_short(&buf[0x04]);

+    file->diskWithCentralDir = read_le_short(&buf[0x06]);

+    file->entryCount = read_le_short(&buf[0x08]);

+    file->totalEntryCount = read_le_short(&buf[0x0a]);

+    file->centralDirSize = read_le_int(&buf[0x0c]);

+    file->centralDirOffest = read_le_int(&buf[0x10]);

+    file->commentLen = read_le_short(&buf[0x14]);

+

+    if (file->commentLen > 0) {

+        if (EOCD_LEN + file->commentLen > len) {

+            fprintf(stderr, "EOCD(%d) + comment(%d) exceeds len (%d)\n",

+                    EOCD_LEN, file->commentLen, len);

+            return -1;

+        }

+        file->comment = buf + EOCD_LEN;

+    }

+

+    return 0;

+}

+

+static int

+read_central_directory_entry(Zipfile* file, Zipentry* entry,

+                const unsigned char** buf, ssize_t* len)

+{

+    const unsigned char* p;

+

+    unsigned short  versionMadeBy;

+    unsigned short  versionToExtract;

+    unsigned short  gpBitFlag;

+    unsigned short  compressionMethod;

+    unsigned short  lastModFileTime;

+    unsigned short  lastModFileDate;

+    unsigned long   crc32;

+    unsigned long   compressedSize;

+    unsigned long   uncompressedSize;

+    unsigned short  extraFieldLength;

+    unsigned short  fileCommentLength;

+    unsigned short  diskNumberStart;

+    unsigned short  internalAttrs;

+    unsigned long   externalAttrs;

+    unsigned long   localHeaderRelOffset;

+    const unsigned char*  extraField;

+    const unsigned char*  fileComment;

+    unsigned int dataOffset;

+    unsigned short lfhExtraFieldSize;

+    

+

+    p = *buf;

+

+    if (*len < ENTRY_LEN) {

+        fprintf(stderr, "cde entry not large enough\n");

+        return -1;

+    }

+

+    if (read_le_int(&p[0x00]) != ENTRY_SIGNATURE) {

+        fprintf(stderr, "Whoops: didn't find expected signature\n");

+        return -1;

+    }

+

+    versionMadeBy = read_le_short(&p[0x04]);

+    versionToExtract = read_le_short(&p[0x06]);

+    gpBitFlag = read_le_short(&p[0x08]);

+    entry->compressionMethod = read_le_short(&p[0x0a]);

+    lastModFileTime = read_le_short(&p[0x0c]);

+    lastModFileDate = read_le_short(&p[0x0e]);

+    crc32 = read_le_int(&p[0x10]);

+    compressedSize = read_le_int(&p[0x14]);

+    entry->uncompressedSize = read_le_int(&p[0x18]);

+    entry->fileNameLength = read_le_short(&p[0x1c]);

+    extraFieldLength = read_le_short(&p[0x1e]);

+    fileCommentLength = read_le_short(&p[0x20]);

+    diskNumberStart = read_le_short(&p[0x22]);

+    internalAttrs = read_le_short(&p[0x24]);

+    externalAttrs = read_le_int(&p[0x26]);

+    localHeaderRelOffset = read_le_int(&p[0x2a]);

+

+    p += ENTRY_LEN;

+

+    // filename

+    if (entry->fileNameLength != 0) {

+        entry->fileName = p;

+    } else {

+        entry->fileName = NULL;

+    }

+    p += entry->fileNameLength;

+

+    // extra field

+    if (extraFieldLength != 0) {

+        extraField = p;

+    } else {

+        extraField = NULL;

+    }

+    p += extraFieldLength;

+

+    // comment, if any

+    if (fileCommentLength != 0) {

+        fileComment = p;

+    } else {

+        fileComment = NULL;

+    }

+    p += fileCommentLength;

+    

+    *buf = p;

+

+    // the size of the extraField in the central dir is how much data there is,

+    // but the one in the local file header also contains some padding.

+    p = file->buf + localHeaderRelOffset;

+    extraFieldLength = read_le_short(&p[0x1c]);

+    

+    dataOffset = localHeaderRelOffset + LFH_SIZE

+        + entry->fileNameLength + extraFieldLength;

+    entry->data = file->buf + dataOffset;

+#if 0

+    printf("file->buf=%p entry->data=%p dataOffset=%x localHeaderRelOffset=%d "

+           "entry->fileNameLength=%d extraFieldLength=%d\n",

+           file->buf, entry->data, dataOffset, localHeaderRelOffset,

+           entry->fileNameLength, extraFieldLength);

+#endif

+    return 0;

+}

+

+/*

+ * Find the central directory and read the contents.

+ *

+ * The fun thing about ZIP archives is that they may or may not be

+ * readable from start to end.  In some cases, notably for archives

+ * that were written to stdout, the only length information is in the

+ * central directory at the end of the file.

+ *

+ * Of course, the central directory can be followed by a variable-length

+ * comment field, so we have to scan through it backwards.  The comment

+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff

+ * itself, plus apparently sometimes people throw random junk on the end

+ * just for the fun of it.

+ *

+ * This is all a little wobbly.  If the wrong value ends up in the EOCD

+ * area, we're hosed.  This appears to be the way that everbody handles

+ * it though, so we're in pretty good company if this fails.

+ */

+int

+read_central_dir(Zipfile *file)

+{

+    int err;

+

+    const unsigned char* buf = file->buf;

+    ssize_t bufsize = file->bufsize;

+    const unsigned char* eocd;

+    const unsigned char* p;

+    const unsigned char* start;

+    ssize_t len;

+    int i;

+

+    // too small to be a ZIP archive?

+    if (bufsize < EOCD_LEN) {

+        fprintf(stderr, "Length is %d -- too small\n", bufsize);

+        goto bail;

+    }

+

+    // find the end-of-central-dir magic

+    if (bufsize > MAX_EOCD_SEARCH) {

+        start = buf + bufsize - MAX_EOCD_SEARCH;

+    } else {

+        start = buf;

+    }

+    p = buf + bufsize - 4;

+    while (p >= start) {

+        if (*p == 0x50 && read_le_int(p) == CD_SIGNATURE) {

+            eocd = p;

+            break;

+        }

+        p--;

+    }

+    if (p < start) {

+        fprintf(stderr, "EOCD not found, not Zip\n");

+        goto bail;

+    }

+

+    // extract eocd values

+    err = read_central_dir_values(file, eocd, (buf+bufsize)-eocd);

+    if (err != 0) {

+        goto bail;

+    }

+

+    if (file->disknum != 0

+          || file->diskWithCentralDir != 0

+          || file->entryCount != file->totalEntryCount) {

+        fprintf(stderr, "Archive spanning not supported\n");

+        goto bail;

+    }

+

+    // Loop through and read the central dir entries.

+    p = buf + file->centralDirOffest;

+    len = (buf+bufsize)-p;

+    for (i=0; i < file->totalEntryCount; i++) {

+        Zipentry* entry = malloc(sizeof(Zipentry));

+        memset(entry, sizeof(Zipentry), 0);

+

+        err = read_central_directory_entry(file, entry, &p, &len);

+        if (err != 0) {

+            fprintf(stderr, "read_central_directory_entry failed\n");

+            free(entry);

+            goto bail;

+        }

+        

+        // add it to our list

+        entry->next = file->entries;

+        file->entries = entry;

+    }

+

+    return 0;

+bail:

+    return -1;

+}

+

diff --git a/libzipfile/private.h b/libzipfile/private.h
new file mode 100644
index 0000000..06f788d
--- /dev/null
+++ b/libzipfile/private.h
@@ -0,0 +1,45 @@
+#ifndef PRIVATE_H

+#define PRIVATE_H

+

+#include <stddef.h>

+#include <stdint.h>

+#include <stdio.h>

+#include <string.h>

+#include <stdlib.h>

+

+typedef struct Zipentry {

+    unsigned long fileNameLength;

+    const unsigned char* fileName;

+    unsigned short compressionMethod;

+    unsigned int uncompressedSize;

+    unsigned int compressedSize;

+    const unsigned char* data;

+    

+    struct Zipentry* next;

+} Zipentry;

+

+typedef struct Zipfile

+{

+    const unsigned char *buf;

+    ssize_t bufsize;

+

+    // Central directory

+    unsigned short  disknum;            //mDiskNumber;

+    unsigned short  diskWithCentralDir; //mDiskWithCentralDir;

+    unsigned short  entryCount;         //mNumEntries;

+    unsigned short  totalEntryCount;    //mTotalNumEntries;

+    unsigned int    centralDirSize;     //mCentralDirSize;

+    unsigned int    centralDirOffest;  // offset from first disk  //mCentralDirOffset;

+    unsigned short  commentLen;         //mCommentLen;

+    const unsigned char*  comment;            //mComment;

+

+    Zipentry* entries;

+} Zipfile;

+

+int read_central_dir(Zipfile* file);

+

+unsigned int read_le_int(const unsigned char* buf);

+unsigned int read_le_short(const unsigned char* buf);

+

+#endif // PRIVATE_H

+

diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c
new file mode 100644
index 0000000..40840ec
--- /dev/null
+++ b/libzipfile/test_zipfile.c
@@ -0,0 +1,92 @@
+#include <zipfile/zipfile.h>

+

+#include <stdio.h>

+#include <stdlib.h>

+

+void dump_zipfile(FILE* to, zipfile_t file);

+

+int

+main(int argc, char** argv)

+{

+    FILE* f;

+    size_t size, unsize;

+    void* buf;

+    void* scratch;

+    zipfile_t zip;

+    zipentry_t entry;

+    int err;

+    enum { HUH, LIST, UNZIP } what = HUH;

+

+    if (strcmp(argv[2], "-l") == 0 && argc == 3) {

+        what = LIST;

+    }

+    else if (strcmp(argv[2], "-u") == 0 && argc == 5) {

+        what = UNZIP;

+    }

+    else {

+        fprintf(stderr, "usage: test_zipfile ZIPFILE -l\n"

+                        "          lists the files in the zipfile\n"

+                        "       test_zipfile ZIPFILE -u FILENAME SAVETO\n"

+                        "          saves FILENAME from the zip file into SAVETO\n");

+        return 1;

+    }

+    

+    f = fopen(argv[1], "r");

+    if (f == NULL) {

+        fprintf(stderr, "couldn't open %s\n", argv[1]);

+        return 1;

+    }

+

+    fseek(f, 0, SEEK_END);

+    size = ftell(f);

+    rewind(f);

+    

+    buf = malloc(size);

+    fread(buf, 1, size, f);

+

+    zip = init_zipfile(buf, size);

+    if (zip == NULL) {

+        fprintf(stderr, "inti_zipfile failed\n");

+        return 1;

+    }

+

+    fclose(f);

+

+

+    switch (what)

+    {

+        case LIST:

+            dump_zipfile(stdout, zip);

+            break;

+        case UNZIP:

+            entry = lookup_zipentry(zip, argv[3]);

+            if (entry == NULL) {

+                fprintf(stderr, "zip file '%s' does not contain file '%s'\n",

+                                argv[1], argv[1]);

+                return 1;

+            }

+            f = fopen(argv[4], "w");

+            if (f == NULL) {

+                fprintf(stderr, "can't open file for writing '%s'\n", argv[4]);

+                return 1;

+            }

+            unsize = get_zipentry_size(entry);

+            size = unsize * 1.001;

+            scratch = malloc(size);

+            printf("scratch=%p\n", scratch);

+            err = decompress_zipentry(entry, scratch, size);

+            if (err != 0) {

+                fprintf(stderr, "error decompressing file\n");

+                return 1;

+            }

+            fwrite(scratch, unsize, 1, f);

+            free(scratch);

+            fclose(f);

+            break;

+    }

+    

+    free(buf);

+

+    return 0;

+}

+

diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c
new file mode 100644
index 0000000..b52d02d
--- /dev/null
+++ b/libzipfile/zipfile.c
@@ -0,0 +1,160 @@
+#include <zipfile/zipfile.h>
+
+#include "private.h"
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+zipfile_t
+init_zipfile(const void* data, size_t size)
+{
+    int err;
+
+    Zipfile *file = malloc(sizeof(Zipfile));
+    if (file == NULL) return NULL;
+    memset(file, 0, sizeof(Zipfile));
+    file->buf = data;
+    file->bufsize = size;
+
+    err = read_central_dir(file);
+    if (err != 0) goto fail;
+
+    return file;
+fail:
+    free(file);
+    return NULL;
+}
+
+void
+release_zipfile(zipfile_t f)
+{
+    Zipfile* file = (Zipfile*)f;
+    Zipentry* entry = file->entries;
+    while (entry) {
+        Zipentry* next = entry->next;
+        free(entry);
+        entry = next;
+    }
+    free(file);
+}
+
+zipentry_t
+lookup_zipentry(zipfile_t f, const char* entryName)
+{
+    Zipfile* file = (Zipfile*)f;
+    Zipentry* entry = file->entries;
+    while (entry) {
+        if (0 == memcmp(entryName, entry->fileName, entry->fileNameLength)) {
+            return entry;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+size_t
+get_zipentry_size(zipentry_t entry)
+{
+    return ((Zipentry*)entry)->uncompressedSize;
+}
+
+char*
+get_zipentry_name(zipentry_t entry)
+{
+    Zipentry* e = (Zipentry*)entry;
+    int l = e->fileNameLength;
+    char* s = malloc(l+1);
+    memcpy(s, e->fileName, l);
+    s[l] = '\0';
+    return s;
+}
+
+enum {
+    STORED = 0,
+    DEFLATED = 8
+};
+
+static int
+uninflate(unsigned char* out, int unlen, const unsigned char* in, int clen)
+{
+    z_stream zstream;
+    unsigned long crc;
+    int err = 0;
+    int zerr;
+    
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = (void*)in;
+    zstream.avail_in = unlen;
+    zstream.next_out = (Bytef*) out;
+    zstream.avail_out = unlen;
+    zstream.data_type = Z_UNKNOWN;
+
+    // Use the undocumented "negative window bits" feature to tell zlib
+    // that there's no zlib header waiting for it.
+    zerr = inflateInit2(&zstream, -MAX_WBITS);
+    if (zerr != Z_OK) {
+        return -1;
+    }
+    
+    // uncompress the data
+    zerr = inflate(&zstream, Z_FINISH);
+    if (zerr != Z_STREAM_END) {
+        fprintf(stderr, "zerr=%d Z_STREAM_END=%d total_out=%lu\n", zerr, Z_STREAM_END,
+                    zstream.total_out);
+        err = -1;
+    }
+    
+     inflateEnd(&zstream);
+    return err;
+}
+
+int
+decompress_zipentry(zipentry_t e, void* buf, int bufsize)
+{
+    Zipentry* entry = (Zipentry*)e;
+    switch (entry->compressionMethod)
+    {
+        case STORED:
+            memcpy(buf, entry->data, entry->uncompressedSize);
+            return 0;
+        case DEFLATED:
+            return uninflate(buf, bufsize, entry->data, entry->compressedSize);
+        default:
+            return -1;
+    }
+}
+
+void
+dump_zipfile(FILE* to, zipfile_t file)
+{
+    Zipfile* zip = (Zipfile*)file;
+    Zipentry* entry = zip->entries;
+    int i;
+
+    fprintf(to, "entryCount=%d\n", zip->entryCount);
+    for (i=0; i<zip->entryCount; i++) {
+        fprintf(to, "  file \"");
+        fwrite(entry->fileName, entry->fileNameLength, 1, to);
+        fprintf(to, "\"\n");
+        entry = entry->next;
+    }
+}
+
+zipentry_t
+iterate_zipfile(zipfile_t file, void** cookie)
+{
+    Zipentry* entry = (Zipentry*)*cookie;
+    if (entry == NULL) {
+        Zipfile* zip = (Zipfile*)file;
+        *cookie = zip->entries;
+        return *cookie;
+    } else {
+        entry = entry->next;
+        *cookie = entry;
+        return entry;
+    }
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
new file mode 100644
index 0000000..3ee051d
--- /dev/null
+++ b/logcat/Android.mk
@@ -0,0 +1,27 @@
+# Copyright 2006 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= logcat.cpp
+
+LOCAL_SHARED_LIBRARIES := liblog
+
+LOCAL_MODULE:= logcat
+
+include $(BUILD_EXECUTABLE)
+
+########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := event-log-tags
+
+#LOCAL_MODULE_TAGS := user development
+
+# This will install the file in /system/etc
+#
+LOCAL_MODULE_CLASS := ETC
+
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+include $(BUILD_PREBUILT)
diff --git a/logcat/MODULE_LICENSE_APACHE2 b/logcat/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/logcat/MODULE_LICENSE_APACHE2
diff --git a/logcat/NOTICE b/logcat/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/logcat/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/logcat/event-log-tags b/logcat/event-log-tags
new file mode 100644
index 0000000..28cad0a
--- /dev/null
+++ b/logcat/event-log-tags
@@ -0,0 +1,346 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+# These are used for testing, do not modify without updating
+# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+42    answer (to life the universe etc|3)
+314   pi
+2718  e
+
+2719 configuration_changed (config mask|1|5)
+2720 sync (id|3),(event|1|5),(source|1|5)
+2721 cpu (total|1|6),(user|1|6),(system|1|6),(iowait|1|6),(irq|1|6),(softirq|1|6)
+2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)
+2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3)
+# This is logged when the device is being forced to sleep (typically by
+# the user pressing the power button).
+2724 power_sleep_requested (wakeLocksCleared|1|1)
+# This is logged when the screen on broadcast has completed
+2725 power_screen_broadcast_send (wakelockCount|1|1)
+# This is logged when the screen broadcast has completed
+2726 power_screen_broadcast_done (on|1|5),(broadcastDuration|2|3),(wakelockCount|1|1)
+# This is logged when the screen on broadcast has completed
+2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1)
+# This is logged when the screen is turned on or off.
+2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
+# This is logged when the partial wake lock (keeping the device awake
+# regardless of whether the screen is off) is acquired or released.
+2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
+# This is logged when battery goes from discharging to charging. 
+# It lets us count the total amount of time between charges and the discharge level
+2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6)
+#
+# Leave IDs through 2739 for more power logs 
+#
+
+# This event is logged when the location service uploads location data.
+2740 location_controller
+# This event is logged when someone is deciding to force a garbage collection
+2741 force_gc (reason|3)
+# This event is logged on each tickle
+2742 tickle (authority|3)
+# What happens in a sync operation (bytes sent and received, and
+# operation details)
+2743 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3)
+
+# The disk space free on the /data partition, in bytes
+2744 free_storage_changed (data|2|2)
+# Device low memory notification and disk space free on the /data partition, in bytes at that time
+2745 low_storage (data|2|2)
+# disk space free on the /data partition in bytes
+2746 free_storage_left (data|2|2)
+
+# when a NotificationManager.notify is called
+2750 notification_enqueue (pkg|3),(id|1|5),(notification|3)
+# when someone tries to cancel a notification, the notification manager sometimes
+# calls this with flags too
+2751 notification_cancel (pkg|3),(id|1|5),(required_flags|1)
+# when someone tries to cancel all of the notifications for a particular package
+2752 notification_cancel_all (pkg|3),(required_flags|1)
+
+# This event is logged when GTalkService encounters important events
+2800 gtalkservice (eventType|1)
+# This event is logged for GTalk connection state changes. The status field is an int, but
+# it really contains 4 separate values, each taking up a byte
+# (eventType, connection state, connection error, network state)
+2801 gtalk_connection (status|1)
+
+2802 watchdog (Service|3)
+2803 watchdog_proc_pss (Process|3),(Pid|1|5),(Pss|1|2)
+2804 watchdog_soft_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2),(Skip|3)
+2805 watchdog_hard_reset (Process|3),(Pid|1|5),(MaxPss|1|2),(Pss|1|2)
+2806 watchdog_pss_stats (EmptyPss|1|2),(EmptyCount|1|1),(BackgroundPss|1|2),(BackgroundCount|1|1),(ServicePss|1|2),(ServiceCount|1|1),(VisiblePss|1|2),(VisibleCount|1|1),(ForegroundPss|1|2),(ForegroundCount|1|1),(NoPssCount|1|1)
+2807 watchdog_proc_stats (DeathsInOne|1|1),(DeathsInTwo|1|1),(DeathsInThree|1|1),(DeathsInFour|1|1),(DeathsInFive|1|1)
+2808 watchdog_scheduled_reboot (Now|2|1),(Interval|1|3),(StartTime|1|3),(Window|1|3),(Skip|3)
+2809 watchdog_meminfo (MemFree|1|2),(Buffers|1|2),(Cached|1|2),(Active|1|2),(Inactive|1|2),(AnonPages|1|2),(Mapped|1|2),(Slab|1|2),(SReclaimable|1|2),(SUnreclaim|1|2),(PageTables|1|2)
+2810 watchdog_vmstat (runtime|2|3),(pgfree|1|1),(pgactivate|1|1),(pgdeactivate|1|1),(pgfault|1|1),(pgmajfault|1|1)
+2811 watchdog_requested_reboot (NoWait|1|1),(ScheduleInterval|1|3),(RecheckInterval|1|3),(StartTime|1|3),(Window|1|3),(MinScreenOff|1|3),(MinNextAlarm|1|3)
+
+# Device boot timings.  We include monotonic clock values because the
+# intrinsic event log times are wall-clock.
+#
+# Runtime starts:
+3000 boot_progress_start (time|2|3)
+# SystemServer.run() starts:
+3010 boot_progress_system_run (time|2|3)
+# ZygoteInit class preloading starts:
+3020 boot_progress_preload_start (time|2|3)
+# ZygoteInit class preloading ends:
+3030 boot_progress_preload_end (time|2|3)
+# ActivityManagerService.systemReady() starts:
+3040 boot_progress_ams_ready (time|2|3)
+# ActivityManagerService calls enableScreenAfterBoot():
+3050 boot_progress_enable_screen (time|2|3)
+# Package Manager starts:
+3060 boot_progress_pms_start (time|2|3)
+# Package Manager .apk scan starts:
+3070 boot_progress_pms_system_scan_start (time|2|3)
+# Package Manager .apk scan starts:
+3080 boot_progress_pms_data_scan_start (time|2|3)
+# Package Manager .apk scan ends:
+3090 boot_progress_pms_scan_end (time|2|3)
+# Package Manager ready:
+3100 boot_progress_pms_ready (time|2|3)
+# + check activity_launch_time for Home app
+
+# Do not change these names without updating the checkin_events setting in
+# google3/googledata/wireless/android/provisioning/gservices.config !!
+#
+# An activity is being finished:
+30001 am_finish_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3)
+# A task is being brought to the front of the screen:
+30002 am_task_to_front (Task|1|5)
+# An existing activity is being given a new intent:
+30003 am_new_intent (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
+# A new task is being created:
+30004 am_create_task (Task ID|1|5)
+# A new activity is being created in an existing task:
+30005 am_create_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
+# An activity has been resumed into the foreground but was not already running:
+30006 am_restart_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+# An activity has been resumed and is now in the foreground:
+30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+# Application Not Responding
+30008 anr (pid|1|5),(Package Name|3),(reason|3)
+# Activity launch time
+30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3)
+# Application process bound to work
+30010 am_proc_bound (PID|1|5),(Process Name|3)
+# Application process died
+30011 am_proc_died (PID|1|5),(Process Name|3)
+# The Activity Manager failed to pause the given activity. 
+30012 am_failed_to_pause (Token|1|5),(Wanting to pause|3),(Currently pausing|3)
+# Attempting to pause the current activity 
+30013 am_pause_activity (Token|1|5),(Component Name|3)
+# Application process has been started
+30014 am_proc_start (PID|1|5),(UID|1|5),(Process Name|3),(Type|3),(Component|3)
+# An application process has been marked as bad
+30015 am_proc_bad (UID|1|5),(Process Name|3)
+# An application process that was bad is now marked as good
+30016 am_proc_good (UID|1|5),(Process Name|3)
+# Reporting to applications that memory is low
+30017 am_low_memory (Num Processes|1|1)
+# An activity is being destroyed:
+30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+# An activity has been relaunched, resumed, and is now in the foreground:
+30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+# An activity has been relaunched:
+30020 am_relaunch_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+# The activity's onPause has been called.
+30021 am_on_paused_called (Component Name|3)
+# The activity's onResume has been called.
+30022 am_on_resume_called (Component Name|3)
+# Kill a process to reclaim memory.
+30023 am_kill_for_memory (PID|1|5),(Process Name|3),(OomAdj|1|5)
+# Discard an undelivered serialized broadcast (timeout/ANR/crash)
+30024 am_broadcast_discard_filter (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
+30025 am_broadcast_discard_app (Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
+# A service is being created
+30030 am_create_service (Service Record|1|5),(Name|3),(Intent|3),(PID|1|5)
+# A service is being destroyed
+30031 am_destroy_service (Service Record|1|5),(Name|3),(PID|1|5)
+# A process has crashed too many times, it is being cleared
+30032 am_process_crashed_too_much (Name|3),(PID|1|5)
+# An unknown process is trying to attach to the activity manager
+30033 am_drop_process (PID|1|5)
+# A service has crashed too many times, it is being stopped
+30034 am_service_crashed_too_much (Crash Count|1|1),(Component Name|3),(PID|1|5)
+# A service is going to be restarted after its process went away
+30035 am_schedule_service_restart (Component Name|3),(Time|2|3)
+# A client was waiting for a content provider, but its process was lost
+30036 am_provider_lost_process (Package Name|3),(UID|1|5),(Name|3)
+
+# Out of memory for surfaces.
+31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
+
+# Re-connecting to input method service because we haven't received its interface
+32000 imf_force_reconnect_ime (IME|4),(Time Since Connect|2|3),(Showing|1|1)
+
+# dvm_gc_info: LIST (LONG, LONG, LONG)
+#
+# First LONG:
+#
+#    [63]    1
+#    [62-24] ASCII process identifier
+#    [23-12] GC time in ms
+#    [11- 0] Bytes freed
+#
+# Second LONG (aggregated heap info):
+#
+#    [63-62] 10
+#    [61-60] Reserved; must be zero
+#    [59-48] Objects freed
+#    [47-36] Actual size (current footprint)
+#    [35-24] Allowed size (current hard max)
+#    [23-12] Objects allocated
+#    [11- 0] Bytes allocated
+#
+# Third LONG (zygote heap info):
+#
+#    [63-62] 11
+#    [61-60] Reserved; must be zero
+#    [59-48] Soft limit
+#    [47-36] Actual size (current footprint)
+#    [35-24] Allowed size (current hard max)
+#    [23-12] Objects allocated
+#    [11- 0] Bytes allocated
+#
+# Fourth LONG:
+#
+#    [63-48] Reserved; must be zero
+#    [47-36] dlmallocFootprint
+#    [35-24] mallinfo: total allocated space
+#    [23-12] External byte limit
+#    [11- 0] External bytes allocated
+#
+# See HeapDebug.c
+#
+20001 dvm_gc_info (custom|2),(custom|2),(custom|2),(custom|2)
+20002 dvm_gc_madvise_info (total|1|2),(zygote|1|2)
+
+75000 sqlite_mem_alarm_current (current|1|2)
+75001 sqlite_mem_alarm_max (max|1|2)
+75002 sqlite_mem_alarm_alloc_attempt (attempts|1|4)
+75003 sqlite_mem_released (Memory released|1|2)
+
+40000 checkin (Check in time|2|3)
+
+50000 menu_item_selected (Menu type where 0 is options and 1 is context|1|5),(Menu item title|3)
+50001 menu_opened (Menu type where 0 is options and 1 is context|1|5)
+# Connectivity state changed:
+# [31-13] Reserved for future use
+# [12- 9] Network subtype (for mobile network, as defined by TelephonyManager)
+# [ 8- 3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)   
+# [ 2- 0] Network type (as defined by ConnectivityManager)
+50020 connectivity_state_changed (custom|1|5)
+
+# Wi-Fi network state changed:
+# [31- 6] Reserved for future use
+# [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)   
+50021 wifi_network_state_changed (network_state|1|5)
+
+# Wi-Fi supplicant state changed:
+# [31- 6] Reserved for future use
+# [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)   
+50022 wifi_supplicant_state_changed (supplicant_state|1|5)
+
+# Wi-Fi driver state changed:
+# [31- 1] Reserved for future use
+# [ 0- 0] Driver start (1) or stopped (0)   
+50023 wifi_driver_state_changed (driver_state|1|5)
+
+# Wi-Fi interface configuration state changed:
+# [31- 1] Reserved for future use
+# [ 0- 0] Interface configuration succeeded (1) or failed (0)   
+50024 wifi_interface_configuration_state_changed (IP_configuration|1|5)
+
+# Wi-Fi supplicant connection state changed:
+# [31- 2] Reserved for future use
+# [ 1- 0] Connected to supplicant (1) or disconnected from supplicant (0),
+#         or supplicant died (2)
+50025 wifi_supplicant_connection_state_changed (connected|1|5)
+
+# PDP Context has a bad DNS address
+50100 pdp_bad_dns_address (dns_address|3)
+
+# For data connection on PDP context, reached the data-out-without-data-in
+# packet count that triggers a countdown to radio restart
+50101 pdp_radio_reset_countdown_triggered (out_packet_count|1|1)
+
+# Radio restart - timed out with no incoming packets.
+50102 pdp_radio_reset (out_packet_count|1|1)
+
+# PDP context reset - timed out with no incoming packets.
+50103 pdp_context_reset (out_packet_count|1|1)
+
+# Reregister to data network - timed out with no incoming packets.
+50104 pdp_reregister_network (out_packet_count|1|1)
+
+# PDP Setup failures
+50105 pdp_setup_fail (cause|1|5), (cid|1|5), (network_type|1|5)
+
+# Call drops
+50106 call_drop (cause|1|5), (cid|1|5), (network_type|1|5)
+
+# Data network registration failed after successful voice registration
+50107 data_network_registration_fail (op_numeric|1|5), (cid|1|5)
+
+# Suspicious status of data connection while radio poweroff
+50108 data_network_status_on_radio_off (dc_state|3), (enable|1|5)
+
+# PDP drop caused by network
+50109 pdp_network_drop (cid|1|5), (network_type|1|5)
+
+# Do not change these names without updating tag in:
+#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c
+51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5)
+
+# db stats.  0 is query, 1 is write (may become more fine grained in the 
+# future)
+52000 db_operation (name|3),(op_type|1|5),(time|2|3)
+
+# http request/response stats
+52001 http_stats (useragent|3),(response|2|3),(processing|2|3),(tx|1|2),(rx|1|2)
+60000 viewroot_draw (Draw time|1|3)
+60001 viewroot_layout (Layout time|1|3)
+60002 view_build_drawing_cache (View created drawing cache|1|5)
+60003 view_use_drawing_cache (View drawn using bitmap cache|1|5)
+
+# 0 for screen off, 1 for screen on, 2 for key-guard done
+70000 screen_toggled (screen_state|1|5)
+
+# browser stats for diary study
+70101 browser_zoom_level_change (start level|1|5),(end level|1|5),(time|2|3)
+70102 browser_double_tap_duration (duration|1|3),(time|2|3)
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
new file mode 100644
index 0000000..3130a1c
--- /dev/null
+++ b/logcat/logcat.cpp
@@ -0,0 +1,568 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <cutils/logger.h>
+#include <cutils/logd.h>
+#include <cutils/sockets.h>
+#include <cutils/logprint.h>
+#include <cutils/event_tag_map.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
+#define DEFAULT_MAX_ROTATED_LOGS 4
+
+static AndroidLogFormat * g_logformat;
+
+/* logd prefixes records with a length field */
+#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
+
+#define LOG_FILE_DIR    "/dev/log/"
+
+
+namespace android {
+
+/* Global Variables */
+
+static const char * g_outputFileName = NULL;
+static int g_logRotateSizeKBytes = 0;                   // 0 means "no log rotation"
+static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
+static int g_outFD = -1;
+static off_t g_outByteCount = 0;
+static int g_isBinary = 0;
+static int g_printBinary = 0;
+
+static EventTagMap* g_eventTagMap = NULL;
+
+static int openLogFile (const char *pathname)
+{
+    return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+}
+
+static void rotateLogs()
+{
+    int err;
+
+    // Can't rotate logs if we're not outputting to a file
+    if (g_outputFileName == NULL) {
+        return;
+    }
+
+    close(g_outFD);
+
+    for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
+        char *file0, *file1;
+
+        asprintf(&file1, "%s.%d", g_outputFileName, i);
+
+        if (i - 1 == 0) {
+            asprintf(&file0, "%s", g_outputFileName);
+        } else {
+            asprintf(&file0, "%s.%d", g_outputFileName, i - 1);
+        }
+
+        err = rename (file0, file1);
+
+        if (err < 0 && errno != ENOENT) {
+            perror("while rotating log files");
+        }
+
+        free(file1);
+        free(file0);
+    }
+
+    g_outFD = openLogFile (g_outputFileName);
+
+    if (g_outFD < 0) {
+        perror ("couldn't open output file");
+        exit(-1);
+    }
+
+    g_outByteCount = 0;
+
+}
+
+void printBinary(struct logger_entry *buf)
+{
+    size_t size = sizeof(logger_entry) + buf->len;
+    int ret;
+    
+    do {
+        ret = write(g_outFD, buf, size);
+    } while (ret < 0 && errno == EINTR);
+}
+
+static void processBuffer(struct logger_entry *buf)
+{
+    int bytesWritten;
+    int err;
+    AndroidLogEntry entry;
+    char binaryMsgBuf[1024];
+
+    if (g_isBinary) {
+        err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
+                binaryMsgBuf, sizeof(binaryMsgBuf));
+        //printf(">>> pri=%d len=%d msg='%s'\n",
+        //    entry.priority, entry.messageLen, entry.message);
+    } else {
+        err = android_log_processLogBuffer(buf, &entry);
+    }
+    if (err < 0)
+        goto error;
+
+    bytesWritten = android_log_filterAndPrintLogLine(
+                        g_logformat, g_outFD, &entry);
+
+    if (bytesWritten < 0) {
+        perror("output error");
+        exit(-1);
+    }
+
+    g_outByteCount += bytesWritten;
+
+    if (g_logRotateSizeKBytes > 0 
+        && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
+    ) {
+        rotateLogs();
+    }
+
+error:
+    //fprintf (stderr, "Error processing record\n");
+    return;
+}
+
+static void readLogLines(int logfd)
+{
+    while (1) {
+        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
+        struct logger_entry *entry = (struct logger_entry *) buf;
+        int ret;
+
+        ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
+        if (ret < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EAGAIN)
+                break;
+            perror("logcat read");
+            exit(EXIT_FAILURE);
+        }
+        else if (!ret) {
+            fprintf(stderr, "read: Unexpected EOF!\n");
+            exit(EXIT_FAILURE);
+        }
+
+        /* NOTE: driver guarantees we read exactly one full entry */
+
+        entry->msg[entry->len] = '\0';
+
+        if (g_printBinary) {
+            printBinary(entry);
+        } else {
+            (void) processBuffer(entry);
+        }
+    }
+}
+
+static int clearLog(int logfd)
+{
+    return ioctl(logfd, LOGGER_FLUSH_LOG);
+}
+
+/* returns the total size of the log's ring buffer */
+static int getLogSize(int logfd)
+{
+    return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
+}
+
+/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
+static int getLogReadableSize(int logfd)
+{
+    return ioctl(logfd, LOGGER_GET_LOG_LEN);
+}
+
+static void setupOutput()
+{
+
+    if (g_outputFileName == NULL) {
+        g_outFD = STDOUT_FILENO;
+
+    } else {
+        struct stat statbuf;
+
+        g_outFD = openLogFile (g_outputFileName);
+
+        if (g_outFD < 0) {
+            perror ("couldn't open output file");
+            exit(-1);
+        }
+
+        fstat(g_outFD, &statbuf);
+
+        g_outByteCount = statbuf.st_size;
+    }
+}
+
+static void show_help(const char *cmd)
+{
+    fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
+
+    fprintf(stderr, "options include:\n"
+                    "  -s              Set default filter to silent.\n"
+                    "                  Like specifying filterspec '*:s'\n"
+                    "  -f <filename>   Log to file. Default to stdout\n"
+                    "  -r [<kbytes>]   Rotate log every kbytes. (16 if unspecified). Requires -f\n"
+                    "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
+                    "  -v <format>     Sets the log print format, where <format> is one of:\n\n"
+                    "                  brief process tag thread raw time threadtime long\n\n"
+                    "  -c              clear (flush) the entire log and exit\n"
+                    "  -d              dump the log and then exit (don't block)\n"
+                    "  -g              get the size of the log's ring buffer and exit\n"
+                    "  -b <buffer>     request alternate ring buffer\n"
+                    "                  ('main' (default), 'radio', 'events')\n"
+                    "  -B              output the log in binary");
+
+
+    fprintf(stderr,"\nfilterspecs are a series of \n"
+                   "  <tag>[:priority]\n\n"
+                   "where <tag> is a log component tag (or * for all) and priority is:\n"
+                   "  V    Verbose\n"
+                   "  D    Debug\n"
+                   "  I    Info\n"
+                   "  W    Warn\n"
+                   "  E    Error\n"
+                   "  F    Fatal\n"
+                   "  S    Silent (supress all output)\n"
+                   "\n'*' means '*:d' and <tag> by itself means <tag>:v\n"
+                   "\nIf not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.\n"
+                   "If no filterspec is found, filter defaults to '*:I'\n"
+                   "\nIf not specified with -v, format is set from ANDROID_PRINTF_LOG\n"
+                   "or defaults to \"brief\"\n\n");
+
+
+
+}
+
+
+} /* namespace android */
+
+static int setLogFormat(const char * formatString)
+{
+    static AndroidLogPrintFormat format;
+
+    format = android_log_formatFromString(formatString);
+
+    if (format == FORMAT_OFF) {
+        // FORMAT_OFF means invalid string
+        return -1;
+    }
+
+    android_log_setPrintFormat(g_logformat, format);
+
+    return 0;
+}
+
+extern "C" void logprint_run_tests(void);
+
+int main (int argc, char **argv)
+{
+    int logfd;
+    int err;
+    int hasSetLogFormat = 0;
+    int clearLog = 0;
+    int getLogSize = 0;
+    int mode = O_RDONLY;
+    char *log_device = strdup("/dev/"LOGGER_LOG_MAIN);
+    const char *forceFilters = NULL;
+
+    g_logformat = android_log_format_new();
+
+    if (argc == 2 && 0 == strcmp(argv[1], "--test")) {
+        logprint_run_tests();
+        exit(0);
+    }
+
+    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+        android::show_help(argv[0]);
+        exit(0);
+    }
+
+    for (;;) {
+        int ret;
+
+        ret = getopt(argc, argv, "cdgsQf:r::n:v:b:B");
+
+        if (ret < 0) {
+            break;
+        }
+
+        switch(ret) {
+            case 's': 
+                // default to all silent
+                android_log_addFilterRule(g_logformat, "*:s");
+            break;
+
+            case 'c':
+                clearLog = 1;
+                mode = O_WRONLY;
+            break;
+
+            case 'd':
+                mode |= O_NONBLOCK;
+            break;
+
+            case 'g':
+                getLogSize = 1;
+            break;
+
+            case 'b':
+                free(log_device);
+                log_device =
+                    (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
+                strcpy(log_device, LOG_FILE_DIR);
+                strcat(log_device, optarg);
+
+                android::g_isBinary = (strcmp(optarg, "events") == 0);
+            break;
+
+            case 'B':
+                android::g_printBinary = 1;
+            break;
+
+            case 'f':
+                // redirect output to a file
+
+                android::g_outputFileName = optarg;
+
+            break;
+
+            case 'r':
+                if (optarg == NULL) {                
+                    android::g_logRotateSizeKBytes 
+                                = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
+                } else {
+                    long logRotateSize;
+                    char *lastDigit;
+
+                    if (!isdigit(optarg[0])) {
+                        fprintf(stderr,"Invalid parameter to -r\n");
+                        android::show_help(argv[0]);
+                        exit(-1);
+                    }
+                    android::g_logRotateSizeKBytes = atoi(optarg);
+                }
+            break;
+
+            case 'n':
+                if (!isdigit(optarg[0])) {
+                    fprintf(stderr,"Invalid parameter to -r\n");
+                    android::show_help(argv[0]);
+                    exit(-1);
+                }
+
+                android::g_maxRotatedLogs = atoi(optarg);
+            break;
+
+            case 'v':
+                err = setLogFormat (optarg);
+                if (err < 0) {
+                    fprintf(stderr,"Invalid parameter to -v\n");
+                    android::show_help(argv[0]);
+                    exit(-1);
+                }
+
+                hasSetLogFormat = 1;
+            break;
+
+            case 'Q':
+                /* this is a *hidden* option used to start a version of logcat                 */
+                /* in an emulated device only. it basically looks for androidboot.logcat=      */
+                /* on the kernel command line. If something is found, it extracts a log filter */
+                /* and uses it to run the program. If nothing is found, the program should     */
+                /* quit immediately                                                            */
+#define  KERNEL_OPTION  "androidboot.logcat="
+#define  CONSOLE_OPTION "androidboot.console="
+                {
+                    int          fd;
+                    char*        logcat;
+                    char*        console;
+                    int          force_exit = 1;
+                    static char  cmdline[1024];
+
+                    fd = open("/proc/cmdline", O_RDONLY);
+                    if (fd >= 0) {
+                        int  n = read(fd, cmdline, sizeof(cmdline)-1 );
+                        if (n < 0) n = 0;
+                        cmdline[n] = 0;
+                        close(fd);
+                    } else {
+                        cmdline[0] = 0;
+                    }
+
+                    logcat  = strstr( cmdline, KERNEL_OPTION );
+                    console = strstr( cmdline, CONSOLE_OPTION );
+                    if (logcat != NULL) {
+                        char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
+                        char*  q = strpbrk( p, " \t\n\r" );;
+
+                        if (q != NULL)
+                            *q = 0;
+
+                        forceFilters = p;
+                        force_exit   = 0;
+                    }
+                    /* if nothing found or invalid filters, exit quietly */
+                    if (force_exit)
+                        exit(0);
+
+                    /* redirect our output to the emulator console */
+                    if (console) {
+                        char*  p = console + sizeof(CONSOLE_OPTION)-1;
+                        char*  q = strpbrk( p, " \t\n\r" );
+                        char   devname[64];
+                        int    len;
+
+                        if (q != NULL) {
+                            len = q - p;
+                        } else
+                            len = strlen(p);
+
+                        len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
+                        fprintf(stderr, "logcat using %s (%d)\n", devname, len);
+                        if (len < (int)sizeof(devname)) {
+                            fd = open( devname, O_WRONLY );
+                            if (fd >= 0) {
+                                dup2(fd, 1);
+                                dup2(fd, 2);
+                                close(fd);
+                            }
+                        }
+                    }
+                }
+                break;
+
+            default:
+                fprintf(stderr,"Unrecognized Option\n");
+                android::show_help(argv[0]);
+                exit(-1);
+            break;
+        }
+    }
+
+    if (android::g_logRotateSizeKBytes != 0 
+        && android::g_outputFileName == NULL
+    ) {
+        fprintf(stderr,"-r requires -f as well\n");
+        android::show_help(argv[0]);
+        exit(-1);
+    }
+
+    android::setupOutput();
+
+    if (hasSetLogFormat == 0) {
+        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+
+        if (logFormat != NULL) {
+            err = setLogFormat(logFormat);
+
+            if (err < 0) {
+                fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", 
+                                    logFormat);
+            }
+        }
+    }
+
+    if (forceFilters) {
+        err = android_log_addFilterString(g_logformat, forceFilters);
+        if (err < 0) {
+            fprintf (stderr, "Invalid filter expression in -logcat option\n");
+            exit(0);
+        }
+    } else if (argc == optind) {
+        // Add from environment variable
+        char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
+
+        if (env_tags_orig != NULL) {
+            err = android_log_addFilterString(g_logformat, env_tags_orig);
+
+            if (err < 0) { 
+                fprintf(stderr, "Invalid filter expression in" 
+                                    " ANDROID_LOG_TAGS\n");
+                android::show_help(argv[0]);
+                exit(-1);
+            }
+        }
+    } else {
+        // Add from commandline
+        for (int i = optind ; i < argc ; i++) {
+            err = android_log_addFilterString(g_logformat, argv[i]);
+
+            if (err < 0) { 
+                fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
+                android::show_help(argv[0]);
+                exit(-1);
+            }
+        }
+    }
+
+    logfd = open(log_device, mode);
+    if (logfd < 0) {
+        fprintf(stderr, "Unable to open log device '%s': %s\n",
+            log_device, strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    if (clearLog) {
+        int ret;
+        ret = android::clearLog(logfd);
+        if (ret) {
+            perror("ioctl");
+            exit(EXIT_FAILURE);
+        }
+        return 0;
+    }
+
+    if (getLogSize) {
+        int size, readable;
+
+        size = android::getLogSize(logfd);
+        if (size < 0) {
+            perror("ioctl");
+            exit(EXIT_FAILURE);
+        }
+
+        readable = android::getLogReadableSize(logfd);
+        if (readable < 0) {
+            perror("ioctl");
+            exit(EXIT_FAILURE);
+        }
+
+        printf("ring buffer is %dKb (%dKb consumed), "
+               "max entry is %db, max payload is %db\n",
+               size / 1024, readable / 1024,
+               (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
+        return 0;
+    }
+
+    //LOG_EVENT_INT(10, 12345);
+    //LOG_EVENT_LONG(11, 0x1122334455667788LL);
+    //LOG_EVENT_STRING(0, "whassup, doc?");
+
+    if (android::g_isBinary)
+        android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
+
+    android::readLogLines(logfd);
+
+    return 0;
+}
diff --git a/logwrapper/Android.mk b/logwrapper/Android.mk
new file mode 100644
index 0000000..5fd6356
--- /dev/null
+++ b/logwrapper/Android.mk
@@ -0,0 +1,7 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= logwrapper.c
+LOCAL_MODULE := logwrapper
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_EXECUTABLE)
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c
new file mode 100644
index 0000000..f00bfbf
--- /dev/null
+++ b/logwrapper/logwrapper.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+
+void fatal(const char *msg) {
+    fprintf(stderr, msg);
+    LOG(LOG_ERROR, "logwrapper", msg);
+    exit(-1);
+}
+
+void usage() {
+    fatal(
+        "Usage: logwrapper [-x] BINARY [ARGS ...]\n"
+        "\n"
+        "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+        "the Android logging system. Tag is set to BINARY, priority is\n"
+        "always LOG_INFO.\n"
+        "\n"
+        "-x: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+        "    fault address is set to the status of wait()\n");
+}
+
+void parent(const char *tag, int seg_fault_on_exit, int parent_read) {
+    int status;
+    char buffer[4096];
+
+    int a = 0;  // start index of unprocessed data
+    int b = 0;  // end index of unprocessed data
+    int sz;
+    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
+
+        sz += b;
+        // Log one line at a time
+        for (b = 0; b < sz; b++) {
+            if (buffer[b] == '\r') {
+                buffer[b] = '\0';
+            } else if (buffer[b] == '\n') {
+                buffer[b] = '\0';
+                LOG(LOG_INFO, tag, &buffer[a]);
+                a = b + 1;
+            }
+        }
+
+        if (a == 0 && b == sizeof(buffer) - 1) {
+            // buffer is full, flush
+            buffer[b] = '\0';
+            LOG(LOG_INFO, tag, &buffer[a]);
+            b = 0;
+        } else if (a != b) {
+            // Keep left-overs
+            b -= a;
+            memmove(buffer, &buffer[a], b);
+            a = 0;
+        } else {
+            a = 0;
+            b = 0;
+        }
+
+    }
+    // Flush remaining data
+    if (a != b) {
+        buffer[b] = '\0';
+        LOG(LOG_INFO, tag, &buffer[a]);
+    }
+    status = 0xAAAA;
+    if (wait(&status) != -1) {  // Wait for child
+        if (WIFEXITED(status))
+            LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+                    WEXITSTATUS(status));
+        else if (WIFSIGNALED(status))
+            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+                    WTERMSIG(status));
+        else if (WIFSTOPPED(status))
+            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+                    WSTOPSIG(status));
+    } else
+        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+                strerror(errno), errno);
+    if (seg_fault_on_exit)
+        *(int *)status = 0;  // causes SIGSEGV with fault_address = status
+}
+
+void child(int argc, char* argv[]) {
+    // create null terminated argv_child array
+    char* argv_child[argc + 1];
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
+
+    if (execvp(argv_child[0], argv_child)) {
+        LOG(LOG_ERROR, "logwrapper",
+            "executing %s failed: %s\n", argv_child[0], strerror(errno));
+        exit(-1);
+    }
+}
+
+int main(int argc, char* argv[]) {
+    pid_t pid;
+    int seg_fault_on_exit = 0;
+
+    int parent_ptty;
+    int child_ptty;
+    char *child_devname = NULL;
+
+    if (argc < 2) {
+        usage();
+    }
+
+    if (strncmp(argv[1], "-d", 2) == 0) {
+        seg_fault_on_exit = 1;
+        argc--;
+        argv++;
+    }
+
+    if (argc < 2) {
+        usage();
+    }
+
+    /* Use ptty instead of socketpair so that STDOUT is not buffered */
+    parent_ptty = open("/dev/ptmx", O_RDWR);
+    if (parent_ptty < 0) {
+        fatal("Cannot create parent ptty\n");
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+        fatal("Problem with /dev/ptmx\n");
+    }
+
+    pid = fork();
+    if (pid < 0) {
+        fatal("Failed to fork\n");
+    } else if (pid == 0) {
+        child_ptty = open(child_devname, O_RDWR);
+        if (child_ptty < 0) {
+            fatal("Problem with child ptty\n");
+        }
+
+        // redirect stdout and stderr
+        close(parent_ptty);
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc - 1, &argv[1]);
+
+    } else {
+        // switch user and group to "log"
+        // this may fail if we are not root, 
+        // but in that case switching user/group is unnecessary 
+        setgid(AID_LOG);
+        setuid(AID_LOG);
+
+        parent(argv[1], seg_fault_on_exit, parent_ptty);
+    }
+
+    return 0;
+}
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
new file mode 100644
index 0000000..18f0ff3
--- /dev/null
+++ b/mkbootimg/Android.mk
@@ -0,0 +1,12 @@
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := mkbootimg.c
+LOCAL_STATIC_LIBRARIES := libmincrypt
+
+LOCAL_MODULE := mkbootimg
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
new file mode 100644
index 0000000..242ab35
--- /dev/null
+++ b/mkbootimg/bootimg.h
@@ -0,0 +1,97 @@
+/* tools/mkbootimg/bootimg.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#ifndef _BOOT_IMAGE_H_
+#define _BOOT_IMAGE_H_
+
+typedef struct boot_img_hdr boot_img_hdr;
+
+#define BOOT_MAGIC "ANDROID!"
+#define BOOT_MAGIC_SIZE 8
+#define BOOT_NAME_SIZE 16
+#define BOOT_ARGS_SIZE 512
+
+struct boot_img_hdr
+{
+    unsigned char magic[BOOT_MAGIC_SIZE];
+
+    unsigned kernel_size;  /* size in bytes */
+    unsigned kernel_addr;  /* physical load addr */
+
+    unsigned ramdisk_size; /* size in bytes */
+    unsigned ramdisk_addr; /* physical load addr */
+
+    unsigned second_size;  /* size in bytes */
+    unsigned second_addr;  /* physical load addr */
+
+    unsigned tags_addr;    /* physical addr for kernel tags */
+    unsigned page_size;    /* flash page size we assume */
+    unsigned unused[2];    /* future expansion: should be 0 */
+
+    unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
+    
+    unsigned char cmdline[BOOT_ARGS_SIZE];
+
+    unsigned id[8]; /* timestamp / checksum / sha1 / etc */
+};
+
+/*
+** +-----------------+ 
+** | boot header     | 1 page
+** +-----------------+
+** | kernel          | n pages  
+** +-----------------+
+** | ramdisk         | m pages  
+** +-----------------+
+** | second stage    | o pages
+** +-----------------+
+**
+** n = (kernel_size + page_size - 1) / page_size
+** m = (ramdisk_size + page_size - 1) / page_size
+** o = (second_size + page_size - 1) / page_size
+**
+** 0. all entities are page_size aligned in flash
+** 1. kernel and ramdisk are required (size != 0)
+** 2. second is optional (second_size == 0 -> no second)
+** 3. load each element (kernel, ramdisk, second) at
+**    the specified physical address (kernel_addr, etc)
+** 4. prepare tags at tag_addr.  kernel_args[] is
+**    appended to the kernel commandline in the tags.
+** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+** 6. if second_size != 0: jump to second_addr
+**    else: jump to kernel_addr
+*/
+
+#if 0
+typedef struct ptentry ptentry;
+
+struct ptentry {
+    char name[16];      /* asciiz partition name    */
+    unsigned start;     /* starting block number    */
+    unsigned length;    /* length in blocks         */
+    unsigned flags;     /* set to zero              */
+};
+
+/* MSM Partition Table ATAG
+**
+** length: 2 + 7 * n
+** atag:   0x4d534d70
+**         <ptentry> x n
+*/
+#endif
+
+#endif
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
new file mode 100644
index 0000000..d803cf6
--- /dev/null
+++ b/mkbootimg/mkbootimg.c
@@ -0,0 +1,251 @@
+/* tools/mkbootimg/mkbootimg.c
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "mincrypt/sha.h"
+#include "bootimg.h"
+
+static void *load_file(const char *fn, unsigned *_sz)
+{
+    char *data;
+    int sz;
+    int fd;
+
+    data = 0;
+    fd = open(fn, O_RDONLY);
+    if(fd < 0) return 0;
+
+    sz = lseek(fd, 0, SEEK_END);
+    if(sz < 0) goto oops;
+
+    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+    data = (char*) malloc(sz);
+    if(data == 0) goto oops;
+
+    if(read(fd, data, sz) != sz) goto oops;
+    close(fd);
+
+    if(_sz) *_sz = sz;
+    return data;
+
+oops:
+    close(fd);
+    if(data != 0) free(data);
+    return 0;
+}
+
+int usage(void)
+{
+    fprintf(stderr,"usage: mkbootimg\n"
+            "       --kernel <filename>\n"
+            "       --ramdisk <filename>\n"
+            "       [ --second <2ndbootloader-filename> ]\n"
+            "       [ --cmdline <kernel-commandline> ]\n"
+            "       [ --board <boardname> ]\n"
+            "       -o|--output <filename>\n"
+            );
+    return 1;
+}
+
+
+
+static unsigned char padding[2048] = { 0, };
+
+int write_padding(int fd, unsigned pagesize, unsigned itemsize)
+{
+    unsigned pagemask = pagesize - 1;
+    unsigned count;
+
+    if((itemsize & pagemask) == 0) {
+        return 0;
+    }
+
+    count = pagesize - (itemsize & pagemask);
+
+    if(write(fd, padding, count) != count) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+int main(int argc, char **argv)
+{
+    boot_img_hdr hdr;
+
+    char *kernel_fn = 0;
+    void *kernel_data = 0;
+    char *ramdisk_fn = 0;
+    void *ramdisk_data = 0;
+    char *second_fn = 0;
+    void *second_data = 0;
+    char *cmdline = "";
+    char *bootimg = 0;
+    char *board = "";
+    unsigned pagesize = 2048;
+    unsigned saddr = 0;
+    int fd;
+    SHA_CTX ctx;
+    uint8_t* sha;
+
+    argc--;
+    argv++;
+
+    memset(&hdr, 0, sizeof(hdr));
+
+    while(argc > 0){
+        char *arg = argv[0];
+        char *val = argv[1];
+        if(argc < 2) {
+            return usage();
+        }
+        argc -= 2;
+        argv += 2;
+        if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
+            bootimg = val;
+        } else if(!strcmp(arg, "--kernel")) {
+            kernel_fn = val;
+        } else if(!strcmp(arg, "--ramdisk")) {
+            ramdisk_fn = val;
+        } else if(!strcmp(arg, "--second")) {
+            second_fn = val;
+        } else if(!strcmp(arg, "--cmdline")) {
+            cmdline = val;
+        } else if(!strcmp(arg, "--saddr")) {
+            saddr = strtoul(val, 0, 16);
+        } else if(!strcmp(arg, "--board")) {
+            board = val;
+        } else {
+            return usage();
+        }
+    }
+
+    if(bootimg == 0) {
+        fprintf(stderr,"error: no output filename specified\n");
+        return usage();
+    }
+
+    if(kernel_fn == 0) {
+        fprintf(stderr,"error: no kernel image specified\n");
+        return usage();
+    }
+
+    if(ramdisk_fn == 0) {
+        fprintf(stderr,"error: no ramdisk image specified\n");
+        return usage();
+    }
+
+    if(strlen(board) >= BOOT_NAME_SIZE) {
+        fprintf(stderr,"error: board name too large\n");
+        return usage();
+    }
+
+    strcpy(hdr.name, board);
+
+    hdr.kernel_addr =  0x10008000;
+    hdr.ramdisk_addr = 0x11000000;
+    if(saddr) {
+        hdr.second_addr =  0x00300000;
+    } else {
+        hdr.second_addr =  0x10F00000;
+    }
+    hdr.tags_addr   =  0x10000100;
+    hdr.page_size = pagesize;
+
+    memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
+
+    if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) {
+        fprintf(stderr,"error: kernel commandline too large\n");
+        return 1;
+    }
+    strcpy((char*)hdr.cmdline, cmdline);
+
+    kernel_data = load_file(kernel_fn, &hdr.kernel_size);
+    if(kernel_data == 0) {
+        fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn);
+        return 1;
+    }
+
+    if(!strcmp(ramdisk_fn,"NONE")) {
+        ramdisk_data = 0;
+        hdr.ramdisk_size = 0;
+    } else {
+        ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size);
+        if(ramdisk_data == 0) {
+            fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn);
+            return 1;
+        }
+    }
+
+    if(second_fn) {
+        second_data = load_file(second_fn, &hdr.second_size);
+        if(second_data == 0) {
+            fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn);
+            return 1;
+        }
+    }
+
+    /* put a hash of the contents in the header so boot images can be
+     * differentiated based on their first 2k.
+     */
+    SHA_init(&ctx);
+    SHA_update(&ctx, kernel_data, hdr.kernel_size);
+    SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size));
+    SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size);
+    SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size));
+    SHA_update(&ctx, second_data, hdr.second_size);
+    SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size));
+    sha = SHA_final(&ctx);
+    memcpy(hdr.id, sha,
+           SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE);
+
+    fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+    if(fd < 0) {
+        fprintf(stderr,"error: could not create '%s'\n", bootimg);
+        return 1;
+    }
+
+    if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail;
+    if(write_padding(fd, pagesize, sizeof(hdr))) goto fail;
+
+    if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail;
+    if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail;
+
+    if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail;
+    if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
+
+    if(second_data) {
+        if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail;
+        if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
+    }
+
+    return 0;
+
+fail:
+    unlink(bootimg);
+    close(fd);
+    fprintf(stderr,"error: failed writing '%s': %s\n", bootimg,
+            strerror(errno));
+    return 1;
+}
diff --git a/mountd/ASEC.c b/mountd/ASEC.c
new file mode 100644
index 0000000..a6aab9c
--- /dev/null
+++ b/mountd/ASEC.c
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+** Android Secure External Cache 
+*/
+
+#include "mountd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <linux/dm-ioctl.h>
+#include <linux/loop.h>
+
+#include <cutils/properties.h>
+#include <cutils/misc.h>
+
+#include "ASEC.h"
+
+//#define MODULE_FAILURE_IS_FATAL
+
+extern int init_module(void *, unsigned long, const char *);
+extern int delete_module(const char *, unsigned int);
+
+struct asec_context
+{
+    char *name;           // Device mapper volume name
+    char *srcPath;        // Path to the source (original) mount
+    char *backingFile;    // Name of the image file
+    unsigned int sectors; // Number of sectors
+    char *dstPath;        // Destination mount point
+    char *crypt;          // Crypt options
+
+    boolean needs_format;
+    boolean started;
+    int cacheFd;
+    int lo_num;
+    int dm_num;
+    unsigned char key[16];
+};
+
+static const char *MODULES[] = { "dm_mod", "crypto", "crypto_algapi", "crypto_blkcipher", 
+                                 "cryptomgr", "dm_crypt", "jbd",  
+                                 "twofish_common", "twofish", "cbc",
+                                 "mbcache", "ext3",
+                                 NULL };
+static const char KEY_PATH[] = "/data/system/asec.key";
+static const char MODULE_PATH[] = "/system/lib/modules";
+static const char MKE2FS_PATH[] = "/system/bin/mke2fs";
+static const char E2FSCK_PATH[] = "/system/bin/e2fsck";
+
+boolean AsecIsStarted(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    return ctx->started;
+}
+
+const char *AsecMountPoint(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    return ctx->dstPath;
+}
+
+static boolean AsecIsEnabled()
+{
+    char value[PROPERTY_VALUE_MAX];
+    int  enabled;
+
+    property_get(ASEC_ENABLED, value, "0");
+
+    if (atoi(value) == 1)
+        return true;
+    return false;
+}
+
+void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
+               const char *Size, const char *DstPath, const char *Crypt)
+{
+    struct asec_context *ctx;
+
+    if (!AsecIsEnabled())
+        return NULL;
+
+    LOG_ASEC("AsecInit(%s, %s, %s, %s, %s, %s):\n",
+             Name, SrcPath, BackingFile, Size, DstPath, Crypt);
+
+    if (!Name || !SrcPath || !BackingFile || !Size || !DstPath || !Crypt) {
+        LOG_ERROR("AsecInit(): Invalid arguments\n");
+        return NULL;
+    }
+
+    if (!(ctx = malloc(sizeof(struct asec_context)))) {
+        LOG_ERROR("AsecInit(): Out of memory\n");
+        return NULL;
+    }
+
+    memset(ctx, 0, sizeof(struct asec_context));
+    ctx->name = strdup(Name);
+    ctx->srcPath = strdup(SrcPath);
+    ctx->backingFile = strdup(BackingFile);
+    ctx->sectors = atoi(Size);
+    ctx->dstPath = strdup(DstPath);
+    ctx->crypt = strdup(Crypt);
+    return ctx;
+}
+
+void AsecDeinit(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+
+    free(ctx->name);
+    free(ctx->srcPath);
+    free(ctx->backingFile);
+    free(ctx->dstPath);
+    free(ctx->crypt);
+
+    free(ctx);
+}
+
+static int AsecLoadModules()
+{
+    int i;
+
+    for (i = 0; MODULES[i] != NULL; i++) {
+	const char *moduleName = MODULES[i];
+        char moduleFile[255];
+        int rc = 0;
+        void *module;
+        unsigned int size;
+
+        sprintf(moduleFile, "%s/%s.ko", MODULE_PATH, moduleName);
+        module = load_file(moduleFile, &size);
+        if (!module) {
+            LOG_ERROR("Failed to load module %s (%d)\n", moduleFile, errno);
+            return -1;
+        }
+
+        rc = init_module(module, size, "");
+        free(module);
+        if (rc && errno != EEXIST) {
+            LOG_ERROR("Failed to init module %s (%d)\n", moduleFile, errno);
+            return -errno;
+        }
+    }
+    return 0;
+}
+
+static int AsecUnloadModules()
+{
+    int i, j, rc;
+
+    for (i = 0; MODULES[i] != NULL; i++);
+
+    for (j = (i - 1); j >= 0; j--) {
+	const char *moduleName = MODULES[j];
+        int maxretry = 10;
+        while(maxretry-- > 0) {
+            rc = delete_module(moduleName, O_NONBLOCK | O_EXCL);
+            if (rc < 0 && errno == EAGAIN)
+                usleep(500000);
+            else
+                break;
+        }
+        if (rc != 0) {
+            LOG_ERROR("Failed to unload module %s\n", moduleName);
+            return -errno;
+        }
+    }
+    return 0;
+}
+
+static int AsecGenerateKey(struct asec_context *ctx)
+{
+    LOG_ASEC("AsecGenerateKey():\n");
+
+    memset((void *) ctx->key, 0x69, sizeof(ctx->key));
+    return 0;
+}
+
+static int AsecLoadGenerateKey(struct asec_context *ctx)
+{
+    int fd;
+    int rc = 0;
+
+    if ((fd = open(KEY_PATH, O_RDWR | O_CREAT, 0600)) < 0) {
+        LOG_ERROR("Error opening / creating keyfile (%d)\n", errno);
+        return -errno;
+    }
+
+    if (read(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
+        LOG_ASEC("Generating key\n");
+        if ((rc = AsecGenerateKey(ctx)) < 0) {
+            LOG_ERROR("Error generating key (%d)\n", rc);
+            goto out;
+        }
+        if (write(fd, ctx->key, sizeof(ctx->key)) != sizeof(ctx->key)) {
+            LOG_ERROR("Error writing keyfile (%d)\n", errno);
+            rc = -1;
+            goto out;
+        }
+    }
+    
+ out:
+    close (fd);
+    return rc;
+}
+
+static int AsecFormatFilesystem(struct asec_context *ctx)
+{
+    char cmdline[255];
+    int rc;
+
+    sprintf(cmdline,
+            "%s -b 4096 -m 1 -j -L \"%s\" /dev/block/dm-%d",
+            MKE2FS_PATH, ctx->name, ctx->dm_num);
+
+    LOG_ASEC("Formatting filesystem (%s)\n", cmdline);
+    // XXX: PROTECT FROM VIKING KILLER
+    if ((rc = system(cmdline)) < 0) {
+        LOG_ERROR("Error executing format command (%d)\n", errno);
+        return -errno;
+    }
+
+    rc = WEXITSTATUS(rc);
+
+    if (!rc) {
+        LOG_ASEC("Format completed\n");
+    } else {
+        LOG_ASEC("Format failed (%d)\n", rc);
+    }
+
+    return rc;
+}
+
+static int AsecCheckFilesystem(struct asec_context *ctx)
+{
+    char cmdline[255];
+    int rc;
+
+    sprintf(cmdline, "%s -p /dev/block/dm-%d", E2FSCK_PATH, ctx->dm_num);
+
+    LOG_ASEC("Checking filesystem (%s)\n", cmdline);
+    // XXX: PROTECT FROM VIKING KILLER
+    if ((rc = system(cmdline)) < 0) {
+        LOG_ERROR("Error executing check command (%d)\n", errno);
+        return -errno;
+    }
+
+    rc = WEXITSTATUS(rc);
+
+    if (rc == 0) {
+        LOG_ASEC("ASEC volume '%s' had no errors\n", ctx->name);
+    } else if (rc == 1) {
+        LOG_ASEC("ASEC volume '%s' had corrected errors\n", ctx->name);
+        rc = 0;
+    } else if (rc == 2) {
+        LOG_ERROR("ASEC volume '%s' had corrected errors (system should be rebooted)\n", ctx->name);
+    } else if (rc == 4) {
+        LOG_ERROR("ASEC volume '%s' had uncorrectable errors\n", ctx->name);
+    } else if (rc == 8) {
+        LOG_ERROR("Operational error while checking volume '%s'\n", ctx->name);
+    } else {
+        LOG_ERROR("Unknown e2fsck exit code (%d)\n", rc);
+    }
+    return rc;
+}
+
+static int AsecOpenCreateCache(struct asec_context *ctx)
+{
+    char filepath[255];
+
+    sprintf(filepath, "%s/%s", ctx->srcPath, ctx->backingFile);
+
+    if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
+        if (errno == ENOENT) {
+            int rc = 0;
+
+            LOG_ASEC("Creating cache file (%u sectors)\n", ctx->sectors);
+            if ((ctx->cacheFd = creat(filepath, 0600)) < 0) {
+                LOG_ERROR("Error creating cache (%d)\n", errno);
+                return -errno;
+            }
+            if (ftruncate(ctx->cacheFd, ctx->sectors * 512) < 0) {
+                LOG_ERROR("Error truncating cache (%d)\n", errno);
+                close(ctx->cacheFd);
+                unlink(filepath);
+                return -errno;
+            }
+            LOG_ASEC("Cache created (%u sectors) \n", ctx->sectors);
+            close(ctx->cacheFd); // creat() is WRONLY
+           
+            if ((ctx->cacheFd = open(filepath, O_RDWR)) < 0) {
+               LOG_ERROR("Error opening cache file (%d)\n", errno);
+                close(ctx->cacheFd);
+                unlink(filepath);
+                return -errno;
+            }
+
+            ctx->needs_format = 1;
+        } else
+            return -errno;
+    } else {
+        struct stat stat_buf;
+
+        if (fstat(ctx->cacheFd, &stat_buf) < 0) {
+            LOG_ERROR("Failed to fstat cache (%d)\n", errno);
+            close(ctx->cacheFd);
+            return -errno;
+        }
+        if (stat_buf.st_size != ctx->sectors * 512) {
+            LOG_ERROR("Cache size %lld != configured size %u\n",
+                      stat_buf.st_size, ctx->sectors * 512);
+        }
+
+        // XXX: Verify volume label matches ctx->name
+    }
+
+    return 0;
+}
+
+static void AsecCloseCache(struct asec_context *ctx)
+{
+    close(ctx->cacheFd);
+}
+
+static void *_align(void *ptr, unsigned int a)
+{
+        register unsigned long agn = --a;
+
+        return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+static struct dm_ioctl *_dm_ioctl_setup(struct asec_context *ctx, int flags)
+{
+    void *buffer;
+    void *p;
+    const size_t min_size = 16 * 1024;
+    size_t len = sizeof(struct dm_ioctl);
+    struct dm_ioctl *io;
+    struct dm_target_spec *tgt;
+    int i;
+    char params[1024];
+    char key[80];
+
+    key[0] = '\0';
+
+    for (i = 0; i < (int) sizeof(ctx->key); i++) {
+        char tmp[8];
+
+        sprintf(tmp, "%02x", ctx->key[i]);
+        strcat(key, tmp);
+    }
+
+    // XXX: Handle ctx->crypt 
+    sprintf(params, "twofish %s 0 /dev/block/loop%d 0", key, ctx->lo_num);
+ 
+    if (len < min_size)
+        len = min_size;
+
+    if (!(buffer = malloc(len))) {
+        LOG_ERROR("Unable to allocate memory\n");
+        return NULL;
+    }
+
+    memset(buffer, 0, len);
+    io = buffer;
+    tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+    
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+
+    io->data_size = len;
+    io->data_start = sizeof(struct dm_ioctl);
+
+    io->flags = flags;
+    io->dev = 0; 
+
+    io->target_count = 1;
+    io->event_nr = 1;
+    strncpy(io->name, ctx->name, sizeof(io->name));
+
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = ctx->sectors;
+    strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type));
+
+    p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+    strcpy((char *) p, params);
+    p+= strlen(params) + 1;
+
+    p = _align(p, 8);
+    tgt->next = p - buffer;
+
+    return io;
+}
+
+static int FindNextAvailableDm()
+{
+    int i;
+
+    for (i = 0; i < 8; i++) {
+        char path[255];
+        sprintf(path, "/dev/block/dm-%d", i);
+        if ((access(path, F_OK) < 0) && (errno == ENOENT))
+            return i;
+    }
+
+    LOG_ERROR("Out of device mapper numbers\n");
+    return -1;
+}
+
+static int AsecCreateDeviceMapping(struct asec_context *ctx)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    ctx->dm_num = FindNextAvailableDm();
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOG_ERROR("Error opening device mapper (%d)\n", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(ctx, 0))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        close(dmFd);
+        return -ENOMEM;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_CREATE, io)) < 0) {
+        LOG_ERROR("device-mapper create ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    } 
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(ctx, DM_STATUS_TABLE_FLAG))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+ 
+    if ((rc = ioctl(dmFd, DM_TABLE_LOAD, io)) < 0) {
+        LOG_ERROR("device-mapper load ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+ 
+    if (!(io = _dm_ioctl_setup(ctx, 0))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_SUSPEND, io)) < 0) {
+        LOG_ERROR("device-mapper resume ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int AsecDestroyDeviceMapping(struct asec_context *ctx)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOG_ERROR("Error opening device mapper (%d)\n", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(ctx, DM_PERSISTENT_DEV_FLAG))) {
+        LOG_ERROR("Unable to setup ioctl (out of memory)\n");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
+        LOG_ERROR("device-mapper remove ioctl failed (%d)\n", errno);
+        rc = -errno;
+        goto out_free;
+    } 
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int AsecMountCache(struct asec_context *ctx)
+{
+    int flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
+    char devname[255];
+
+    if (access(ctx->dstPath, R_OK)) {
+        LOG_ERROR("Destination mount point '%s' unavailable (%d)\n", ctx->dstPath, errno);
+        return -errno;
+    }
+
+    sprintf(devname, "/dev/block/dm-%d", ctx->dm_num);
+
+    if (mount(devname, ctx->dstPath, "ext3", flags, NULL)) {
+        LOG_ERROR("ASEC mount failed (%d)\n", errno);
+        return -errno;
+    }
+    
+    return 0;
+}
+
+static int AsecUnmountCache(struct asec_context *ctx)
+{
+    if (umount(ctx->dstPath)) {
+        if (errno == EBUSY) {
+            LOG_ASEC("ASEC volume '%s' still busy\n", ctx->name);
+        } else {
+            LOG_ERROR("ASEC umount failed (%d)\n", errno);
+        }
+        return -errno;
+    }
+    LOG_ASEC("ASEC volume '%s' unmounted\n", ctx->name);
+    return 0;
+}
+
+static int FindNextAvailableLoop()
+{
+    int i;
+
+    for (i = 0; i < MAX_LOOP; i++) {
+        struct loop_info info;
+        char devname[255];
+        int fd;
+
+        sprintf(devname, "/dev/block/loop%d", i);
+
+        if ((fd = open(devname, O_RDONLY)) < 0) {
+            LOG_ERROR("Unable to open %s (%d)\n", devname, errno);
+            return -errno;
+        }
+
+        if (ioctl(fd, LOOP_GET_STATUS, &info) < 0) {
+            close(fd);
+
+            if (errno == ENXIO)
+                return i;
+
+            LOG_ERROR("Unable to get loop status for %s (%d)\n", devname, errno);
+            return -errno;
+        }
+        close(fd);
+    }
+    return -ENXIO;
+}
+
+static int AsecCreateLoop(struct asec_context *ctx)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    ctx->lo_num = FindNextAvailableLoop();
+    if (ctx->lo_num < 0) {
+        LOG_ERROR("No loop devices available\n");
+        return -ENXIO;
+    }
+
+    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
+    device_fd = open(devname, O_RDWR);
+    if (device_fd < 0) {
+        LOG_ERROR("failed to open loop device (%d)\n", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_SET_FD, ctx->cacheFd) < 0) {
+        LOG_ERROR("loop_set_fd ioctl failed (%d)\n", errno);
+        rc = -errno;
+    }
+    close(device_fd);
+    return rc;
+}
+
+static int AsecDestroyLoop(struct asec_context *ctx)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    sprintf(devname, "/dev/block/loop%d", ctx->lo_num);
+    device_fd = open(devname, O_RDONLY);
+    if (device_fd < 0) {
+        LOG_ERROR("Failed to open loop (%d)\n", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
+        LOG_ERROR("Failed to destroy loop (%d)\n", errno);
+        rc = -errno;
+    }
+
+    close(device_fd);
+    return rc;
+}
+
+int AsecStart(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+    char value[PROPERTY_VALUE_MAX];
+    int rc = 0;
+
+    if (!ctx)
+        return -EINVAL;
+
+    if (ctx->started)
+        return -EBUSY;
+
+    LOG_ASEC("AsecStart(%s):\n", ctx->name);
+
+    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
+
+    if ((rc = AsecLoadModules()) < 0) {
+        LOG_ERROR("AsecStart: Failed to load kernel modules\n");
+#ifdef MODULE_FAILURE_IS_FATAL
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+#endif
+    }
+
+    if ((rc = AsecLoadGenerateKey(ctx))) {
+        LOG_ERROR("AsecStart: Failed to load / generate key\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+    
+    if ((rc = AsecOpenCreateCache(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to open / create cache\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecCreateLoop(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to create loop\n");
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	goto fail_closecache;
+    }
+
+    if ((rc = AsecCreateDeviceMapping(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to create devmapping (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+        goto fail_destroyloop;
+    }
+    
+    if (ctx->needs_format) {
+        if ((rc = AsecFormatFilesystem(ctx))) {
+            LOG_ERROR("AsecStart: Failed to format cache (%d)\n", rc);
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+            goto fail_destroydm;
+        }
+        ctx->needs_format = 0;
+    } else {
+        if ((rc = AsecCheckFilesystem(ctx))) {
+            LOG_ERROR("AsecStart: Failed to check filesystem (%d)\n", rc);
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+            goto fail_destroydm;
+        }
+    }
+
+    if ((rc = AsecMountCache(ctx)) < 0) {
+        LOG_ERROR("AsecStart: Failed to mount cache (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+        goto fail_destroydm;
+    }
+    
+    NotifyAsecState(ASEC_AVAILABLE, ctx->dstPath);
+    ctx->started = true;
+
+    return rc;
+
+ fail_destroydm:
+    AsecDestroyDeviceMapping(ctx);
+ fail_destroyloop:
+    AsecDestroyLoop(ctx);
+ fail_closecache:
+    AsecCloseCache(ctx);
+    return rc;
+}
+
+int AsecStop(void *Handle)
+{
+    struct asec_context *ctx = (struct asec_context *) Handle;
+    int rc = 0;
+
+    if (!ctx->started)
+        return -EINVAL;
+
+    LOG_ASEC("AsecStop(%s):\n", ctx->name);
+
+    NotifyAsecState(ASEC_BUSY, ctx->dstPath);
+
+    if ((rc = AsecUnmountCache(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to unmount cache (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecDestroyDeviceMapping(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to destroy devmapping (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    if ((rc = AsecDestroyLoop(ctx)) < 0) {
+        LOG_ERROR("AsecStop: Failed to destroy loop device (%d)\n", rc);
+        NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	return rc;
+    }
+
+    AsecCloseCache(ctx);
+ 
+    if ((rc = AsecUnloadModules()) < 0) {
+        if (rc == -EAGAIN) {
+            LOG_ASEC("AsecStop: Kernel modules still in use\n");
+        } else {
+            LOG_ERROR("AsecStop: Failed to unload kernel modules (%d)\n", rc);
+#ifdef MODULE_FAILURE_IS_FATAL
+            NotifyAsecState(ASEC_FAILED_INTERR, ctx->dstPath);
+	    return rc;
+#endif
+        }
+    }
+
+    ctx->started = false;
+    NotifyAsecState(ASEC_DISABLED, ctx->dstPath);
+    return rc;
+}
diff --git a/mountd/ASEC.h b/mountd/ASEC.h
new file mode 100644
index 0000000..c87b288
--- /dev/null
+++ b/mountd/ASEC.h
@@ -0,0 +1,66 @@
+#ifndef _ASEC_H
+#define _ASEC_H
+
+#define ASEC_STORES_MAX 4
+#define MAX_LOOP 8
+
+typedef enum AsecState {
+    // Feature disabled
+    ASEC_DISABLED,
+
+    // Feature enabled and operational
+    ASEC_AVAILABLE,
+
+    // Busy
+    ASEC_BUSY,
+
+    // Internal Error
+    ASEC_FAILED_INTERR,
+
+    // No media available
+    ASEC_FAILED_NOMEDIA,
+
+    // Media is corrupt
+    ASEC_FAILED_BADMEDIA,
+
+    // Key mismatch
+    ASEC_FAILED_BADKEY,
+} AsecState;
+
+/*
+ * ASEC commands
+ */
+#define ASEC_CMD_SEND_STATUS		"asec_send_status"
+#define ASEC_CMD_ENABLE			"asec_enable"
+#define ASEC_CMD_DISABLE		"asec_disable"
+
+/*
+ * ASEC events
+ */
+
+// These events correspond to the states in the AsecState enum.
+// A path to the ASEC mount point follows the colon
+#define ASEC_EVENT_DISABLED		"asec_disabled:"
+#define ASEC_EVENT_AVAILABLE		"asec_available:"
+#define ASEC_EVENT_BUSY			"asec_busy:"
+#define ASEC_EVENT_FAILED_INTERR	"asec_failed_interror:"
+#define ASEC_EVENT_FAILED_NOMEDIA	"asec_failed_nomedia"
+#define ASEC_EVENT_FAILED_BADMEDIA	"asec_failed_badmedia:"
+#define ASEC_EVENT_FAILED_BADKEY	"asec_failed_badkey:"
+
+/*
+ * System Properties
+ */
+
+#define ASEC_ENABLED			"asec.enabled"
+
+#define ASEC_STATUS			"ro.asec.status"
+#define ASEC_STATUS_DISABLED		"disabled"
+#define ASEC_STATUS_AVAILABLE		"available"
+#define ASEC_STATUS_BUSY			"busy"
+#define ASEC_STATUS_FAILED_INTERR	"internal_error"
+#define ASEC_STATUS_FAILED_NOMEDIA	"no_media"
+#define ASEC_STATUS_FAILED_BADMEDIA	"bad_media"
+#define ASEC_STATUS_FAILED_BADKEY	"bad_key"
+
+#endif
diff --git a/mountd/Android.mk b/mountd/Android.mk
new file mode 100644
index 0000000..16532fa
--- /dev/null
+++ b/mountd/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=        \
+    AutoMount.c          \
+    ProcessKiller.c      \
+    Server.c             \
+    mountd.c		 \
+    ASEC.c		 \
+    logwrapper.c
+
+LOCAL_MODULE:= mountd
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := -DCREATE_MOUNT_POINTS=0
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+# disabled - we are using vold now instead
+# include $(BUILD_EXECUTABLE)
diff --git a/mountd/AutoMount.c b/mountd/AutoMount.c
new file mode 100644
index 0000000..12ad572
--- /dev/null
+++ b/mountd/AutoMount.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+** mountd automount support
+*/
+
+#include "mountd.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <linux/loop.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/netlink.h>
+
+#define DEVPATH    "/dev/block/"
+#define DEVPATHLENGTH 11    // strlen(DEVPATH)
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+// timeout value for poll() when retries are pending
+#define POLL_TIMEOUT    1000
+
+#define MAX_MOUNT_RETRIES   3
+#define MAX_UNMOUNT_RETRIES   5
+
+typedef enum {
+    // device is unmounted
+    kUnmounted,
+    
+    // attempting to mount device
+    kMounting,
+    
+    // device is unmounted
+    kMounted,
+    
+    // attempting to unmount device
+    // so the media can be removed
+    kUnmountingForEject,
+    
+    // attempting to mount device
+    // so it can be shared via USB mass storage
+    kUnmountingForUms,
+} MountState;
+
+typedef struct MountPoint {
+    // block device to mount
+    const char* device;
+    
+    // mount point for device
+    const char* mountPoint;
+
+    // path to the UMS driver file for specifying the block device path
+    const char* driverStorePath;
+    
+    // true if device can be shared via
+    // USB mass storage
+    boolean enableUms;
+ 
+    // Array of ASEC handles
+    void *asecHandles[ASEC_STORES_MAX];
+
+    // true if the device is being shared via USB mass storage
+    boolean umsActive;
+    
+    // current state of the mount point
+    MountState state;
+    
+    // number of mount or unmount retries so far, 
+    // when attempting to mount or unmount the device
+    int retryCount; 
+ 
+    // next in sMountPointList linked list
+    struct MountPoint* next;   
+} MountPoint;
+
+// list of our mount points (does not change after initialization)
+static MountPoint* sMountPointList = NULL;
+boolean gMassStorageEnabled = false;
+boolean gMassStorageConnected = false;
+
+static pthread_t sAutoMountThread = 0;
+static pid_t gExcludedPids[2] = {-1, -1};
+
+static const char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
+
+// number of mount points that have timeouts pending
+static int sRetriesPending = 0;
+
+// for synchronization between sAutoMountThread and the server thread
+static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// requests the USB mass_storage driver to begin or end sharing a block device
+// via USB mass storage.
+static void SetBackingStore(MountPoint* mp, boolean enable) 
+{
+    int fd;
+
+    if (!mp->driverStorePath) {
+        LOG_ERROR("no driver_store_path specified in config file for %s", mp->device);
+        return;
+    }
+
+    LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false"));
+    fd = open(mp->driverStorePath, O_WRONLY);
+    if (fd < 0)
+    {
+        LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath);
+    }
+    else
+    {
+        if (enable)
+        {
+            write(fd, mp->device, strlen(mp->device));
+            mp->umsActive = true;
+        }
+        else
+        {
+            char ch = 0;
+            write(fd, &ch, 1);
+            mp->umsActive = false;
+        }
+        close(fd);
+    }
+}
+
+static boolean ReadMassStorageState()
+{
+    FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
+    if (file)
+    {
+        char    buffer[20];
+        fgets(buffer, sizeof(buffer), file);
+        fclose(file);
+        return (strncmp(buffer, "online", strlen("online")) == 0);
+    }
+    else
+    {
+        LOG_ERROR("could not read initial mass storage state\n");
+        return false;
+    }
+}
+
+static boolean IsLoopMounted(const char* path)
+{
+    FILE* f;
+    int count;
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    int result = 0;
+    int path_length = strlen(path);
+       
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        LOG_ERROR("could not open /proc/mounts\n");
+        return -1;
+    }
+
+    do {
+        count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+        if (count == 3) {
+            if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
+            {
+                result = 1;
+                break;
+            }
+        }
+    } while (count == 3);
+
+    fclose(f);
+    LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
+    return result;
+}
+
+static int CheckFilesystem(const char *device)
+{
+    char cmdline[255];
+    int rc;
+
+    // XXX: SAN: Check for FAT signature
+    
+    int result = access(FSCK_MSDOS_PATH, X_OK);
+    if (result != 0) {
+        LOG_MOUNT("CheckFilesystem(%s): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device);
+        return 0;
+    }
+ 
+    char *args[7];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-V";
+    args[3] = "-w";
+    args[4] = "-p";
+    args[5] = device;
+    args[6] = NULL;
+
+    LOG_MOUNT("Checking filesystem on %s\n", device);
+    rc = logwrap(6, args);
+  
+    // XXX: We need to be able to distinguish between a FS with an error
+    // and a block device which does not have a FAT fs at all on it
+    if (rc == 0) {
+        LOG_MOUNT("Filesystem check completed OK\n");
+        return 0;
+    } else if (rc == 1) {
+        LOG_MOUNT("Filesystem check failed (general failure)\n");
+        return -EINVAL;
+    } else if (rc == 2) {
+        LOG_MOUNT("Filesystem check failed (invalid usage)\n");
+        return -EIO;
+    } else {
+        LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
+        return -EIO;
+    }
+}
+
+static int DoMountDevice(const char* device, const char* mountPoint)
+{
+    LOG_MOUNT("Attempting mount of %s on %s\n", device, mountPoint);
+
+#if CREATE_MOUNT_POINTS
+    // make sure mount point exists
+    mkdir(mountPoint, 0000);
+#endif
+
+    int flags = 0;
+    
+    if (device && strncmp(device, "/dev/", 5))
+    {
+        // mount with the loop driver if device does not start with "/dev/"
+        int file_fd, device_fd;
+        
+        // FIXME - only one loop mount supported at a time
+        file_fd = open(device, O_RDWR);
+        if (file_fd < -1) {
+            LOG_ERROR("open backing file %s failed\n", device);
+            return 1;
+        }
+        device_fd = open(LOOP_DEVICE, O_RDWR);
+        if (device_fd < -1) {
+            LOG_ERROR("open %s failed", LOOP_DEVICE);
+            close(file_fd);
+            return 1;
+        }
+        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
+        {
+            LOG_ERROR("ioctl LOOP_SET_FD failed\n");
+            close(file_fd);
+            close(device_fd);
+            return 1;
+        }
+
+        close(file_fd);
+        close(device_fd);
+        device = "/dev/block/loop0";
+    }
+
+    int result = access(device, R_OK);
+    if (result) {
+	LOG_ERROR("Unable to access '%s' (%d)\n", device, errno);
+   	return -errno;
+    }
+
+#if 0
+    if ((result = CheckFilesystem(device))) {
+        LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
+        // XXX:  Notify framework - need a new SDCARD state for the following:
+        //       - SD cards which are not present
+        //       - SD cards with no partition table
+        //       - SD cards with no filesystem
+        //       - SD cards with bad filesystem
+        return result;
+    }
+#endif
+
+    // Extra safety measures:
+    flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+    // Also, set fmask = 711 so that files cannot be marked executable,
+    // and cannot by opened by uid 1000 (system). Similar, dmask = 700
+    // so that directories cannot be accessed by uid 1000.
+    result = mount(device, mountPoint, "vfat", flags, 
+                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    if (result && errno == EROFS) {
+        LOG_ERROR("mount failed EROFS, try again read-only\n");
+        flags |= MS_RDONLY;
+        result = mount(device, mountPoint, "vfat", flags,
+                       "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    }
+
+    if (result == 0) {
+        LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint);
+        NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
+
+        MountPoint* mp = sMountPointList;
+        while (mp) {
+            if (!strcmp(mountPoint, mp->mountPoint)) {
+                int i;
+             
+                for (i = 0; i < ASEC_STORES_MAX; i++) {
+                    if (mp->asecHandles[i] != NULL) {
+                        int a_result;
+                        if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
+                            LOG_ERROR("ASEC start failure (%d)\n", a_result);
+                        }
+                    }
+                }
+                break;
+            }
+            mp = mp -> next;
+        }
+    } else if (errno == EBUSY) {
+        LOG_MOUNT("Mount failed (already mounted)\n");
+        result = 0;
+    } else {
+#if CREATE_MOUNT_POINTS
+        rmdir(mountPoint);
+#endif
+        LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint);
+    }
+
+    return result;
+}
+
+static int DoUnmountDevice(MountPoint *mp)
+{
+    boolean loop = IsLoopMounted(mp->mountPoint);
+    int i;
+
+    for (i = 0; i < ASEC_STORES_MAX; i++) {
+        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i]))
+            AsecStop(mp->asecHandles[i]);
+    }
+
+    int result = umount(mp->mountPoint);
+    LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
+
+    if (result == 0)
+    {
+#if CREATE_MOUNT_POINTS
+        rmdir(mountPoint);
+#endif
+        NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
+    }
+
+    if (loop)
+    {
+        // free the loop device
+        int loop_fd = open(LOOP_DEVICE, O_RDONLY);
+        if (loop_fd < -1) {
+            LOG_ERROR("open loop device failed\n");
+        }
+        if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+            LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
+        }
+
+        close(loop_fd);
+    }
+
+    // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
+    if (result && (errno == EINVAL || errno == ENOENT))
+        result = 0;
+
+    return result;
+}
+
+static int MountPartition(const char* device, const char* mountPoint)
+{
+    char    buf[100];
+    int i;
+    
+    // attempt to mount subpartitions of the device
+    for (i = 1; i < 10; i++)
+    {
+        int rc;
+        snprintf(buf, sizeof(buf), "%sp%d", device, i);
+        rc = DoMountDevice(buf, mountPoint);
+        LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc);
+        if (rc == 0)
+            return 0;
+    }
+
+    return -1;
+}
+
+/*****************************************************
+ * 
+ * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
+ * 
+ *****************************************************/
+
+static void SetState(MountPoint* mp, MountState state)
+{
+    mp->state = state;
+}
+
+// Enter a state that requires retries and timeouts.
+static void SetRetries(MountPoint* mp, MountState state)
+{
+    SetState(mp, state);
+    mp->retryCount = 0;
+
+    sRetriesPending++;
+    // wake up the automounter thread if we are being called 
+    // from somewhere else with no retries pending
+    if (sRetriesPending == 1 && sAutoMountThread != 0 && 
+            pthread_self() != sAutoMountThread)
+        pthread_kill(sAutoMountThread, SIGUSR1);
+}
+
+// Exit a state that requires retries and timeouts.
+static void ClearRetries(MountPoint* mp, MountState state)
+{
+    SetState(mp, state);
+    sRetriesPending--;
+}
+
+// attempt to mount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestMount(MountPoint* mp)
+{
+    LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
+
+    if (mp->state != kMounted && mp->state != kMounting &&
+            access(mp->device, R_OK) == 0) {
+        // try raw device first
+        if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
+            MountPartition(mp->device, mp->mountPoint) == 0)
+        {
+            SetState(mp, kMounted);
+        }
+        else 
+        {
+            SetState(mp, kMounting);
+            mp->retryCount = 0;
+            SetRetries(mp, kMounting);
+        }
+    }
+}
+
+// Force the kernel to drop all caches.
+static void DropSystemCaches(void)
+{
+    int fd;
+
+    LOG_MOUNT("Dropping system caches\n");
+    fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
+
+    if (fd > 0) {
+        char ch = 3;
+        int rc;
+
+        rc = write(fd, &ch, 1);
+        if (rc <= 0)
+            LOG_MOUNT("Error dropping caches (%d)\n", rc);
+        close(fd);
+    }
+}
+
+// attempt to unmount the specified mount point.
+// set up retry/timeout if it does not succeed at first.
+static void RequestUnmount(MountPoint* mp, MountState retryState)
+{
+    int result;
+
+    LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
+    
+    if (mp->state == kMounted)
+    {
+        SendUnmountRequest(mp->mountPoint);
+
+        // do this in case the user pulls the SD card before we can successfully unmount
+        sync();
+        DropSystemCaches();
+
+        if (DoUnmountDevice(mp) == 0) 
+        {
+            SetState(mp, kUnmounted);
+            if (retryState == kUnmountingForUms) 
+            {
+                SetBackingStore(mp, true);
+                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+            }
+        }
+        else 
+        {
+            LOG_MOUNT("unmount failed, set retry\n");
+            SetRetries(mp, retryState);
+        }
+    } 
+    else if (mp->state == kMounting)
+    {
+        SetState(mp, kUnmounted);
+    }
+}
+
+// returns true if the mount point should be shared via USB mass storage
+static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
+{
+    return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
+}
+
+// handles changes in gMassStorageEnabled and gMassStorageConnected
+static void MassStorageStateChanged()
+{
+    MountPoint* mp = sMountPointList;
+
+    boolean enable = (gMassStorageEnabled && gMassStorageConnected);
+    LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
+    
+    while (mp)
+    {
+        if (mp->enableUms)
+        {
+            if (enable)
+            {
+                if (mp->state == kMounting)
+                    SetState(mp, kUnmounted);
+                if (mp->state == kUnmounted) 
+                {
+                    SetBackingStore(mp, true);
+                    NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+                }
+                else
+                {
+                    LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
+                    // need to successfully unmount first
+                    RequestUnmount(mp, kUnmountingForUms);
+                }
+            } else if (mp->umsActive) {
+                SetBackingStore(mp, false);
+                if (mp->state == kUnmountingForUms)
+                {
+                    ClearRetries(mp, kMounted);
+                    NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
+                }
+                else if (mp->state == kUnmounted)
+                {
+                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
+                    RequestMount(mp);
+                }
+            }
+        }
+
+        mp = mp->next;
+    }
+}
+
+// called when USB mass storage connected state changes
+static void HandleMassStorageOnline(boolean connected)
+{
+    if (connected != gMassStorageConnected)
+    {
+        gMassStorageConnected = connected;
+        SendMassStorageConnected(connected);
+        
+        // we automatically reset to mass storage off after USB is connected
+        if (!connected)
+            gMassStorageEnabled = false;
+    
+        MassStorageStateChanged();
+    }
+}
+
+// called when a new block device has been created
+static void HandleMediaInserted(const char* device)
+{
+    MountPoint* mp = sMountPointList;
+    
+    LOG_MOUNT("HandleMediaInserted(%s):\n", device);
+
+    while (mp)
+    {
+        // see if the device matches mount point's block device
+        if (mp->state == kUnmounted &&
+                strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0) 
+        {
+            if (MassStorageEnabledForMountPoint(mp))
+            {
+                SetBackingStore(mp, true);
+                NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+            }
+            else
+                RequestMount(mp);
+        }  
+        mp = mp->next;
+    }
+}
+
+// called when a new block device has been deleted
+static void HandleMediaRemoved(const char* device)
+{    
+    MountPoint* mp = sMountPointList;
+    while (mp)
+    {
+        if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
+        {
+            if (mp->enableUms)
+                SetBackingStore(mp, false);
+
+             if (mp->state == kMounted) 
+            {
+                RequestUnmount(mp, kUnmountingForEject);
+                NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
+            }
+            
+            NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
+            break;
+        }  
+        mp = mp->next;
+    }
+}
+
+// Handle retrying to mount or unmount devices, 
+// and handle timeout condition if we have tried too many times
+static void HandleRetries()
+{
+    MountPoint* mp = sMountPointList;
+    
+    while (mp)
+    {
+       if (mp->state == kMounting) 
+       {
+            if (MountPartition(mp->device, mp->mountPoint) == 0)
+            {
+                // mount succeeded - clear the retry for this mount point
+                ClearRetries(mp, kMounted);
+            } 
+            else 
+            {
+                mp->retryCount++;
+                if (mp->retryCount == MAX_MOUNT_RETRIES)
+                {
+                    // we failed to mount the device too many times
+                    ClearRetries(mp, kUnmounted);
+                    // notify that we failed to mount
+                    NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
+                }
+            }
+       } 
+       else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
+       {
+            if (DoUnmountDevice(mp) == 0)
+            {
+                // unmounting succeeded
+                // start mass storage, if state is kUnmountingForUms
+                if (mp->state == kUnmountingForUms)
+                {
+                    SetBackingStore(mp, true);
+                     NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
+                }
+                // clear the retry for this mount point
+                ClearRetries(mp, kUnmounted);
+            } 
+            else 
+            {
+                mp->retryCount++;
+                if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
+                {
+                    // kill any processes that are preventing the device from unmounting
+                    // send SIGKILL instead of SIGTERM if the first attempt did not succeed
+                    boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
+                    
+                    int i;
+
+                    for (i = 0; i < ASEC_STORES_MAX; i++) {
+                        if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) {
+                            LOG_MOUNT("Killing processes for ASEC path '%s'\n",
+                                      AsecMountPoint(mp->asecHandles[i]));
+                            KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]),
+                                                       sigkill,
+                                                       gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t));
+
+                            // Now that we've killed the processes, try to stop the volume again
+                            AsecStop(mp->asecHandles[i]);
+                        }
+                    }
+
+                    // unmounting the device is failing, so start killing processes
+                    KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids, 
+                                               sizeof(gExcludedPids) / sizeof(pid_t));
+
+                }
+            }
+       } 
+        
+        mp = mp->next;
+    }
+}
+
+/*****************************************************
+ * 
+ * AUTO-MOUNTER THREAD
+ * 
+ *****************************************************/
+
+static void sigusr1_handler(int signo)
+{
+    // don't need to do anything here
+}
+
+// create a socket for listening to inotify events
+int CreateINotifySocket()
+{
+    // initialize inotify
+    int fd = inotify_init();
+
+    if (fd < 0) {
+        LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
+        return -1;
+    }
+
+    fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+
+    return fd;
+}
+
+
+// create a socket for listening to uevents
+int CreateUEventSocket()
+{
+    struct sockaddr_nl addr;
+    int sz = 64*1024;
+    int fd;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_pid = getpid();
+    addr.nl_groups = 0xffffffff;
+
+   fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+    if(fd < 0)
+    {
+        LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
+        return -1;
+    }
+
+    setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
+
+    if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
+        close(fd);
+        return -1;
+    }
+
+    return fd;
+}
+
+/*
+ * Automounter main event thread.
+ * This thread listens for block devices being created and deleted via inotify,
+ * and listens for changes in the USB mass storage connected/disconnected via uevents from the 
+ * power supply driver.
+ * This thread also handles retries and timeouts for requests to mount or unmount a device.
+ */
+static void* AutoMountThread(void* arg)
+{
+    int inotify_fd;
+    int uevent_fd;
+    int id;
+    struct sigaction    actions;
+
+    gExcludedPids[1] = getpid();
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = sigusr1_handler;
+    sigaction(SIGUSR1, &actions, NULL);
+    
+    // initialize inotify
+    inotify_fd = CreateINotifySocket();
+    // watch for files created and deleted in "/dev"
+    inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
+
+    // initialize uevent watcher
+    uevent_fd = CreateUEventSocket();
+    if (uevent_fd < 0) 
+    {
+        LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
+        return NULL;
+    }
+    
+    while (1)
+    {
+        struct pollfd fds[2];
+        int timeout, result;
+
+#define INOTIFY_IDX 0
+#define UEVENT_IDX  1
+    
+        fds[INOTIFY_IDX].fd = inotify_fd;
+        fds[INOTIFY_IDX].events = POLLIN;
+        fds[INOTIFY_IDX].revents = 0;
+        fds[UEVENT_IDX].fd = uevent_fd;
+        fds[UEVENT_IDX].events = POLLIN;
+        fds[UEVENT_IDX].revents = 0;
+        
+        // wait for an event or a timeout to occur.
+        // poll() can also return in response to a SIGUSR1 signal
+        timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
+        result = poll(fds, 2, timeout);
+
+        // lock the mutex while we are handling events
+        pthread_mutex_lock(&sMutex);
+
+        // handle inotify notifications for block device creation and deletion
+        if (fds[INOTIFY_IDX].revents == POLLIN)
+        {
+            struct inotify_event    event;
+            char    buffer[512];
+            int length = read(inotify_fd, buffer, sizeof(buffer));
+            int offset = 0;
+ 
+            while (length >= (int)sizeof(struct inotify_event))
+            {
+               struct inotify_event* event = (struct inotify_event *)&buffer[offset];
+               
+               if (event->mask == IN_CREATE)
+               {
+                   LOG_MOUNT("/dev/block/%s created\n", event->name);
+                   HandleMediaInserted(event->name);
+               }
+               else if (event->mask == IN_DELETE)
+               {
+                   LOG_MOUNT("/dev/block/%s deleted\n", event->name);
+                   HandleMediaRemoved(event->name);
+               }
+               
+               int size = sizeof(struct inotify_event) + event->len;
+               length -= size;
+               offset += size;
+            }
+        }
+
+        // handle uevent notifications for USB state changes
+        if (fds[UEVENT_IDX].revents == POLLIN)
+        {
+            char buffer[64*1024];
+            int count;
+            
+            count = recv(uevent_fd, buffer, sizeof(buffer), 0);
+            if (count > 0) {
+                char* s = buffer;
+                char* end = s + count;
+                char* type = NULL;
+                char* online = NULL;
+                char* switchName = NULL;
+                char* switchState = NULL;
+                                
+                while (s < end) {
+                    if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
+                        type = s + strlen("POWER_SUPPLY_TYPE=");
+                    else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
+                        online = s + strlen("POWER_SUPPLY_ONLINE=");                    
+                    else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
+                        switchName = s + strlen("SWITCH_NAME=");                    
+                    else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
+                        switchState = s + strlen("SWITCH_STATE=");                    
+                    s += (strlen(s) + 1);
+                }
+
+                // we use the usb_mass_storage switch state to tell us when USB is online
+                if (switchName && switchState && 
+                        !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
+                {
+                    LOG_MOUNT("USB online\n");
+                    HandleMassStorageOnline(true);
+                }
+                
+                // and we use the power supply state to tell us when USB is offline
+                // we can't rely on the switch for offline detection because we get false positives
+                // when USB is reenumerated by the host.
+                if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
+                {
+                    LOG_MOUNT("USB offline\n");
+                    HandleMassStorageOnline(false);
+                }
+            }
+        }
+
+       // handle retries
+       if (sRetriesPending)
+            HandleRetries();
+
+        // done handling events, so unlock the mutex
+        pthread_mutex_unlock(&sMutex);
+    }
+
+    inotify_rm_watch(inotify_fd, id);
+    close(inotify_fd);
+    close(uevent_fd);
+
+    return NULL;
+}
+
+/*****************************************************
+ * 
+ * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
+ * 
+ *****************************************************/
+
+// Called to enable or disable USB mass storage support
+void EnableMassStorage(boolean enable)
+{
+    pthread_mutex_lock(&sMutex);
+
+    LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
+    gMassStorageEnabled = enable;
+    MassStorageStateChanged();
+    pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be mounted
+void MountMedia(const char* mountPoint)
+{
+    MountPoint* mp = sMountPointList;
+ 
+    LOG_MOUNT("MountMedia(%s)\n", mountPoint);
+   
+    pthread_mutex_lock(&sMutex);
+    while (mp)
+    {
+        if (strcmp(mp->mountPoint, mountPoint) == 0)
+        {
+            if (mp->state == kUnmountingForEject)
+            {
+                // handle the case where we try to remount before we actually unmounted
+                ClearRetries(mp, kMounted);
+            }
+            
+            // don't attempt to mount if mass storage is active
+            if (!MassStorageEnabledForMountPoint(mp))
+                RequestMount(mp);
+        }
+        
+        mp = mp->next;
+    }
+    pthread_mutex_unlock(&sMutex);
+ }
+
+// Called to request that the specified mount point be unmounted
+void UnmountMedia(const char* mountPoint)
+{
+    MountPoint* mp = sMountPointList;
+    
+    pthread_mutex_lock(&sMutex);
+    while (mp)
+    {
+        if (strcmp(mp->mountPoint, mountPoint) == 0)
+            RequestUnmount(mp, kUnmountingForEject);
+        
+        mp = mp->next;
+    }
+    pthread_mutex_unlock(&sMutex);
+}
+
+boolean IsMassStorageEnabled()
+{
+    return gMassStorageEnabled;
+}
+
+boolean IsMassStorageConnected()
+{
+    return gMassStorageConnected;
+}
+
+/***********************************************
+ * 
+ * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
+ * 
+ ***********************************************/
+ 
+void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
+{
+    MountPoint* newMountPoint;
+    
+    LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath);
+    // add a new MountPoint to the head of our linked list
+    newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
+    newMountPoint->device = device;
+    newMountPoint->mountPoint = mountPoint;
+    newMountPoint->driverStorePath = driverStorePath;
+    newMountPoint->enableUms = enableUms;
+    newMountPoint->umsActive = false;
+    newMountPoint->state = kUnmounted;
+    newMountPoint->retryCount = 0;
+
+    // add to linked list
+    newMountPoint->next = sMountPointList;
+    sMountPointList = newMountPoint;
+    return newMountPoint;
+}
+
+int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size,
+                        const char *mount_point, const char *crypt)
+{
+    MountPoint *mp = (MountPoint *) Mp;
+    int i;
+
+    for (i = 0; i < ASEC_STORES_MAX; i++) {
+        if (!mp->asecHandles[i])
+            break;   
+    }
+
+    if (i == ASEC_STORES_MAX) {
+        LOG_ERROR("Maximum # of ASEC stores exceeded\n");
+        return -EINVAL;
+    }
+
+    if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt)))
+        return -1;
+
+    return 0;
+}
+static void MountDevices()
+{
+    MountPoint* mp = sMountPointList;
+    while (mp)
+    {
+        RequestMount(mp);
+        mp = mp->next;
+    }
+}
+
+void StartAutoMounter()
+{
+    gExcludedPids[0] = getpid();
+
+    gMassStorageConnected = ReadMassStorageState();
+    LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
+
+    MountDevices();
+    pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
+}
diff --git a/mountd/MODULE_LICENSE_APACHE2 b/mountd/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mountd/MODULE_LICENSE_APACHE2
diff --git a/mountd/NOTICE b/mountd/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/mountd/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/mountd/ProcessKiller.c b/mountd/ProcessKiller.c
new file mode 100644
index 0000000..e377774
--- /dev/null
+++ b/mountd/ProcessKiller.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+** mountd process killer
+*/
+
+#include "mountd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/stat.h>
+
+
+static boolean ReadSymLink(const char* path, char* link)
+{
+    struct stat s;
+    int length;
+
+    if (lstat(path, &s) < 0)
+        return false;
+    if ((s.st_mode & S_IFMT) != S_IFLNK)
+        return false;
+   
+    // we have a symlink    
+    length = readlink(path, link, PATH_MAX - 1);
+    if (length <= 0) 
+        return false;
+    link[length] = 0;
+    return true;
+}
+
+static boolean PathMatchesMountPoint(const char* path, const char* mountPoint)
+{
+    int length = strlen(mountPoint);
+    if (length > 1 && strncmp(path, mountPoint, length) == 0)
+    {
+        // we need to do extra checking if mountPoint does not end in a '/'
+        if (mountPoint[length - 1] == '/')
+            return true;
+        // if mountPoint does not have a trailing slash, we need to make sure
+        // there is one in the path to avoid partial matches.
+        return (path[length] == 0 || path[length] == '/');
+    }
+    
+    return false;
+}
+
+static void GetProcessName(int pid, char buffer[PATH_MAX])
+{
+    int fd;
+    sprintf(buffer, "/proc/%d/cmdline", pid);
+    fd = open(buffer, O_RDONLY);
+    if (fd < 0) {
+        strcpy(buffer, "???");
+    } else {
+        int length = read(fd, buffer, PATH_MAX - 1);
+        buffer[length] = 0;
+        close(fd);
+    }
+}
+
+static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
+{
+    DIR*    dir;
+    struct dirent* de;
+    boolean fileOpen = false;
+    char    path[PATH_MAX];
+    char    link[PATH_MAX];
+    int     parent_length;
+
+    // compute path to process's directory of open files
+    sprintf(path, "/proc/%d/fd", pid);
+    dir = opendir(path);
+    if (!dir)
+        return false;
+
+    // remember length of the path
+    parent_length = strlen(path);
+    // append a trailing '/'
+    path[parent_length++] = '/';
+    
+    while ((de = readdir(dir)) != 0 && !fileOpen) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+        
+        // append the file name, after truncating to parent directory
+        path[parent_length] = 0;
+        strcat(path, de->d_name);
+
+        if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+        {
+            char    name[PATH_MAX];
+            GetProcessName(pid, name);
+            LOG_ERROR("process %s (%d) has open file %s\n", name, pid, link);
+            fileOpen = true;
+        }
+    }
+
+    closedir(dir);
+    return fileOpen;
+}
+
+static boolean CheckFileMaps(int pid, const char* mountPoint)
+{
+    FILE*   file;
+    char    buffer[PATH_MAX + 100];
+    boolean mapOpen = false;
+
+    sprintf(buffer, "/proc/%d/maps", pid);
+    file = fopen(buffer, "r");
+    if (!file)
+        return false;
+    
+    while (!mapOpen && fgets(buffer, sizeof(buffer), file))
+    {
+        // skip to the path
+        const char* path = strchr(buffer, '/');
+        if (path && PathMatchesMountPoint(path, mountPoint))
+        {
+            char    name[PATH_MAX];
+            GetProcessName(pid, name);
+            LOG_ERROR("process %s (%d) has open file map for %s\n", name, pid, path);
+            mapOpen = true;
+        }
+    }
+    
+    fclose(file);
+    return mapOpen;
+}
+
+static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
+{
+    char    path[PATH_MAX];
+    char    link[PATH_MAX];
+
+    sprintf(path, "/proc/%d/%s", pid, name);
+    if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) 
+    {
+        char    name[PATH_MAX];
+        GetProcessName(pid, name);
+        LOG_ERROR("process %s (%d) has %s in %s\n", name, pid, message, mountPoint);
+        return true;
+    }
+    else
+        return false;
+}
+
+static int get_pid(const char* s)
+{
+    int result = 0;
+    while (*s) {
+        if (!isdigit(*s)) return -1;
+        result = 10 * result + (*s++ - '0');
+    }
+    return result;
+}
+
+// hunt down and kill processes that have files open on the given mount point
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded)
+{
+    DIR*    dir;
+    struct dirent* de;
+
+    LOG_ERROR("KillProcessesWithOpenFiles %s\n", mountPoint);
+    dir = opendir("/proc");
+    if (!dir) return;
+
+    while ((de = readdir(dir)) != 0)
+    {
+        boolean killed = false;
+        // does the name look like a process ID?
+        int pid = get_pid(de->d_name);
+        if (pid == -1) continue;
+
+        if (CheckFileDescriptorSymLinks(pid, mountPoint)    // check for open files
+                || CheckFileMaps(pid, mountPoint)           // check for mmap()
+                || CheckSymLink(pid, mountPoint, "cwd", "working directory")    // check working directory
+                || CheckSymLink(pid, mountPoint, "root", "chroot")              // check for chroot()
+                || CheckSymLink(pid, mountPoint, "exe", "executable path")      // check executable path
+            ) 
+        {
+            int i;
+            boolean hit = false;
+
+            for (i = 0; i < num_excluded; i++) {
+                if (pid == excluded[i]) {
+                    LOG_ERROR("I just need a little more TIME captain!\n");
+                    hit = true;
+                    break;
+                }
+            }
+
+            if (!hit) {
+                LOG_ERROR("Killing process %d\n", pid);
+                kill(pid, (sigkill ? SIGKILL : SIGTERM));
+            }
+        }
+    }
+
+    closedir(dir);
+}        
diff --git a/mountd/Server.c b/mountd/Server.c
new file mode 100644
index 0000000..64459bd
--- /dev/null
+++ b/mountd/Server.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+**	mountd server support
+*/
+
+#include "mountd.h"
+#include "ASEC.h"
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <pthread.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <private/android_filesystem_config.h>
+
+
+// current client file descriptor
+static int sFD = -1;
+
+// to synchronize writing to client
+static pthread_mutex_t sWriteMutex = PTHREAD_MUTEX_INITIALIZER;
+
+// path for media that failed to mount before the runtime is connected
+static char* sDeferredUnmountableMediaPath = NULL;
+
+// last asec msg before the runtime was connected
+static char* sAsecDeferredMessage = NULL;
+static char* sAsecDeferredArgument = NULL;
+
+static int Write(const char* message)
+{
+    int result = -1;
+
+    pthread_mutex_lock(&sWriteMutex);
+    
+    LOG_SERVER("Write: %s\n", message);
+    if (sFD >= 0)
+        result = write(sFD, message, strlen(message) + 1);
+
+    pthread_mutex_unlock(&sWriteMutex); 
+    
+    return result;
+}
+
+static int Write2(const char* message, const char* data)
+{
+    int result = -1;
+
+    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
+    if (!buffer)
+    {
+        LOG_ERROR("alloca failed in Write2\n");
+        return -1;
+    }
+
+    strcpy(buffer, message);
+    strcat(buffer, data);
+    return Write(buffer);
+}
+
+static void SendStatus()
+{
+    Write(IsMassStorageConnected() ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+    Write(IsMassStorageEnabled() ? MOUNTD_UMS_ENABLED : MOUNTD_UMS_DISABLED);
+}
+
+static void DoCommand(const char* command)
+{
+    LOG_SERVER("DoCommand %s\n", command);
+    
+    if (strcmp(command, MOUNTD_ENABLE_UMS) == 0)
+    {
+        EnableMassStorage(true);
+        Write(MOUNTD_UMS_ENABLED);
+     }
+    else if (strcmp(command, MOUNTD_DISABLE_UMS) == 0) 
+    {
+        EnableMassStorage(false);
+        Write(MOUNTD_UMS_DISABLED);
+    }
+    else if (strcmp(command, MOUNTD_SEND_STATUS) == 0)
+    {
+        SendStatus();
+    }
+    else if (strncmp(command, MOUNTD_MOUNT_MEDIA, strlen(MOUNTD_MOUNT_MEDIA)) == 0)
+    {
+        const char* path = command + strlen(MOUNTD_MOUNT_MEDIA);
+        MountMedia(path);
+    }
+    else if (strncmp(command, MOUNTD_EJECT_MEDIA, strlen(MOUNTD_EJECT_MEDIA)) == 0)
+    {
+        const char* path = command + strlen(MOUNTD_EJECT_MEDIA);
+        UnmountMedia(path);
+    } 
+    else if (strncmp(command, ASEC_CMD_ENABLE, strlen(ASEC_CMD_ENABLE)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_ENABLE\n");
+	// XXX: SAN: Impliment
+    }
+    else if (strncmp(command, ASEC_CMD_DISABLE, strlen(ASEC_CMD_DISABLE)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_DISABLE\n");
+	// XXX: SAN: Impliment
+    }
+    else if (strncmp(command, ASEC_CMD_SEND_STATUS, strlen(ASEC_CMD_SEND_STATUS)) == 0) {
+        LOG_ASEC("Got ASEC_CMD_SEND_STATUS\n");
+	// XXX: SAN: Impliment
+    }
+    else
+        LOGE("unknown command %s\n", command);
+}
+
+int RunServer()
+{
+    int socket = android_get_control_socket(MOUNTD_SOCKET);
+    if (socket < 0) {
+        LOGE("Obtaining file descriptor for socket '%s' failed: %s",
+             MOUNTD_SOCKET, strerror(errno));
+        return -1;
+    }
+
+    if (listen(socket, 4) < 0) {
+        LOGE("Unable to listen on file descriptor '%d' for socket '%s': %s",
+             socket, MOUNTD_SOCKET, strerror(errno));
+        return -1;
+    }
+
+    while (1)
+    {
+        struct sockaddr addr;
+        socklen_t alen;
+        struct ucred cred;
+        socklen_t size;
+        
+        alen = sizeof(addr);
+        sFD = accept(socket, &addr, &alen);
+        if (sFD < 0)
+            continue;
+            
+        if (sDeferredUnmountableMediaPath) {
+            NotifyMediaState(sDeferredUnmountableMediaPath, MEDIA_UNMOUNTABLE, false);
+            free(sDeferredUnmountableMediaPath);
+            sDeferredUnmountableMediaPath = NULL;
+        }
+
+        if (sAsecDeferredMessage) {
+    
+            if (Write2(sAsecDeferredMessage, sAsecDeferredArgument) < 0)
+                LOG_ERROR("Failed to deliver deferred ASEC msg to framework\n");
+            free(sAsecDeferredMessage);
+            free(sAsecDeferredArgument);
+            sAsecDeferredMessage = sAsecDeferredArgument = NULL;
+        }
+
+        while (1)
+        {    
+            char    buffer[101];
+            int result = read(sFD, buffer, sizeof(buffer) - 1);
+            if (result > 0)
+            {
+                int start = 0;
+                int i;
+                // command should be zero terminated, but just in case
+                buffer[result] = 0;
+                for (i = 0; i < result; i++) 
+                {
+                    if (buffer[i] == 0) 
+                    {
+                        DoCommand(buffer + start);
+                        start = i + 1;
+                    }                   
+                }
+            }
+            else
+            {
+                close(sFD);
+                sFD = -1;
+                break;
+            }
+        }
+    }  
+
+    // should never get here
+    return 0;
+}
+
+void SendMassStorageConnected(boolean connected)
+{
+    Write(connected ? MOUNTD_UMS_CONNECTED : MOUNTD_UMS_DISCONNECTED);
+}
+
+void SendUnmountRequest(const char* path)
+{
+    Write2(MOUNTD_REQUEST_EJECT, path);
+}
+
+void NotifyAsecState(AsecState state, const char *argument)
+{
+    const char *event = NULL;
+    const char *status = NULL;
+    boolean deferr = true;;
+
+    switch (state) {
+        case ASEC_DISABLED:
+            event = ASEC_EVENT_DISABLED;
+            status = ASEC_STATUS_DISABLED;
+            break;
+        case ASEC_AVAILABLE:
+            event = ASEC_EVENT_AVAILABLE;
+            status = ASEC_STATUS_AVAILABLE;
+            break;
+        case ASEC_BUSY:
+            event = ASEC_EVENT_BUSY;
+            status = ASEC_STATUS_BUSY;
+            deferr = false;
+            break;
+        case ASEC_FAILED_INTERR:
+            event = ASEC_EVENT_FAILED_INTERR;
+            status = ASEC_STATUS_FAILED_INTERR;
+            break;
+        case ASEC_FAILED_NOMEDIA:
+            event = ASEC_EVENT_FAILED_NOMEDIA;
+            status = ASEC_STATUS_FAILED_NOMEDIA;
+            break;
+        case ASEC_FAILED_BADMEDIA:
+            event = ASEC_EVENT_FAILED_BADMEDIA;
+            status = ASEC_STATUS_FAILED_BADMEDIA;
+            break;
+        case ASEC_FAILED_BADKEY:
+            event = ASEC_EVENT_FAILED_BADKEY;
+            status = ASEC_STATUS_FAILED_BADKEY;
+            break;
+        default:
+            LOG_ERROR("unknown AsecState %d in NotifyAsecState\n", state);
+            return;
+    }
+
+    property_set(ASEC_STATUS, status);
+
+    int result = Write2(event, argument);
+    if ((result < 0) && deferr) {
+        if (sAsecDeferredMessage) 
+            free(sAsecDeferredMessage);
+        sAsecDeferredMessage = strdup(event);
+        if (sAsecDeferredArgument)
+            free(sAsecDeferredArgument);
+        sAsecDeferredArgument = strdup(argument);
+        LOG_ASEC("Deferring event '%s' arg '%s' until framework connects\n", event, argument);
+    }
+}
+
+void NotifyMediaState(const char* path, MediaState state, boolean readOnly)
+{
+    const char* event = NULL;
+    const char* propertyValue = NULL;
+    
+    switch (state) {
+        case MEDIA_REMOVED:
+            event = MOUNTD_MEDIA_REMOVED;
+            propertyValue = EXTERNAL_STORAGE_REMOVED;
+            break;
+        case MEDIA_UNMOUNTED:
+            event = MOUNTD_MEDIA_UNMOUNTED;
+            propertyValue = EXTERNAL_STORAGE_UNMOUNTED;
+            break;
+        case MEDIA_MOUNTED:
+            event = (readOnly ? MOUNTD_MEDIA_MOUNTED_READ_ONLY : MOUNTD_MEDIA_MOUNTED);
+             propertyValue = (readOnly ? EXTERNAL_STORAGE_MOUNTED_READ_ONLY : EXTERNAL_STORAGE_MOUNTED);
+           break;
+        case MEDIA_SHARED:
+            event = MOUNTD_MEDIA_SHARED;
+            propertyValue = EXTERNAL_STORAGE_SHARED;
+            break;
+        case MEDIA_BAD_REMOVAL:
+            event = MOUNTD_MEDIA_BAD_REMOVAL;
+            propertyValue = EXTERNAL_STORAGE_BAD_REMOVAL;
+            break;
+        case MEDIA_UNMOUNTABLE:
+            event = MOUNTD_MEDIA_UNMOUNTABLE;
+            propertyValue = EXTERNAL_STORAGE_UNMOUNTABLE;
+            break;
+        default:
+            LOG_ERROR("unknown MediaState %d in NotifyMediaState\n", state);
+            return;
+    }
+    
+    property_set(EXTERNAL_STORAGE_STATE, propertyValue);
+    int result = Write2(event, path);
+    if (result < 0 && state == MEDIA_UNMOUNTABLE) {
+    
+        // if we cannot communicate with the runtime, defer this message until the runtime is available
+        sDeferredUnmountableMediaPath = strdup(path);
+    }
+}
diff --git a/mountd/logwrapper.c b/mountd/logwrapper.c
new file mode 100644
index 0000000..69606ab
--- /dev/null
+++ b/mountd/logwrapper.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+
+int parent(const char *tag, int parent_read) {
+    int status;
+    char buffer[4096];
+
+    int a = 0;  // start index of unprocessed data
+    int b = 0;  // end index of unprocessed data
+    int sz;
+    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
+
+        sz += b;
+        // Log one line at a time
+        for (b = 0; b < sz; b++) {
+            if (buffer[b] == '\r') {
+                buffer[b] = '\0';
+            } else if (buffer[b] == '\n') {
+                buffer[b] = '\0';
+                LOG(LOG_INFO, tag, &buffer[a]);
+                a = b + 1;
+            }
+        }
+
+        if (a == 0 && b == sizeof(buffer) - 1) {
+            // buffer is full, flush
+            buffer[b] = '\0';
+            LOG(LOG_INFO, tag, &buffer[a]);
+            b = 0;
+        } else if (a != b) {
+            // Keep left-overs
+            b -= a;
+            memmove(buffer, &buffer[a], b);
+            a = 0;
+        } else {
+            a = 0;
+            b = 0;
+        }
+
+    }
+    // Flush remaining data
+    if (a != b) {
+        buffer[b] = '\0';
+        LOG(LOG_INFO, tag, &buffer[a]);
+    }
+    status = 0xAAAA;
+    if (wait(&status) != -1) {  // Wait for child
+        if (WIFEXITED(status)) {
+            LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+                    WEXITSTATUS(status));
+            return WEXITSTATUS(status);
+        } else if (WIFSIGNALED(status))
+            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+                    WTERMSIG(status));
+        else if (WIFSTOPPED(status))
+            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+                    WSTOPSIG(status));
+    } else
+        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+                strerror(errno), errno);
+    return -EAGAIN;
+}
+
+void child(int argc, char* argv[]) {
+    // create null terminated argv_child array
+    char* argv_child[argc + 1];
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
+
+    // XXX: PROTECT FROM VIKING KILLER
+    if (execvp(argv_child[0], argv_child)) {
+        LOG(LOG_ERROR, "logwrapper",
+            "executing %s failed: %s\n", argv_child[0], strerror(errno));
+        exit(-1);
+    }
+}
+
+int logwrap(int argc, char* argv[])
+{
+    pid_t pid;
+
+    int parent_ptty;
+    int child_ptty;
+    char *child_devname = NULL;
+
+    /* Use ptty instead of socketpair so that STDOUT is not buffered */
+    parent_ptty = open("/dev/ptmx", O_RDWR);
+    if (parent_ptty < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty\n");
+	return -errno;
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx\n");
+	return -1;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Failed to fork\n");
+        return -errno;
+    } else if (pid == 0) {
+        child_ptty = open(child_devname, O_RDWR);
+        if (child_ptty < 0) {
+	    LOG(LOG_ERROR, "logwrapper", "Problem with child ptty\n");
+            return -errno;
+        }
+
+        // redirect stdout and stderr
+        close(parent_ptty);
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc, argv);
+    } else {
+        // switch user and group to "log"
+        // this may fail if we are not root, 
+        // but in that case switching user/group is unnecessary 
+        
+      //  setgid(AID_LOG);
+      //  setuid(AID_LOG);
+
+        return parent(argv[0], parent_ptty);
+    }
+
+    return 0;
+}
diff --git a/mountd/mountd.c b/mountd/mountd.c
new file mode 100644
index 0000000..27ec8de
--- /dev/null
+++ b/mountd/mountd.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+**    mountd main program
+*/
+
+#include "mountd.h"
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
+#include <private/android_filesystem_config.h>
+
+#ifdef MOUNTD_LOG
+FILE*    logFile;
+#endif
+
+struct asec_cfg {
+    const char *name;
+    const char *backing_file;
+    const char *size;
+    const char *mount_point;
+    const char *crypt;
+};
+
+static int ProcessAsecData(cnode *node, struct asec_cfg *stores, int idx)
+{
+    cnode *child = node->first_child;
+    const char *name = NULL;
+    const char *file = NULL;
+    const char *size = NULL;
+    const char *mp = NULL;
+    const char *crypt = NULL;
+
+    LOG_ASEC("ProcessAsecData(%s, %p, %d)\n", node->name, stores, idx);
+
+    while (child) {
+        if (!strcmp(child->name, "name"))
+            name = child->value;
+        else if (!strcmp(child->name, "backing_file"))
+            file = child->value;
+        else if (!strcmp(child->name, "size"))
+            size = child->value;
+        else if (!strcmp(child->name, "mount_point"))
+            mp = child->value;
+        else if (!strcmp(child->name, "crypt"))
+            crypt = child->value;
+        child = child->next;
+    }
+
+    if (!name || !file || !size || !mp || !crypt) {
+        LOG_ERROR("Missing required token from config. Skipping ASEC volume\n");
+        return -1;
+    } else if (idx == ASEC_STORES_MAX) {
+        LOG_ERROR("Maximum # of ASEC stores already defined\n");
+        return -1;
+    }
+
+    stores[idx].name = name;
+    stores[idx].backing_file = file;
+    stores[idx].size = size;
+    stores[idx].mount_point = mp;
+    stores[idx].crypt = crypt;
+    return ++idx;
+}
+
+static void ReadConfigFile(const char* path)
+{
+    cnode* root = config_node("", "");
+    cnode* node;
+
+    config_load_file(root, path);
+    node = root->first_child;
+
+    while (node)
+    {
+        if (strcmp(node->name, "mount") == 0)
+        {
+            const char* block_device = NULL;
+            const char* mount_point = NULL;
+            const char* driver_store_path = NULL;
+            boolean enable_ums = false;
+            cnode* child = node->first_child;
+            struct asec_cfg asec_stores[ASEC_STORES_MAX];
+            int    asec_idx = 0;
+
+            memset(asec_stores, 0, sizeof(asec_stores));
+
+            while (child)
+            {
+                const char* name = child->name;
+                const char* value = child->value;
+
+                if (!strncmp(name, "asec_", 5)) {
+                     int rc = ProcessAsecData(child, asec_stores, asec_idx);
+                     if (rc < 0) {
+                         LOG_ERROR("Error processing ASEC cfg data\n");
+                     } else
+                         asec_idx = rc;
+                } else if (strcmp(name, "block_device") == 0)
+                    block_device = value;
+                else if (strcmp(name, "mount_point") == 0)
+                    mount_point = value;
+                else if (strcmp(name, "driver_store_path") == 0)
+                    driver_store_path = value;
+                else if (strcmp(name, "enable_ums") == 0 &&
+                        strcmp(value, "true") == 0)
+                    enable_ums = true;
+                
+                child = child->next;
+            }
+
+            // mount point and removable fields are optional
+            if (block_device && mount_point)
+            {
+                void *mp = AddMountPoint(block_device, mount_point, driver_store_path, enable_ums);
+                int i;
+
+                for (i = 0; i < asec_idx; i++) {
+                    AddAsecToMountPoint(mp, asec_stores[i].name, asec_stores[i].backing_file,
+                                        asec_stores[i].size, asec_stores[i].mount_point,
+                                        asec_stores[i].crypt);
+                }
+            }
+        }
+            
+        node = node->next;
+    }
+}
+
+int main(int argc, char* argv[])
+{
+    const char*     configPath = "/system/etc/mountd.conf";
+    int             i;    
+
+    for (i = 1; i < argc; i++)
+    {
+        const char* arg = argv[i];
+        
+        if (strcmp(arg, "-f") == 0)
+        {
+            if (i < argc - 1)
+                configPath = argv[++i];
+        }
+    }
+        
+    ReadConfigFile(configPath);
+    StartAutoMounter();
+    return RunServer();
+}
diff --git a/mountd/mountd.h b/mountd/mountd.h
new file mode 100644
index 0000000..c4bc91d
--- /dev/null
+++ b/mountd/mountd.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOUNTD_H__
+#define MOUNTD_H__
+
+#define LOG_TAG "mountd"
+#include "cutils/log.h"
+
+#include "ASEC.h"
+
+typedef int boolean;
+enum {
+    false = 0,
+    true = 1
+};
+
+#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
+
+// Set this for logging error messages
+#define ENABLE_LOG_ERROR
+
+// set this to log automounter events
+#define ENABLE_LOG_MOUNT
+
+// set this to log server events
+//#define ENABLE_LOG_SERVER
+
+// set this to log ASEC events
+#define ENABLE_LOG_ASEC
+
+#ifdef ENABLE_LOG_ERROR
+#define LOG_ERROR(fmt, args...) \
+    { LOGE(fmt , ## args); }
+#else
+#define LOG_ERROR(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ERROR */
+
+#ifdef ENABLE_LOG_MOUNT
+#define LOG_MOUNT(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_MOUNT(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_MOUNT */
+
+#ifdef ENABLE_LOG_SERVER
+#define LOG_SERVER(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_SERVER(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_SERVER */
+
+#ifdef ENABLE_LOG_ASEC
+#define LOG_ASEC(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_ASEC(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ASEC */
+
+
+typedef enum MediaState {
+    // no media in SD card slot
+    MEDIA_REMOVED,
+    
+    // media in SD card slot, but not mounted
+    MEDIA_UNMOUNTED,
+    
+    // media in SD card slot and mounted at its mount point
+    MEDIA_MOUNTED,
+    
+    // media in SD card slot, unmounted, and shared as a mass storage device
+    MEDIA_SHARED,
+    
+    // media was removed from SD card slot, but mount point was not unmounted
+    // this state is cleared after the mount point is unmounted
+    MEDIA_BAD_REMOVAL,
+
+    // media in SD card slot could not be mounted (corrupt file system?)
+    MEDIA_UNMOUNTABLE,
+} MediaState;
+
+// socket name for connecting to mountd
+#define MOUNTD_SOCKET         "mountd"
+
+// mountd commands
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_ENABLE_UMS     "enable_ums"
+#define MOUNTD_DISABLE_UMS    "disable_ums"
+#define MOUNTD_SEND_STATUS    "send_status"
+
+// these commands should contain a mount point following the colon
+#define MOUNTD_MOUNT_MEDIA  "mount_media:"
+#define MOUNTD_EJECT_MEDIA  "eject_media:"
+
+// mountd events
+// these must match the corresponding strings in //device/java/android/android/os/UsbListener.java
+#define MOUNTD_UMS_ENABLED              "ums_enabled"
+#define MOUNTD_UMS_DISABLED             "ums_disabled"
+#define MOUNTD_UMS_CONNECTED            "ums_connected"
+#define MOUNTD_UMS_DISCONNECTED         "ums_disconnected"
+
+// these events correspond to the states in the MediaState enum.
+// a path to the mount point follows the colon.
+#define MOUNTD_MEDIA_REMOVED            "media_removed:"
+#define MOUNTD_MEDIA_UNMOUNTED        	"media_unmounted:"
+#define MOUNTD_MEDIA_MOUNTED          	"media_mounted:"
+#define MOUNTD_MEDIA_MOUNTED_READ_ONLY  "media_mounted_ro:"
+#define MOUNTD_MEDIA_SHARED             "media_shared:"
+#define MOUNTD_MEDIA_BAD_REMOVAL        "media_bad_removal:"
+#define MOUNTD_MEDIA_UNMOUNTABLE        "media_unmountable:"
+
+// this event sent to request unmount for media mount point
+#define MOUNTD_REQUEST_EJECT            "request_eject:"
+
+// system properties
+// these must match the corresponding strings in //device/java/android/android/os/Environment.java
+#define EXTERNAL_STORAGE_STATE          "EXTERNAL_STORAGE_STATE"
+#define EXTERNAL_STORAGE_REMOVED        "removed"
+#define EXTERNAL_STORAGE_UNMOUNTED      "unmounted"
+#define EXTERNAL_STORAGE_MOUNTED        "mounted"
+#define EXTERNAL_STORAGE_MOUNTED_READ_ONLY        "mounted_ro"
+#define EXTERNAL_STORAGE_SHARED         "shared"
+#define EXTERNAL_STORAGE_BAD_REMOVAL    "bad_removal"
+#define EXTERNAL_STORAGE_UNMOUNTABLE    "unmountable"
+
+// AutoMount.c
+
+boolean IsMassStorageEnabled();
+boolean IsMassStorageConnected();
+
+void MountMedia(const char* mountPoint);
+void UnmountMedia(const char* mountPoint);
+void EnableMassStorage(boolean enable);
+
+// call this before StartAutoMounter() to add a mount point to monitor
+void *AddMountPoint(const char* device, const char* mountPoint, const char* driverStorePath,
+                    boolean enableUms);
+
+int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file,
+                        const char *size, const char *mount_point, const char *crypt);
+
+// start automounter thread
+void StartAutoMounter();
+
+// check /proc/mounts for mounted file systems, and notify mount or unmount for any that are in our automount list
+void NotifyExistingMounts();
+
+
+// ASEC.c
+
+void *AsecInit(const char *Name, const char *SrcPath, const char *BackingFile,
+               const char *Size, const char *DstPath, const char *Crypt);
+int AsecStart(void *Handle);
+int AsecStop(void *Handle);
+void AsecDeinit(void *Handle);
+boolean AsecIsStarted(void *Handle);
+const char *AsecMountPoint(void *Handle);
+
+// ProcessKiller.c
+
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, pid_t *excluded, int num_excluded);
+
+// logwrapper.c
+int logwrap(int argc, char* argv[]);
+
+// Server.c
+
+int RunServer();
+void SendMassStorageConnected(boolean connected); 
+void SendUnmountRequest(const char* path);
+void NotifyMediaState(const char* path, MediaState state, boolean readOnly);
+void NotifyAsecState(AsecState state, const char *argument);
+#endif // MOUNTD_H__
diff --git a/netcfg/Android.mk b/netcfg/Android.mk
new file mode 100644
index 0000000..949f417
--- /dev/null
+++ b/netcfg/Android.mk
@@ -0,0 +1,16 @@
+ifneq ($(BUILD_TINY_ANDROID),true)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= netcfg.c
+LOCAL_MODULE:= netcfg
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+#LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+#LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+#LOCAL_STATIC_LIBRARIES := libcutils libc
+
+LOCAL_SHARED_LIBRARIES := libc libnetutils
+
+include $(BUILD_EXECUTABLE)
+endif
diff --git a/netcfg/MODULE_LICENSE_APACHE2 b/netcfg/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/netcfg/MODULE_LICENSE_APACHE2
diff --git a/netcfg/NOTICE b/netcfg/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/netcfg/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c
new file mode 100644
index 0000000..fc9cf48
--- /dev/null
+++ b/netcfg/netcfg.c
@@ -0,0 +1,175 @@
+/* system/bin/netcfg/netcfg.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <dirent.h>
+
+static int verbose = 0;
+
+int ifc_init();
+void ifc_close();
+int ifc_up(char *iname);
+int ifc_down(char *iname);
+int ifc_remove_host_routes(char *iname);
+int ifc_remove_default_route(char *iname);
+int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags);
+int do_dhcp(char *iname);
+
+void die(const char *reason)
+{
+    perror(reason);
+    exit(1);
+}
+
+const char *ipaddr(unsigned addr)
+{
+    static char buf[32];
+    
+    sprintf(buf,"%d.%d.%d.%d", 
+            addr & 255,
+            ((addr >> 8) & 255),
+            ((addr >> 16) & 255), 
+            (addr >> 24));
+    return buf;
+}
+
+void usage(void)
+{
+    fprintf(stderr,"usage: netcfg [<interface> {dhcp|up|down}]\n");
+    exit(1);    
+}
+
+int dump_interface(const char *name)
+{
+    unsigned addr, mask, flags;
+    
+    if(ifc_get_info(name, &addr, &mask, &flags)) {
+        return 0;
+    }
+
+    printf("%-8s %s  ", name, flags & 1 ? "UP  " : "DOWN");
+    printf("%-16s", ipaddr(addr));
+    printf("%-16s", ipaddr(mask));
+    printf("0x%08x\n", flags);
+    return 0;
+}
+
+int dump_interfaces(void)
+{
+    DIR *d;
+    struct dirent *de;
+    
+    d = opendir("/sys/class/net");
+    if(d == 0) return -1;
+    
+    while((de = readdir(d))) {
+        if(de->d_name[0] == '.') continue;
+        dump_interface(de->d_name);
+    }
+    closedir(d);
+    return 0;
+}
+
+struct 
+{
+    const char *name;
+    int nargs;
+    void *func;
+} CMDS[] = {
+    { "dhcp",   1, do_dhcp },
+    { "up",     1, ifc_up },
+    { "down",   1, ifc_down },
+    { "flhosts",  1, ifc_remove_host_routes },
+    { "deldefault", 1, ifc_remove_default_route },
+    { 0, 0, 0 },
+};
+
+static int call_func(void *_func, unsigned nargs, char **args)
+{
+    switch(nargs){
+    case 1: {
+        int (*func)(char *a0) = _func;
+        return func(args[0]);
+    }
+    case 2: {
+        int (*func)(char *a0, char *a1) = _func;
+        return func(args[0], args[1]);
+    }
+    case 3: {
+        int (*func)(char *a0, char *a1, char *a2) = _func;
+        return func(args[0], args[1], args[2]);
+    }
+    default:
+        return -1;
+    }
+}
+
+int main(int argc, char **argv)
+{
+    char *iname;
+    int n;
+    
+    if(ifc_init()) {
+        die("Cannot perform requested operation");
+    }
+
+    if(argc == 1) {
+        int result = dump_interfaces();
+        ifc_close();
+        return result;
+    }
+
+    if(argc < 3) usage();
+
+    iname = argv[1];
+    if(strlen(iname) > 16) usage();
+
+    argc -= 2;
+    argv += 2;
+    while(argc > 0) {
+        for(n = 0; CMDS[n].name; n++){
+            if(!strcmp(argv[0], CMDS[n].name)) {
+                char *cmdname = argv[0];
+                int nargs = CMDS[n].nargs;
+                
+                argv[0] = iname;
+                if(argc < nargs) {
+                    fprintf(stderr, "not enough arguments for '%s'\n", cmdname);
+                    ifc_close();
+                    exit(1);
+                }
+                if(call_func(CMDS[n].func, nargs, argv)) {
+                    fprintf(stderr, "action '%s' failed (%s)\n", cmdname, strerror(errno));
+                    ifc_close();
+                    exit(1);
+                }
+                argc -= nargs;
+                argv += nargs;
+                goto done;
+            }
+        }
+        fprintf(stderr,"no such action '%s'\n", argv[0]);
+        usage();
+    done:
+        ;
+    }
+    ifc_close();
+
+    return 0;
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
new file mode 100644
index 0000000..b2fe8cf
--- /dev/null
+++ b/rootdir/Android.mk
@@ -0,0 +1,58 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# files that live under /system/etc/...
+
+copy_from := \
+	etc/dbus.conf \
+	etc/init.goldfish.sh \
+	etc/hosts
+
+dont_copy := \
+	etc/init.gprs-pppd \
+	etc/ppp/chap-secrets \
+	etc/ppp/ip-down \
+	etc/ppp/ip-up
+
+copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from))
+copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from))
+
+$(copy_to) : PRIVATE_MODULE := system_etcdir
+$(copy_to) : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP)
+	$(transform-prebuilt-to-target)
+
+ALL_PREBUILT += $(copy_to)
+
+
+# files that live under /...
+
+# Only copy init.rc if the target doesn't have its own.
+ifneq ($(TARGET_PROVIDES_INIT_RC),true)
+file := $(TARGET_ROOT_OUT)/init.rc
+$(file) : $(LOCAL_PATH)/init.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+endif
+
+file := $(TARGET_ROOT_OUT)/init.goldfish.rc
+$(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP)
+	$(transform-prebuilt-to-target)
+ALL_PREBUILT += $(file)
+	
+
+# create some directories (some are mount points)
+DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \
+		sbin \
+		dev \
+		proc \
+		sys \
+		system \
+		data \
+	) \
+	$(TARGET_OUT_DATA)
+
+$(DIRS):
+	@echo Directory: $@
+	@mkdir -p $@
+
+ALL_PREBUILT += $(DIRS)
diff --git a/rootdir/etc/dbus.conf b/rootdir/etc/dbus.conf
new file mode 100644
index 0000000..75586b9
--- /dev/null
+++ b/rootdir/etc/dbus.conf
@@ -0,0 +1,27 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- Our well-known bus type, do not change this -->
+  <type>system</type>
+
+  <!-- Only allow socket-credentials-based authentication -->
+  <auth>EXTERNAL</auth>
+
+  <!-- Only listen on a local socket. (abstract=/path/to/socket 
+       means use abstract namespace, don't really create filesystem 
+       file; only Linux supports this. Use path=/whatever on other 
+       systems.) -->
+  <listen>unix:path=/dev/socket/dbus</listen>
+
+  <!-- Allow everything, D-Bus socket is protected by unix filesystem
+       permissions -->
+  <policy context="default">
+    <allow send_interface="*"/>
+    <allow receive_interface="*"/>
+    <allow own="*"/>
+    <allow user="*"/>
+    <allow send_requested_reply="true"/>
+    <allow receive_requested_reply="true"/>
+  </policy>
+</busconfig>
diff --git a/rootdir/etc/hosts b/rootdir/etc/hosts
new file mode 100644
index 0000000..99848f6
--- /dev/null
+++ b/rootdir/etc/hosts
@@ -0,0 +1 @@
+127.0.0.1		    localhost
diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc
new file mode 100644
index 0000000..96480f3
--- /dev/null
+++ b/rootdir/etc/init.goldfish.rc
@@ -0,0 +1,54 @@
+on boot
+    setprop ARGH ARGH
+    setprop net.eth0.dns1 10.0.2.3
+    setprop net.gprs.local-ip 10.0.2.15
+    setprop ro.radio.use-ppp no
+    setprop ro.build.product generic
+    setprop ro.product.device generic
+
+# fake some battery state
+    setprop status.battery.state Slow
+    setprop status.battery.level 5
+    setprop status.battery.level_raw  50
+    setprop status.battery.level_scale 9
+
+# disable some daemons the emulator doesn't want
+    stop dund
+    stop akmd
+
+    setprop ro.setupwizard.mode EMULATOR
+
+# enable Google-specific location features,
+# like NetworkLocationProvider and LocationCollector
+    setprop ro.com.google.locationfeatures 1
+
+# For the emulator, which bypasses Setup Wizard, you can specify
+# account info for the device via these two properties.  Google
+# Login Service will insert these accounts into the database when
+# it is created (ie, after a data wipe).
+#
+#   setprop ro.config.hosted_account username@hosteddomain.org:password
+#   setprop ro.config.google_account username@gmail.com:password
+#
+# You MUST have a Google account on the device, and you MAY
+# additionally have a hosted account.  No other configuration is
+# supported, and arbitrary breakage may result if you specify
+# something else.
+
+service goldfish-setup /system/etc/init.goldfish.sh
+    oneshot
+
+service qemud /system/bin/qemud
+    socket qemud_gsm     stream 666
+    socket qemud_gps     stream 666
+    socket qemud_control stream 666
+    oneshot
+
+# -Q is a special logcat option that forces the
+# program to check wether it runs on the emulator
+# if it does, it redirects its output to the device
+# named by the androidboot.console kernel option
+# if not, is simply exit immediately
+
+service goldfish-logcat /system/bin/logcat -Q
+    oneshot
diff --git a/rootdir/etc/init.goldfish.sh b/rootdir/etc/init.goldfish.sh
new file mode 100755
index 0000000..0eb0154
--- /dev/null
+++ b/rootdir/etc/init.goldfish.sh
@@ -0,0 +1,39 @@
+#!/system/bin/sh
+
+ifconfig eth0 10.0.2.15 netmask 255.255.255.0 up
+route add default gw 10.0.2.2 dev eth0
+
+qemud=`getprop.ro.kernel.android.qemud`
+if test -z "$qemud"; then
+    radio_ril=`getprop ro.kernel.android.ril`
+    if test -z "$radio_ril"; then
+        # no need for the radio interface daemon
+        # telephony is entirely emulated in Java
+        setprop ro.radio.noril yes
+        stop ril-daemon
+    fi
+fi
+
+num_dns=`getprop ro.kernel.android.ndns`
+case "$num_dns" in
+    2) setprop net.eth0.dns2 10.0.2.4
+    ;;
+    3) setprop net.eth0.dns2 10.0.2.4
+    setprop net.eth0.dns3 10.0.2.5
+    ;;
+    4) setprop net.eth0.dns2 10.0.2.4
+    setprop net.eth0.dns3 10.0.2.5
+    setprop net.eth0.dns4 10.0.2.6
+    ;;
+esac
+
+# disable boot animation for a faster boot sequence when needed
+boot_anim=`getprop ro.kernel.android.bootanim`
+case "$boot_anim" in
+    0)  setprop debug.sf.nobootanimation 1
+    ;;
+esac
+
+# this line doesn't really do anything useful. however without it the
+# previous setprop doesn't seem to apply for some really odd reason
+setprop ro.qemu.init.completed 1
diff --git a/rootdir/etc/init.gprs-pppd b/rootdir/etc/init.gprs-pppd
new file mode 100755
index 0000000..521eec9
--- /dev/null
+++ b/rootdir/etc/init.gprs-pppd
@@ -0,0 +1,23 @@
+#!/system/bin/sh
+# An unforunate wrapper script 
+# so that the exit code of pppd may be retrieved
+
+
+# this is a workaround for issue #651747
+#trap "/system/bin/sleep 1;exit 0" TERM
+
+
+PPPD_PID=
+
+/system/bin/setprop "net.gprs.ppp-exit" ""
+
+/system/bin/log -t pppd "Starting pppd"
+
+/system/bin/pppd $*
+
+PPPD_EXIT=$?
+PPPD_PID=$!
+
+/system/bin/log -t pppd "pppd exited with $PPPD_EXIT"
+
+/system/bin/setprop "net.gprs.ppp-exit" "$PPPD_EXIT"
diff --git a/rootdir/etc/init.testmenu b/rootdir/etc/init.testmenu
new file mode 100755
index 0000000..7ae16d5
--- /dev/null
+++ b/rootdir/etc/init.testmenu
@@ -0,0 +1,322 @@
+#!/system/bin/sh
+
+atdev=/dev/omap_csmi_tty0
+pppdev=/dev/omap_csmi_tty1
+
+n1=`cat /data/phoneentry1 2>/dev/null`
+n2=`cat /data/phoneentry2 2>/dev/null`
+n3=`cat /data/phoneentry3 2>/dev/null`
+n1=${n1:-"*#06#"}
+n2=${n2:-"*#06#"}
+n3=${n3:-"*#06#"}
+phoneoutputpid=
+eventoutputpid=
+notifypid=
+notifytoggle=false
+pppdpid=
+powerdidletime=120
+
+# map phone specific keys
+setkey -k 0xe4 -v 0x23 # map #
+setkey -k 0xe3 -v 0x2a # map *
+setkey -k 231 -v 513 # map send to newline
+#setkey -k 0x67 -v 0x20b # map up to scroll back
+#setkey -k 0x6c -v 0x20a # map down to scroll forward
+setkey -k 0x73 -v 0x20b # map volume up to scroll back
+setkey -k 0x72 -v 0x20a # map volume down to scroll forward
+setkey -k 0x60 -v 0x211 # map PoC to next console
+
+# tuttle keys
+setkey -k 0x38 -v 0x703 # map leftalt to alt
+setkey -k 0x9b -v 0x703 # map mail to alt
+setkey -t 8 -k 0x9b -v 0x703 # map alt-mail to alt
+setkey -t 8 -k 0x10 -v 0x21 # map alt-q to !
+setkey -t 8 -k 0x11 -v 0x31 # map alt-w to 1
+setkey -t 8 -k 0x12 -v 0x32 # map alt-e to 2
+setkey -t 8 -k 0x13 -v 0x33 # map alt-r to 3
+setkey -t 8 -k 0x14 -v 0x2b # map alt-t to +
+setkey -t 8 -k 0x15 -v 0x28 # map alt-y to (
+setkey -t 8 -k 0x16 -v 0x29 # map alt-u to )
+setkey -t 8 -k 0x17 -v 0x2d # map alt-i to -
+setkey -t 8 -k 0x18 -v 0x5f # map alt-o to _
+setkey -t 8 -k 0x19 -v 0x22 # map alt-p to "
+setkey -t 8 -k 0x1e -v 0x23 # map alt-a to #
+setkey -t 8 -k 0x1f -v 0x34 # map alt-s to 4
+setkey -t 8 -k 0x20 -v 0x35 # map alt-d to 5
+setkey -t 8 -k 0x21 -v 0x36 # map alt-f to 6
+setkey -t 8 -k 0x22 -v 0x2f # map alt-g to /
+setkey -t 8 -k 0x23 -v 0x3f # map alt-h to ?
+setkey -t 8 -k 0x24 -v 0xa3 # map alt-j to pound
+setkey -t 8 -k 0x25 -v 0x24 # map alt-k to $
+setkey -t 8 -k 0x2c -v 0x2a # map alt-z to *
+setkey -t 8 -k 0x2d -v 0x37 # map alt-x to 7
+setkey -t 8 -k 0x2e -v 0x38 # map alt-c to 8
+setkey -t 8 -k 0x2f -v 0x39 # map alt-v to 9
+setkey -t 8 -k 0x30 -v 0x7c # map alt-b to |
+setkey -t 8 -k 0x31 -v 0x40 # map alt-n to @
+setkey -t 8 -k 0x32 -v 0x3d # map alt-m to =
+setkey -t 8 -k 0x33 -v 0x3b # map alt-, to ;
+setkey -t 8 -k 0x34 -v 0x3a # map alt-. to :
+setkey -t 8 -k 0x0f -v 0x30 # map alt-tab to 0
+setkey -t 8 -k 0x67 -v 0x20b # map alt-up to scroll back
+setkey -t 8 -k 0x6c -v 0x20a # map alt-down to scroll forward
+
+while true
+do
+	echo
+	echo "------------------------------"
+	echo " 1: init commands"
+	echo " 2: call commands"
+	echo " 3: misc phone"
+	echo " 4: phone debug output"
+	echo " 5: test data connection"
+	echo " 6: start runtime"
+	echo " 7: start runtime w/output"
+	echo " 8: stop runtime"
+	echo " 9: misc"
+	echo -n ": "
+	while true
+	do
+		c=`readtty -t 50 -f -a 1234567890#`
+		case "$c" in
+			"" ) ;;
+			* ) break;
+		esac
+	done
+	echo Got key -$c-
+	case $c in
+		"1" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Print phone output"
+				echo " 2: ATQ0V1E1+CMEE=2;+CREG=0"
+				echo " 3: AT+CFUN=1"
+				echo " 4: AT+COPS=0"
+				echo " 5: AT+CREG?"
+				echo " 6: Stop phone output"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 1234560#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) kill $phoneoutputpid; cat $atdev & phoneoutputpid=$! ;;
+					"2" ) echo -e "ATQ0V1E1+CMEE=2;+CREG=0\r" >$atdev;;
+					"3" ) echo -e "AT+CFUN=1\r" >$atdev;;
+					"4" ) echo -e "AT+COPS=0\r" >$atdev;;
+					"5" ) echo -e "AT+CREG?\r" >$atdev;;
+					"6" ) kill $phoneoutputpid; phoneoutputpid= ;;
+					"0" ) break;;
+				esac
+			done
+		;;
+		"2" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Dial: ATD $n1;"
+				echo " 2: Dial: ATD $n2;"
+				echo " 3: Dial: ATD $n3;"
+				echo " 4: Set number for 1"
+				echo " 5: Set number for 2"
+				echo " 6: Set number for 3"
+				echo " 7: Dial: ATD ...;"
+				echo " 8: Hang up: ATH"
+				echo " 9: Answer: ATA"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 1234567890#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) echo "Dialing $n1"; echo -e "ATD $n1;\r" >$atdev;;
+					"2" ) echo "Dialing $n2"; echo -e "ATD $n2;\r" >$atdev;;
+					"3" ) echo "Dialing $n3"; echo -e "ATD $n3;\r" >$atdev;;
+					"4" ) echo -n "Number: "; read n1; echo $n1 >/data/phoneentry1;;
+					"5" ) echo -n "Number: "; read n2; echo $n2 >/data/phoneentry2;;
+					"6" ) echo -n "Number: "; read n3; echo $n3 >/data/phoneentry3;;
+					"7" ) echo -n "Number: "; read n; echo "Dialing $n"; echo -e "ATD $n;\r" >$atdev;;
+					"8" ) echo -e "ATH\r" >$atdev;;
+					"9" ) echo -e "ATA\r" >$atdev;;
+					"0" ) break;;
+				esac
+			done
+		;;
+		"3" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Save FFS data"
+				echo " 2: Load user FFS data"
+				echo " 3: Load system FFS data"
+				echo " 4: Reset FFS data"
+				echo " 5: Set uplink gain"
+				echo " 6: Set echo"
+				echo " 7: cat /dev/omap_csmi_battery_t"
+				echo " 8: cat /dev/omap_csmi_htc"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 123456780#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) cat /dev/omap_csmi_ffs >/data/ffsdata;;
+					"2" ) cat /data/ffsdata >/dev/omap_csmi_ffs;;
+					"3" ) cat /system/ffsdata >/dev/omap_csmi_ffs;;
+					"4" ) echo - >/dev/omap_csmi_ffs;;
+					"5" )
+						echo -n "Gain: "; read g;
+						echo gu$g >/tmp/gain;
+						cat /tmp/gain 2>/dev/null >/dev/omap_csmi_audio_tes
+					;;
+					"6" )
+						echo -n "Echo param (hex): "; read e;
+						echo "e0x$e" >/tmp/echo;
+						cat /tmp/echo 2>/dev/null >/dev/omap_csmi_audio_tes
+					;;
+					"7" ) cat /dev/omap_csmi_battery_t;;
+					"8" ) cat /dev/omap_csmi_htc;;
+					"0" ) break;;
+				esac
+			done
+		;;
+		"4" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Toggle debug I/O"
+				echo " 2: Toggle debug Flow"
+				echo " 3: Toggle debug Interrupt"
+				echo " 4: Toggle debug Info"
+				echo " 5: Toggle GSM run state"
+				echo " 6: Clear GSM data area"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 1234560#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) echo -n "i" >/sys/devices/system/omap_csmi/debug;;
+					"2" ) echo -n "f" >/sys/devices/system/omap_csmi/debug;;
+					"3" ) echo -n "I" >/sys/devices/system/omap_csmi/debug;;
+					"4" ) echo -n "F" >/sys/devices/system/omap_csmi/debug;;
+					"5" ) echo -n "s" >/sys/devices/system/omap_csmi/debug;;
+					"6" ) echo -n "c" >/sys/devices/system/omap_csmi/debug;;
+					"0" ) break;;
+				esac
+			done
+		;;
+		"5" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Start pppd - userspace"
+				echo " 2: Start pppd - kernel"
+				echo " 3: Start pppd - kernel <at1"
+				echo " 4: Configure ppp data to at2"
+				echo " 5: Test with HTTP GET"
+				echo " 6: Kill pppd"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 1234560#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) kill $pppdpid; pppd notty < $pppdev > $pppdev & pppdpid=$!;;
+					"2" ) kill $pppdpid; pppd nodetach $pppdev & pppdpid=$!;;
+					"3" ) kill &pppdpid; pppd nodetach $pppdev connect "sh -c \"chat -v -f /etc/ppp/connect-data <$atdev >$atdev\"" & pppdpid=$!;;
+					"4" ) echo -e 'AT%DATA=2,"UART",1,,"SER","UART",0\r' >$atdev;;
+					"5" ) test-data-connection;;
+					"6" ) kill $pppdpid; pppdpid=;;
+					"0" ) break;;
+				esac
+			done
+		;;
+		"6" )
+			echo
+			echo ------------------------
+			echo Starting android runtime
+			echo ------------------------
+			start
+		;;
+		"7" )
+			echo
+			echo ------------------------
+			echo Starting android runtime
+			echo ------------------------
+			if exists /data/singleproc
+			then
+				single_process="-s"
+			else
+				single_process=""
+			fi
+			start runtime $single_process
+		;;
+		"8" )
+			stop
+		;;
+		"9" )
+			while true; do
+				echo
+				echo "------------------------------"
+				echo " 1: Print events"
+				echo " 2: Stop event output"
+				if $notifytoggle
+				then
+					echo " 3: stop notify"
+				else
+					echo " 3: notify /sys/android_power"
+				fi
+				echo " 4: start powerd"
+				echo " 5: start powerd verbose"
+				echo " 6: stop powerd"
+				echo " 7: set powerd idletime ($powerdidletime)"
+				echo " 8: start multitap shell"
+				if exists /data/singleproc
+				then
+					echo " 9: enable multiprocess"
+				else
+					echo " 9: disable multiprocess"
+				fi
+				echo " c: start shell"
+				echo " 0: back"
+				echo -n ": "
+				c=`readtty -f -a 1234567890c#`
+				echo Got key -$c-
+				case "$c" in
+					"1" ) kill $eventoutputpid; getevent & eventoutputpid=$! ;;
+					"2" ) kill $eventoutputpid; eventoutputpid= ;;
+					"3" )
+						if $notifytoggle
+						then
+							kill $notifypid
+							notifypid=
+							notifytoggle=false
+						else
+							kill $notifypid
+							notify -m 0x00000002 -c 0 -p -v 0 -w 30 /sys/android_power &
+							notifypid=$!
+							notifytoggle=true
+						fi
+					;;
+					"4" ) start powerd -i $powerdidletime ;;
+					"5" ) start powerd -i $powerdidletime -v ;;
+					"6" ) stop powerd ;;
+					"7" ) echo -n "Idle time (seconds): "; read powerdidletime ;;
+					"8" )
+						readtty -f -p -t 10 -e "[ ~" | sh -i
+					;;
+					"9" )
+						if exists /data/singleproc
+						then
+							echo "Enabling multiprocess environment."
+							rm /data/singleproc
+						else
+							echo "Disabling multiprocess environment."
+							echo >/data/singleproc "true"
+						fi
+					;;
+					"c" ) sh -i <>/dev/tty0 1>&0 2>&1 ;;
+					"0" ) break;;
+				esac
+			done
+		;;
+	esac
+done
+
diff --git a/rootdir/etc/mountd.conf b/rootdir/etc/mountd.conf
new file mode 100644
index 0000000..094a2c7
--- /dev/null
+++ b/rootdir/etc/mountd.conf
@@ -0,0 +1,19 @@
+## mountd configuration file
+
+## add a mount entry for each mount point to be managed by mountd
+mount {
+    ## root block device with partition map or raw FAT file system
+    block_device    /dev/block/mmcblk0
+        
+    ## mount point for block device
+    mount_point     /sdcard
+    
+    ## true if this mount point can be shared via USB mass storage
+    enable_ums      true
+    
+    ## path to the UMS driver file for specifying the block device path  
+    ## use this for the mass_storage function driver
+    driver_store_path   /sys/devices/platform/usb_mass_storage/lun0/file
+    ## use this for android_usb composite gadget driver
+    ##driver_store_path   /sys/devices/platform/msm_hsusb/gadget/lun0/file
+}
diff --git a/rootdir/etc/ppp/chap-secrets b/rootdir/etc/ppp/chap-secrets
new file mode 100644
index 0000000..6546b0f
--- /dev/null
+++ b/rootdir/etc/ppp/chap-secrets
@@ -0,0 +1,2 @@
+* * bogus
+
diff --git a/rootdir/etc/ppp/ip-down b/rootdir/etc/ppp/ip-down
new file mode 100755
index 0000000..672fa1e
--- /dev/null
+++ b/rootdir/etc/ppp/ip-down
@@ -0,0 +1,14 @@
+#!/system/bin/sh
+case $1 in
+    ppp1)
+	echo 0 > /proc/sys/net/ipv4/ip_forward;
+	;;
+esac
+
+# Use interface name if linkname is not available
+NAME=${LINKNAME:-"$1"}
+
+/system/bin/setprop "net.$NAME.dns1" "$DNS1"
+/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
+/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
+/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
diff --git a/rootdir/etc/ppp/ip-up b/rootdir/etc/ppp/ip-up
new file mode 100755
index 0000000..cb2d577
--- /dev/null
+++ b/rootdir/etc/ppp/ip-up
@@ -0,0 +1,24 @@
+#!/system/bin/sh
+case $1 in
+    ppp1)
+	/android/bin/iptables --flush;
+	/android/bin/iptables --table nat --flush;
+	/android/bin/iptables --delete-chain;
+	/android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE;
+	/android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT;
+	echo 0 > /proc/sys/net/ipv4/ip_forward;
+	echo 1 > /proc/sys/net/ipv4/ip_forward;
+	;;
+    ppp0)
+        /system/bin/setprop "net.interfaces.defaultroute" "gprs"
+        ;;
+esac
+
+# Use interface name if linkname is not available
+NAME=${LINKNAME:-"$1"}
+
+/system/bin/setprop "net.$NAME.dns1" "$DNS1"
+/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
+/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
+/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
+
diff --git a/rootdir/init.rc b/rootdir/init.rc
new file mode 100644
index 0000000..3f8c6a0
--- /dev/null
+++ b/rootdir/init.rc
@@ -0,0 +1,245 @@
+
+on init
+
+sysclktz 0
+
+loglevel 3
+
+# setup the global environment
+    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
+    export LD_LIBRARY_PATH /system/lib
+    export ANDROID_BOOTLOGO 1
+    export ANDROID_ROOT /system
+    export ANDROID_ASSETS /system/app
+    export ANDROID_DATA /data
+    export EXTERNAL_STORAGE /sdcard
+    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar
+
+# Backward compatibility
+    symlink /system/etc /etc
+
+# create mountpoints and mount tmpfs on sqlite_stmt_journals
+    mkdir /sdcard 0000 system system
+    mkdir /system
+    mkdir /data 0771 system system
+    mkdir /cache 0770 system cache
+    mkdir /sqlite_stmt_journals 01777 root root
+    mount tmpfs tmpfs /sqlite_stmt_journals size=4m
+
+    mount rootfs rootfs / ro remount
+
+    write /proc/sys/kernel/panic_on_oops 1
+    write /proc/sys/kernel/hung_task_timeout_secs 0
+    write /proc/cpu/alignment 4
+    write /proc/sys/kernel/sched_latency_ns 10000000
+    write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
+
+# mount mtd partitions
+    # Mount /system rw first to give the filesystem a chance to save a checkpoint
+    mount yaffs2 mtd@system /system 
+    mount yaffs2 mtd@system /system ro remount
+
+    # We chown/chmod /data again so because mount is run as root + defaults
+    mount yaffs2 mtd@userdata /data nosuid nodev
+    chown system system /data
+    chmod 0771 /data
+
+    # Same reason as /data above
+    mount yaffs2 mtd@cache /cache nosuid nodev
+    chown system cache /cache
+    chmod 0770 /cache
+
+    # This may have been created by the recovery system with odd permissions
+    chown system system /cache/recovery
+    chmod 0770 /cache/recovery
+
+# create basic filesystem structure
+    mkdir /data/misc 01771 system misc
+    mkdir /data/misc/hcid 0770 bluetooth bluetooth
+    mkdir /data/local 0771 shell shell
+    mkdir /data/local/tmp 0771 shell shell
+    mkdir /data/data 0771 system system
+    mkdir /data/app-private 0771 system system
+    mkdir /data/app 0771 system system
+    mkdir /data/property 0700 root root
+
+    # create dalvik-cache and double-check the perms
+    mkdir /data/dalvik-cache 0771 system system
+    chown system system /data/dalvik-cache
+    chmod 0771 /data/dalvik-cache
+
+    # create the lost+found directories, so as to enforce our permissions
+    mkdir /data/lost+found 0770
+    mkdir /cache/lost+found 0770
+
+    # double check the perms, in case lost+found already exists, and set owner
+    chown root root /data/lost+found
+    chmod 0770 /data/lost+found
+    chown root root /cache/lost+found
+    chmod 0770 /cache/lost+found
+
+on boot
+# basic network init
+    ifup lo
+    hostname localhost
+    domainname localdomain
+
+# set RLIMIT_NICE to allow priorities from 19 to -20
+    setrlimit 13 40 40
+
+# Define the oom_adj values for the classes of processes that can be
+# killed by the kernel.  These are used in ActivityManagerService.
+    setprop ro.FOREGROUND_APP_ADJ 0
+    setprop ro.VISIBLE_APP_ADJ 1
+    setprop ro.SECONDARY_SERVER_ADJ 2
+    setprop ro.HIDDEN_APP_MIN_ADJ 7
+    setprop ro.CONTENT_PROVIDER_ADJ 14
+    setprop ro.EMPTY_APP_ADJ 15
+
+# Define the memory thresholds at which the above process classes will
+# be killed.  These numbers are in pages (4k).
+    setprop ro.FOREGROUND_APP_MEM 1536
+    setprop ro.VISIBLE_APP_MEM 2048
+    setprop ro.SECONDARY_SERVER_MEM 4096
+    setprop ro.HIDDEN_APP_MEM 5120
+    setprop ro.CONTENT_PROVIDER_MEM 5632
+    setprop ro.EMPTY_APP_MEM 6144
+
+# Write value must be consistent with the above properties.
+    write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15
+
+    write /proc/sys/vm/overcommit_memory 1
+    write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
+
+    # Set init its forked children's oom_adj.
+    write /proc/1/oom_adj -16
+
+    # Permissions for System Server and daemons.
+    chown radio system /sys/android_power/state
+    chown radio system /sys/android_power/request_state
+    chown radio system /sys/android_power/acquire_full_wake_lock
+    chown radio system /sys/android_power/acquire_partial_wake_lock
+    chown radio system /sys/android_power/release_wake_lock
+    chown radio system /sys/power/state
+    chown radio system /sys/power/wake_lock
+    chown radio system /sys/power/wake_unlock
+    chmod 0660 /sys/power/state
+    chmod 0660 /sys/power/wake_lock
+    chmod 0660 /sys/power/wake_unlock
+    chown system system /sys/class/timed_output/vibrator/enable
+    chown system system /sys/class/leds/keyboard-backlight/brightness
+    chown system system /sys/class/leds/lcd-backlight/brightness
+    chown system system /sys/class/leds/button-backlight/brightness
+    chown system system /sys/class/leds/red/brightness
+    chown system system /sys/class/leds/green/brightness
+    chown system system /sys/class/leds/blue/brightness
+    chown system system /sys/class/leds/red/device/grpfreq
+    chown system system /sys/class/leds/red/device/grppwm
+    chown system system /sys/class/leds/red/device/blink
+    chown system system /sys/class/leds/red/brightness
+    chown system system /sys/class/leds/green/brightness
+    chown system system /sys/class/leds/blue/brightness
+    chown system system /sys/class/leds/red/device/grpfreq
+    chown system system /sys/class/leds/red/device/grppwm
+    chown system system /sys/class/leds/red/device/blink
+    chown system system /sys/class/timed_output/vibrator/enable
+    chown system system /sys/module/sco/parameters/disable_esco
+    chown system system /sys/kernel/ipv4/tcp_wmem_min
+    chown system system /sys/kernel/ipv4/tcp_wmem_def
+    chown system system /sys/kernel/ipv4/tcp_wmem_max
+    chown system system /sys/kernel/ipv4/tcp_rmem_min
+    chown system system /sys/kernel/ipv4/tcp_rmem_def
+    chown system system /sys/kernel/ipv4/tcp_rmem_max
+    chown root radio /proc/cmdline
+
+# Define TCP buffer sizes for various networks
+#   ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax,
+    setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208
+    setprop net.tcp.buffersize.wifi    4095,87380,110208,4096,16384,110208
+    setprop net.tcp.buffersize.umts    4094,87380,110208,4096,16384,110208
+    setprop net.tcp.buffersize.edge    4093,26280,35040,4096,16384,35040
+    setprop net.tcp.buffersize.gprs    4092,8760,11680,4096,8760,11680
+
+    class_start default
+
+## Daemon processes to be run by init.
+##
+service console /system/bin/sh
+    console
+
+# adbd is controlled by the persist.service.adb.enable system property
+service adbd /sbin/adbd
+    disabled
+
+# adbd on at boot in emulator
+on property:ro.kernel.qemu=1
+    start adbd
+
+on property:persist.service.adb.enable=1
+    start adbd
+
+on property:persist.service.adb.enable=0
+    stop adbd
+
+service servicemanager /system/bin/servicemanager
+    user system
+    critical
+    onrestart restart zygote
+    onrestart restart media
+
+service mountd /system/bin/mountd
+    socket mountd stream 0660 root mount
+
+service debuggerd /system/bin/debuggerd
+
+service ril-daemon /system/bin/rild
+    socket rild stream 660 root radio
+    socket rild-debug stream 660 radio system
+    user root
+    group radio cache inet misc
+
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
+    socket zygote stream 666
+    onrestart write /sys/android_power/request_state wake
+    onrestart write /sys/power/state on
+
+service media /system/bin/mediaserver
+    user media
+    group system audio camera graphics inet net_bt net_bt_admin
+
+service bootsound /system/bin/playmp3
+    user media
+    group audio
+    oneshot
+
+service dbus /system/bin/dbus-daemon --system --nofork
+    socket dbus stream 660 bluetooth bluetooth
+    user bluetooth
+    group bluetooth net_bt_admin
+
+#STOPSHIP: disable the verbose logging
+service hcid /system/bin/logwrapper /system/bin/hcid -d -s -n -f /etc/bluez/hcid.conf
+    socket bluetooth stream 660 bluetooth bluetooth
+    socket dbus_bluetooth stream 660 bluetooth bluetooth
+    # init.rc does not yet support applying capabilities, so run as root and
+    # let hcid drop uid to bluetooth with the right linux capabilities
+    group bluetooth net_bt_admin misc
+    disabled
+
+service hfag /system/bin/sdptool add --channel=10 HFAG
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
+
+service hsag /system/bin/sdptool add --channel=11 HSAG
+    user bluetooth
+    group bluetooth net_bt_admin
+    disabled
+    oneshot
+
+service installd /system/bin/installd
+    socket installd stream 600 system system
+
+service flash_recovery /system/bin/flash_image recovery /system/recovery.img
+    oneshot
diff --git a/sh/Android.mk b/sh/Android.mk
new file mode 100644
index 0000000..09bb6ac
--- /dev/null
+++ b/sh/Android.mk
@@ -0,0 +1,49 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	alias.c \
+	arith.c \
+	arith_lex.c \
+	builtins.c \
+	cd.c \
+	error.c \
+	eval.c \
+	exec.c \
+	expand.c \
+	input.c \
+	jobs.c \
+	main.c \
+	memalloc.c \
+	miscbltin.c \
+	mystring.c \
+	nodes.c \
+	options.c \
+	parser.c \
+	redir.c \
+	show.c \
+	syntax.c \
+	trap.c \
+	output.c \
+	var.c \
+	bltin/echo.c \
+	init.c
+
+LOCAL_MODULE:= sh
+
+LOCAL_CFLAGS += -DSHELL
+
+make_ash_files: PRIVATE_SRC_FILES := $(SRC_FILES)
+make_ash_files: PRIVATE_CFLAGS := $(LOCAL_CFLAGS)
+make_ash_files:
+	p4 edit arith.c arith_lex.c arith.h builtins.h builtins.c 
+	p4 edit init.c nodes.c nodes.h token.h 
+	sh ./mktokens
+	bison -o arith.c arith.y
+	flex -o arith_lex.c arith_lex.l
+	perl -ne 'print if ( /^\#\s*define\s+ARITH/ );' < arith.c > arith.h
+	sh ./mkbuiltins shell.h builtins.def . -Wall -O2
+	sh ./mknodes.sh nodetypes nodes.c.pat .
+	sh ./mkinit.sh $(PRIVATE_SRC_FILES) 
+
+include $(BUILD_EXECUTABLE)
diff --git a/sh/MODULE_LICENSE_BSD b/sh/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sh/MODULE_LICENSE_BSD
diff --git a/sh/NOTICE b/sh/NOTICE
new file mode 100644
index 0000000..49a66d2
--- /dev/null
+++ b/sh/NOTICE
@@ -0,0 +1,31 @@
+Copyright (c) 1991, 1993
+     The Regents of the University of California.  All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kenneth Almquist.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
diff --git a/sh/TOUR b/sh/TOUR
new file mode 100644
index 0000000..f5c00c4
--- /dev/null
+++ b/sh/TOUR
@@ -0,0 +1,357 @@
+#	$NetBSD: TOUR,v 1.8 1996/10/16 14:24:56 christos Exp $
+#	@(#)TOUR	8.1 (Berkeley) 5/31/93
+
+NOTE -- This is the original TOUR paper distributed with ash and
+does not represent the current state of the shell.  It is provided anyway
+since it provides helpful information for how the shell is structured,
+but be warned that things have changed -- the current shell is
+still under development.
+
+================================================================
+
+                       A Tour through Ash
+
+               Copyright 1989 by Kenneth Almquist.
+
+
+DIRECTORIES:  The subdirectory bltin contains commands which can
+be compiled stand-alone.  The rest of the source is in the main
+ash directory.
+
+SOURCE CODE GENERATORS:  Files whose names begin with "mk" are
+programs that generate source code.  A complete list of these
+programs is:
+
+        program         intput files        generates
+        -------         ------------        ---------
+        mkbuiltins      builtins            builtins.h builtins.c
+        mkinit          *.c                 init.c
+        mknodes         nodetypes           nodes.h nodes.c
+        mksignames          -               signames.h signames.c
+        mksyntax            -               syntax.h syntax.c
+        mktokens            -               token.h
+        bltin/mkexpr    unary_op binary_op  operators.h operators.c
+
+There are undoubtedly too many of these.  Mkinit searches all the
+C source files for entries looking like:
+
+        INIT {
+              x = 1;    /* executed during initialization */
+        }
+
+        RESET {
+              x = 2;    /* executed when the shell does a longjmp
+                           back to the main command loop */
+        }
+
+        SHELLPROC {
+              x = 3;    /* executed when the shell runs a shell procedure */
+        }
+
+It pulls this code out into routines which are when particular
+events occur.  The intent is to improve modularity by isolating
+the information about which modules need to be explicitly
+initialized/reset within the modules themselves.
+
+Mkinit recognizes several constructs for placing declarations in
+the init.c file.
+        INCLUDE "file.h"
+includes a file.  The storage class MKINIT makes a declaration
+available in the init.c file, for example:
+        MKINIT int funcnest;    /* depth of function calls */
+MKINIT alone on a line introduces a structure or union declara-
+tion:
+        MKINIT
+        struct redirtab {
+              short renamed[10];
+        };
+Preprocessor #define statements are copied to init.c without any
+special action to request this.
+
+INDENTATION:  The ash source is indented in multiples of six
+spaces.  The only study that I have heard of on the subject con-
+cluded that the optimal amount to indent is in the range of four
+to six spaces.  I use six spaces since it is not too big a jump
+from the widely used eight spaces.  If you really hate six space
+indentation, use the adjind (source included) program to change
+it to something else.
+
+EXCEPTIONS:  Code for dealing with exceptions appears in
+exceptions.c.  The C language doesn't include exception handling,
+so I implement it using setjmp and longjmp.  The global variable
+exception contains the type of exception.  EXERROR is raised by
+calling error.  EXINT is an interrupt.  EXSHELLPROC is an excep-
+tion which is raised when a shell procedure is invoked.  The pur-
+pose of EXSHELLPROC is to perform the cleanup actions associated
+with other exceptions.  After these cleanup actions, the shell
+can interpret a shell procedure itself without exec'ing a new
+copy of the shell.
+
+INTERRUPTS:  In an interactive shell, an interrupt will cause an
+EXINT exception to return to the main command loop.  (Exception:
+EXINT is not raised if the user traps interrupts using the trap
+command.)  The INTOFF and INTON macros (defined in exception.h)
+provide uninterruptable critical sections.  Between the execution
+of INTOFF and the execution of INTON, interrupt signals will be
+held for later delivery.  INTOFF and INTON can be nested.
+
+MEMALLOC.C:  Memalloc.c defines versions of malloc and realloc
+which call error when there is no memory left.  It also defines a
+stack oriented memory allocation scheme.  Allocating off a stack
+is probably more efficient than allocation using malloc, but the
+big advantage is that when an exception occurs all we have to do
+to free up the memory in use at the time of the exception is to
+restore the stack pointer.  The stack is implemented using a
+linked list of blocks.
+
+STPUTC:  If the stack were contiguous, it would be easy to store
+strings on the stack without knowing in advance how long the
+string was going to be:
+        p = stackptr;
+        *p++ = c;       /* repeated as many times as needed */
+        stackptr = p;
+The folloing three macros (defined in memalloc.h) perform these
+operations, but grow the stack if you run off the end:
+        STARTSTACKSTR(p);
+        STPUTC(c, p);   /* repeated as many times as needed */
+        grabstackstr(p);
+
+We now start a top-down look at the code:
+
+MAIN.C:  The main routine performs some initialization, executes
+the user's profile if necessary, and calls cmdloop.  Cmdloop is
+repeatedly parses and executes commands.
+
+OPTIONS.C:  This file contains the option processing code.  It is
+called from main to parse the shell arguments when the shell is
+invoked, and it also contains the set builtin.  The -i and -j op-
+tions (the latter turns on job control) require changes in signal
+handling.  The routines setjobctl (in jobs.c) and setinteractive
+(in trap.c) are called to handle changes to these options.
+
+PARSING:  The parser code is all in parser.c.  A recursive des-
+cent parser is used.  Syntax tables (generated by mksyntax) are
+used to classify characters during lexical analysis.  There are
+three tables:  one for normal use, one for use when inside single
+quotes, and one for use when inside double quotes.  The tables
+are machine dependent because they are indexed by character vari-
+ables and the range of a char varies from machine to machine.
+
+PARSE OUTPUT:  The output of the parser consists of a tree of
+nodes.  The various types of nodes are defined in the file node-
+types.
+
+Nodes of type NARG are used to represent both words and the con-
+tents of here documents.  An early version of ash kept the con-
+tents of here documents in temporary files, but keeping here do-
+cuments in memory typically results in significantly better per-
+formance.  It would have been nice to make it an option to use
+temporary files for here documents, for the benefit of small
+machines, but the code to keep track of when to delete the tem-
+porary files was complex and I never fixed all the bugs in it.
+(AT&T has been maintaining the Bourne shell for more than ten
+years, and to the best of my knowledge they still haven't gotten
+it to handle temporary files correctly in obscure cases.)
+
+The text field of a NARG structure points to the text of the
+word.  The text consists of ordinary characters and a number of
+special codes defined in parser.h.  The special codes are:
+
+        CTLVAR              Variable substitution
+        CTLENDVAR           End of variable substitution
+        CTLBACKQ            Command substitution
+        CTLBACKQ|CTLQUOTE   Command substitution inside double quotes
+        CTLESC              Escape next character
+
+A variable substitution contains the following elements:
+
+        CTLVAR type name '=' [ alternative-text CTLENDVAR ]
+
+The type field is a single character specifying the type of sub-
+stitution.  The possible types are:
+
+        VSNORMAL            $var
+        VSMINUS             ${var-text}
+        VSMINUS|VSNUL       ${var:-text}
+        VSPLUS              ${var+text}
+        VSPLUS|VSNUL        ${var:+text}
+        VSQUESTION          ${var?text}
+        VSQUESTION|VSNUL    ${var:?text}
+        VSASSIGN            ${var=text}
+        VSASSIGN|VSNUL      ${var=text}
+
+In addition, the type field will have the VSQUOTE flag set if the
+variable is enclosed in double quotes.  The name of the variable
+comes next, terminated by an equals sign.  If the type is not
+VSNORMAL, then the text field in the substitution follows, ter-
+minated by a CTLENDVAR byte.
+
+Commands in back quotes are parsed and stored in a linked list.
+The locations of these commands in the string are indicated by
+CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether
+the back quotes were enclosed in double quotes.
+
+The character CTLESC escapes the next character, so that in case
+any of the CTL characters mentioned above appear in the input,
+they can be passed through transparently.  CTLESC is also used to
+escape '*', '?', '[', and '!' characters which were quoted by the
+user and thus should not be used for file name generation.
+
+CTLESC characters have proved to be particularly tricky to get
+right.  In the case of here documents which are not subject to
+variable and command substitution, the parser doesn't insert any
+CTLESC characters to begin with (so the contents of the text
+field can be written without any processing).  Other here docu-
+ments, and words which are not subject to splitting and file name
+generation, have the CTLESC characters removed during the vari-
+able and command substitution phase.  Words which are subject
+splitting and file name generation have the CTLESC characters re-
+moved as part of the file name phase.
+
+EXECUTION:  Command execution is handled by the following files:
+        eval.c     The top level routines.
+        redir.c    Code to handle redirection of input and output.
+        jobs.c     Code to handle forking, waiting, and job control.
+        exec.c     Code to to path searches and the actual exec sys call.
+        expand.c   Code to evaluate arguments.
+        var.c      Maintains the variable symbol table.  Called from expand.c.
+
+EVAL.C:  Evaltree recursively executes a parse tree.  The exit
+status is returned in the global variable exitstatus.  The alter-
+native entry evalbackcmd is called to evaluate commands in back
+quotes.  It saves the result in memory if the command is a buil-
+tin; otherwise it forks off a child to execute the command and
+connects the standard output of the child to a pipe.
+
+JOBS.C:  To create a process, you call makejob to return a job
+structure, and then call forkshell (passing the job structure as
+an argument) to create the process.  Waitforjob waits for a job
+to complete.  These routines take care of process groups if job
+control is defined.
+
+REDIR.C:  Ash allows file descriptors to be redirected and then
+restored without forking off a child process.  This is accom-
+plished by duplicating the original file descriptors.  The redir-
+tab structure records where the file descriptors have be dupli-
+cated to.
+
+EXEC.C:  The routine find_command locates a command, and enters
+the command in the hash table if it is not already there.  The
+third argument specifies whether it is to print an error message
+if the command is not found.  (When a pipeline is set up,
+find_command is called for all the commands in the pipeline be-
+fore any forking is done, so to get the commands into the hash
+table of the parent process.  But to make command hashing as
+transparent as possible, we silently ignore errors at that point
+and only print error messages if the command cannot be found
+later.)
+
+The routine shellexec is the interface to the exec system call.
+
+EXPAND.C:  Arguments are processed in three passes.  The first
+(performed by the routine argstr) performs variable and command
+substitution.  The second (ifsbreakup) performs word splitting
+and the third (expandmeta) performs file name generation.  If the
+"/u" directory is simulated, then when "/u/username" is replaced
+by the user's home directory, the flag "didudir" is set.  This
+tells the cd command that it should print out the directory name,
+just as it would if the "/u" directory were implemented using
+symbolic links.
+
+VAR.C:  Variables are stored in a hash table.  Probably we should
+switch to extensible hashing.  The variable name is stored in the
+same string as the value (using the format "name=value") so that
+no string copying is needed to create the environment of a com-
+mand.  Variables which the shell references internally are preal-
+located so that the shell can reference the values of these vari-
+ables without doing a lookup.
+
+When a program is run, the code in eval.c sticks any environment
+variables which precede the command (as in "PATH=xxx command") in
+the variable table as the simplest way to strip duplicates, and
+then calls "environment" to get the value of the environment.
+There are two consequences of this.  First, if an assignment to
+PATH precedes the command, the value of PATH before the assign-
+ment must be remembered and passed to shellexec.  Second, if the
+program turns out to be a shell procedure, the strings from the
+environment variables which preceded the command must be pulled
+out of the table and replaced with strings obtained from malloc,
+since the former will automatically be freed when the stack (see
+the entry on memalloc.c) is emptied.
+
+BUILTIN COMMANDS:  The procedures for handling these are scat-
+tered throughout the code, depending on which location appears
+most appropriate.  They can be recognized because their names al-
+ways end in "cmd".  The mapping from names to procedures is
+specified in the file builtins, which is processed by the mkbuil-
+tins command.
+
+A builtin command is invoked with argc and argv set up like a
+normal program.  A builtin command is allowed to overwrite its
+arguments.  Builtin routines can call nextopt to do option pars-
+ing.  This is kind of like getopt, but you don't pass argc and
+argv to it.  Builtin routines can also call error.  This routine
+normally terminates the shell (or returns to the main command
+loop if the shell is interactive), but when called from a builtin
+command it causes the builtin command to terminate with an exit
+status of 2.
+
+The directory bltins contains commands which can be compiled in-
+dependently but can also be built into the shell for efficiency
+reasons.  The makefile in this directory compiles these programs
+in the normal fashion (so that they can be run regardless of
+whether the invoker is ash), but also creates a library named
+bltinlib.a which can be linked with ash.  The header file bltin.h
+takes care of most of the differences between the ash and the
+stand-alone environment.  The user should call the main routine
+"main", and #define main to be the name of the routine to use
+when the program is linked into ash.  This #define should appear
+before bltin.h is included; bltin.h will #undef main if the pro-
+gram is to be compiled stand-alone.
+
+CD.C:  This file defines the cd and pwd builtins.  The pwd com-
+mand runs /bin/pwd the first time it is invoked (unless the user
+has already done a cd to an absolute pathname), but then
+remembers the current directory and updates it when the cd com-
+mand is run, so subsequent pwd commands run very fast.  The main
+complication in the cd command is in the docd command, which
+resolves symbolic links into actual names and informs the user
+where the user ended up if he crossed a symbolic link.
+
+SIGNALS:  Trap.c implements the trap command.  The routine set-
+signal figures out what action should be taken when a signal is
+received and invokes the signal system call to set the signal ac-
+tion appropriately.  When a signal that a user has set a trap for
+is caught, the routine "onsig" sets a flag.  The routine dotrap
+is called at appropriate points to actually handle the signal.
+When an interrupt is caught and no trap has been set for that
+signal, the routine "onint" in error.c is called.
+
+OUTPUT:  Ash uses it's own output routines.  There are three out-
+put structures allocated.  "Output" represents the standard out-
+put, "errout" the standard error, and "memout" contains output
+which is to be stored in memory.  This last is used when a buil-
+tin command appears in backquotes, to allow its output to be col-
+lected without doing any I/O through the UNIX operating system.
+The variables out1 and out2 normally point to output and errout,
+respectively, but they are set to point to memout when appropri-
+ate inside backquotes.
+
+INPUT:  The basic input routine is pgetc, which reads from the
+current input file.  There is a stack of input files; the current
+input file is the top file on this stack.  The code allows the
+input to come from a string rather than a file.  (This is for the
+-c option and the "." and eval builtin commands.)  The global
+variable plinno is saved and restored when files are pushed and
+popped from the stack.  The parser routines store the number of
+the current line in this variable.
+
+DEBUGGING:  If DEBUG is defined in shell.h, then the shell will
+write debugging information to the file $HOME/trace.  Most of
+this is done using the TRACE macro, which takes a set of printf
+arguments inside two sets of parenthesis.  Example:
+"TRACE(("n=%d0, n))".  The double parenthesis are necessary be-
+cause the preprocessor can't handle functions with a variable
+number of arguments.  Defining DEBUG also causes the shell to
+generate a core dump if it is sent a quit signal.  The tracing
+code is in show.c.
diff --git a/sh/alias.c b/sh/alias.c
new file mode 100644
index 0000000..59a3dc1
--- /dev/null
+++ b/sh/alias.c
@@ -0,0 +1,273 @@
+/*	$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)alias.c	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "shell.h"
+#include "input.h"
+#include "output.h"
+#include "error.h"
+#include "memalloc.h"
+#include "mystring.h"
+#include "alias.h"
+#include "options.h"	/* XXX for argptr (should remove?) */
+#include "var.h"
+
+#define ATABSIZE 39
+
+struct alias *atab[ATABSIZE];
+
+STATIC void setalias(char *, char *);
+STATIC int unalias(char *);
+STATIC struct alias **hashalias(char *);
+
+STATIC
+void
+setalias(char *name, char *val)
+{
+	struct alias *ap, **app;
+
+	app = hashalias(name);
+	for (ap = *app; ap; ap = ap->next) {
+		if (equal(name, ap->name)) {
+			INTOFF;
+			ckfree(ap->val);
+			ap->val	= savestr(val);
+			INTON;
+			return;
+		}
+	}
+	/* not found */
+	INTOFF;
+	ap = ckmalloc(sizeof (struct alias));
+	ap->name = savestr(name);
+	/*
+	 * XXX - HACK: in order that the parser will not finish reading the
+	 * alias value off the input before processing the next alias, we
+	 * dummy up an extra space at the end of the alias.  This is a crock
+	 * and should be re-thought.  The idea (if you feel inclined to help)
+	 * is to avoid alias recursions.  The mechanism used is: when
+	 * expanding an alias, the value of the alias is pushed back on the
+	 * input as a string and a pointer to the alias is stored with the
+	 * string.  The alias is marked as being in use.  When the input
+	 * routine finishes reading the string, it markes the alias not
+	 * in use.  The problem is synchronization with the parser.  Since
+	 * it reads ahead, the alias is marked not in use before the
+	 * resulting token(s) is next checked for further alias sub.  The
+	 * H A C K is that we add a little fluff after the alias value
+	 * so that the string will not be exhausted.  This is a good
+	 * idea ------- ***NOT***
+	 */
+#ifdef notyet
+	ap->val = savestr(val);
+#else /* hack */
+	{
+	int len = strlen(val);
+	ap->val = ckmalloc(len + 2);
+	memcpy(ap->val, val, len);
+	ap->val[len] = ' ';	/* fluff */
+	ap->val[len+1] = '\0';
+	}
+#endif
+	ap->next = *app;
+	*app = ap;
+	INTON;
+}
+
+STATIC int
+unalias(char *name)
+{
+	struct alias *ap, **app;
+
+	app = hashalias(name);
+
+	for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
+		if (equal(name, ap->name)) {
+			/*
+			 * if the alias is currently in use (i.e. its
+			 * buffer is being used by the input routine) we
+			 * just null out the name instead of freeing it.
+			 * We could clear it out later, but this situation
+			 * is so rare that it hardly seems worth it.
+			 */
+			if (ap->flag & ALIASINUSE)
+				*ap->name = '\0';
+			else {
+				INTOFF;
+				*app = ap->next;
+				ckfree(ap->name);
+				ckfree(ap->val);
+				ckfree(ap);
+				INTON;
+			}
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
+#ifdef mkinit
+MKINIT void rmaliases(void);
+
+SHELLPROC {
+	rmaliases();
+}
+#endif
+
+void
+rmaliases(void)
+{
+	struct alias *ap, *tmp;
+	int i;
+
+	INTOFF;
+	for (i = 0; i < ATABSIZE; i++) {
+		ap = atab[i];
+		atab[i] = NULL;
+		while (ap) {
+			ckfree(ap->name);
+			ckfree(ap->val);
+			tmp = ap;
+			ap = ap->next;
+			ckfree(tmp);
+		}
+	}
+	INTON;
+}
+
+struct alias *
+lookupalias(char *name, int check)
+{
+	struct alias *ap = *hashalias(name);
+
+	for (; ap; ap = ap->next) {
+		if (equal(name, ap->name)) {
+			if (check && (ap->flag & ALIASINUSE))
+				return (NULL);
+			return (ap);
+		}
+	}
+
+	return (NULL);
+}
+
+char *
+get_alias_text(char *name)
+{
+	struct alias *ap;
+
+	ap = lookupalias(name, 0);
+	if (ap == NULL)
+		return NULL;
+	return ap->val;
+}
+
+/*
+ * TODO - sort output
+ */
+int
+aliascmd(int argc, char **argv)
+{
+	char *n, *v;
+	int ret = 0;
+	struct alias *ap;
+
+	if (argc == 1) {
+		int i;
+
+		for (i = 0; i < ATABSIZE; i++)
+			for (ap = atab[i]; ap; ap = ap->next) {
+				if (*ap->name != '\0') {
+					out1fmt("alias %s=", ap->name);
+					print_quoted(ap->val);
+					out1c('\n');
+				}
+			}
+		return (0);
+	}
+	while ((n = *++argv) != NULL) {
+		if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
+			if ((ap = lookupalias(n, 0)) == NULL) {
+				outfmt(out2, "alias: %s not found\n", n);
+				ret = 1;
+			} else {
+				out1fmt("alias %s=", n);
+				print_quoted(ap->val);
+				out1c('\n');
+			}
+		} else {
+			*v++ = '\0';
+			setalias(n, v);
+		}
+	}
+
+	return (ret);
+}
+
+int
+unaliascmd(int argc, char **argv)
+{
+	int i;
+
+	while ((i = nextopt("a")) != '\0') {
+		if (i == 'a') {
+			rmaliases();
+			return (0);
+		}
+	}
+	for (i = 0; *argptr; argptr++)
+		i = unalias(*argptr);
+
+	return (i);
+}
+
+STATIC struct alias **
+hashalias(char *p)
+{
+	unsigned int hashval;
+
+	hashval = *p << 4;
+	while (*p)
+		hashval+= *p++;
+	return &atab[hashval % ATABSIZE];
+}
diff --git a/sh/alias.h b/sh/alias.h
new file mode 100644
index 0000000..7ce25f4
--- /dev/null
+++ b/sh/alias.h
@@ -0,0 +1,50 @@
+/*	$NetBSD: alias.h,v 1.6 2003/08/07 09:05:29 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)alias.h	8.2 (Berkeley) 5/4/95
+ */
+
+#define ALIASINUSE	1
+
+struct alias {
+	struct alias *next;
+	char *name;
+	char *val;
+	int flag;
+};
+
+struct alias *lookupalias(char *, int);
+char *get_alias_text(char *);
+int aliascmd(int, char **);
+int unaliascmd(int, char **);
+void rmaliases(void);
diff --git a/sh/arith.c b/sh/arith.c
new file mode 100644
index 0000000..f8f92a9
--- /dev/null
+++ b/sh/arith.c
@@ -0,0 +1,1587 @@
+/* A Bison parser, made by GNU Bison 1.875d.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     ARITH_NUM = 258,
+     ARITH_LPAREN = 259,
+     ARITH_RPAREN = 260,
+     ARITH_OR = 261,
+     ARITH_AND = 262,
+     ARITH_BOR = 263,
+     ARITH_BXOR = 264,
+     ARITH_BAND = 265,
+     ARITH_NE = 266,
+     ARITH_EQ = 267,
+     ARITH_LE = 268,
+     ARITH_GE = 269,
+     ARITH_GT = 270,
+     ARITH_LT = 271,
+     ARITH_RSHIFT = 272,
+     ARITH_LSHIFT = 273,
+     ARITH_SUB = 274,
+     ARITH_ADD = 275,
+     ARITH_REM = 276,
+     ARITH_DIV = 277,
+     ARITH_MUL = 278,
+     ARITH_BNOT = 279,
+     ARITH_NOT = 280,
+     ARITH_UNARYPLUS = 281,
+     ARITH_UNARYMINUS = 282
+   };
+#endif
+#define ARITH_NUM 258
+#define ARITH_LPAREN 259
+#define ARITH_RPAREN 260
+#define ARITH_OR 261
+#define ARITH_AND 262
+#define ARITH_BOR 263
+#define ARITH_BXOR 264
+#define ARITH_BAND 265
+#define ARITH_NE 266
+#define ARITH_EQ 267
+#define ARITH_LE 268
+#define ARITH_GE 269
+#define ARITH_GT 270
+#define ARITH_LT 271
+#define ARITH_RSHIFT 272
+#define ARITH_LSHIFT 273
+#define ARITH_SUB 274
+#define ARITH_ADD 275
+#define ARITH_REM 276
+#define ARITH_DIV 277
+#define ARITH_MUL 278
+#define ARITH_BNOT 279
+#define ARITH_NOT 280
+#define ARITH_UNARYPLUS 281
+#define ARITH_UNARYMINUS 282
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 1 "arith.y"
+
+/*	$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)arith.y	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "expand.h"
+#include "shell.h"
+#include "error.h"
+#include "output.h"
+#include "memalloc.h"
+
+const char *arith_buf, *arith_startbuf;
+
+void yyerror(const char *);
+#ifdef TESTARITH
+int main(int , char *[]);
+int error(char *);
+#endif
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+typedef int YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 214 of yacc.c.  */
+#line 202 "arith.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+#  define YYFREE free
+# endif
+# ifndef YYMALLOC
+#  define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   define YYSTACK_ALLOC alloca
+#  endif
+# else
+#  if defined (alloca) || defined (_ALLOCA_H)
+#   define YYSTACK_ALLOC alloca
+#  else
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+	 || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short int yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short int) + sizeof (YYSTYPE))			\
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  register YYSIZE_T yyi;		\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  14
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   170
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  28
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  3
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  26
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  52
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   282
+
+#define YYTRANSLATE(YYX) 						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned char yyprhs[] =
+{
+       0,     0,     3,     5,     9,    13,    17,    21,    25,    29,
+      33,    37,    41,    45,    49,    53,    57,    61,    65,    69,
+      73,    77,    81,    84,    87,    90,    93
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      29,     0,    -1,    30,    -1,     4,    30,     5,    -1,    30,
+       6,    30,    -1,    30,     7,    30,    -1,    30,     8,    30,
+      -1,    30,     9,    30,    -1,    30,    10,    30,    -1,    30,
+      12,    30,    -1,    30,    15,    30,    -1,    30,    14,    30,
+      -1,    30,    16,    30,    -1,    30,    13,    30,    -1,    30,
+      11,    30,    -1,    30,    18,    30,    -1,    30,    17,    30,
+      -1,    30,    20,    30,    -1,    30,    19,    30,    -1,    30,
+      23,    30,    -1,    30,    22,    30,    -1,    30,    21,    30,
+      -1,    25,    30,    -1,    24,    30,    -1,    19,    30,    -1,
+      20,    30,    -1,     3,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned char yyrline[] =
+{
+       0,    76,    76,    82,    83,    84,    85,    86,    87,    88,
+      89,    90,    91,    92,    93,    94,    95,    96,    97,    98,
+      99,   104,   109,   110,   111,   112,   113
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "ARITH_NUM", "ARITH_LPAREN",
+  "ARITH_RPAREN", "ARITH_OR", "ARITH_AND", "ARITH_BOR", "ARITH_BXOR",
+  "ARITH_BAND", "ARITH_NE", "ARITH_EQ", "ARITH_LE", "ARITH_GE", "ARITH_GT",
+  "ARITH_LT", "ARITH_RSHIFT", "ARITH_LSHIFT", "ARITH_SUB", "ARITH_ADD",
+  "ARITH_REM", "ARITH_DIV", "ARITH_MUL", "ARITH_BNOT", "ARITH_NOT",
+  "ARITH_UNARYPLUS", "ARITH_UNARYMINUS", "$accept", "exp", "expr", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short int yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    28,    29,    30,    30,    30,    30,    30,    30,    30,
+      30,    30,    30,    30,    30,    30,    30,    30,    30,    30,
+      30,    30,    30,    30,    30,    30,    30
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     1,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     2,     2,     2,     2,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       0,    26,     0,     0,     0,     0,     0,     0,     2,     0,
+      24,    25,    23,    22,     1,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     3,     4,     5,     6,     7,     8,    14,
+       9,    13,    11,    10,    12,    16,    15,    18,    17,    21,
+      20,    19
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+      -1,     7,     8
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -13
+static const short int yypact[] =
+{
+      28,   -13,    28,    28,    28,    28,    28,    12,    67,    49,
+     -13,   -13,   -13,   -13,   -13,    28,    28,    28,    28,    28,
+      28,    28,    28,    28,    28,    28,    28,    28,    28,    28,
+      28,    28,    28,   -13,    84,   100,   115,    23,   128,   139,
+     139,   -12,   -12,   -12,   -12,   144,   144,   147,   147,   -13,
+     -13,   -13
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yysigned_char yypgoto[] =
+{
+     -13,   -13,    -2
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+       9,    10,    11,    12,    13,    26,    27,    28,    29,    30,
+      31,    32,    14,    34,    35,    36,    37,    38,    39,    40,
+      41,    42,    43,    44,    45,    46,    47,    48,    49,    50,
+      51,     1,     2,    19,    20,    21,    22,    23,    24,    25,
+      26,    27,    28,    29,    30,    31,    32,     3,     4,     0,
+       0,     0,     5,     6,    33,    15,    16,    17,    18,    19,
+      20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+      30,    31,    32,    15,    16,    17,    18,    19,    20,    21,
+      22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
+      32,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    17,    18,
+      19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
+      29,    30,    31,    32,    18,    19,    20,    21,    22,    23,
+      24,    25,    26,    27,    28,    29,    30,    31,    32,    20,
+      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
+      31,    32,    22,    23,    24,    25,    26,    27,    28,    29,
+      30,    31,    32,    28,    29,    30,    31,    32,    30,    31,
+      32
+};
+
+static const yysigned_char yycheck[] =
+{
+       2,     3,     4,     5,     6,    17,    18,    19,    20,    21,
+      22,    23,     0,    15,    16,    17,    18,    19,    20,    21,
+      22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
+      32,     3,     4,    10,    11,    12,    13,    14,    15,    16,
+      17,    18,    19,    20,    21,    22,    23,    19,    20,    -1,
+      -1,    -1,    24,    25,     5,     6,     7,     8,     9,    10,
+      11,    12,    13,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    23,     6,     7,     8,     9,    10,    11,    12,
+      13,    14,    15,    16,    17,    18,    19,    20,    21,    22,
+      23,     7,     8,     9,    10,    11,    12,    13,    14,    15,
+      16,    17,    18,    19,    20,    21,    22,    23,     8,     9,
+      10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+      20,    21,    22,    23,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    11,
+      12,    13,    14,    15,    16,    17,    18,    19,    20,    21,
+      22,    23,    13,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    23,    19,    20,    21,    22,    23,    21,    22,
+      23
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,     3,     4,    19,    20,    24,    25,    29,    30,    30,
+      30,    30,    30,    30,     0,     6,     7,     8,     9,    10,
+      11,    12,    13,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    23,     5,    30,    30,    30,    30,    30,    30,
+      30,    30,    30,    30,    30,    30,    30,    30,    30,    30,
+      30,    30
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { 								\
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;							\
+    }								\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)		\
+   ((Current).first_line   = (Rhs)[1].first_line,	\
+    (Current).first_column = (Rhs)[1].first_column,	\
+    (Current).last_line    = (Rhs)[N].last_line,	\
+    (Current).last_column  = (Rhs)[N].last_column)
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (0)
+
+# define YYDSYMPRINT(Args)			\
+do {						\
+  if (yydebug)					\
+    yysymprint Args;				\
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)		\
+do {								\
+  if (yydebug)							\
+    {								\
+      YYFPRINTF (stderr, "%s ", Title);				\
+      yysymprint (stderr, 					\
+                  Token, Value);	\
+      YYFPRINTF (stderr, "\n");					\
+    }								\
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short int *bottom;
+    short int *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (Rule);		\
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    {
+      YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short int yyssa[YYINITDEPTH];
+  short int *yyss = yyssa;
+  register short int *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack. Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	short int *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	short int *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyoverflowlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 76 "arith.y"
+    {
+			return (yyvsp[0]);
+		;}
+    break;
+
+  case 3:
+#line 82 "arith.y"
+    { yyval = yyvsp[-1]; ;}
+    break;
+
+  case 4:
+#line 83 "arith.y"
+    { yyval = yyvsp[-2] ? yyvsp[-2] : yyvsp[0] ? yyvsp[0] : 0; ;}
+    break;
+
+  case 5:
+#line 84 "arith.y"
+    { yyval = yyvsp[-2] ? ( yyvsp[0] ? yyvsp[0] : 0 ) : 0; ;}
+    break;
+
+  case 6:
+#line 85 "arith.y"
+    { yyval = yyvsp[-2] | yyvsp[0]; ;}
+    break;
+
+  case 7:
+#line 86 "arith.y"
+    { yyval = yyvsp[-2] ^ yyvsp[0]; ;}
+    break;
+
+  case 8:
+#line 87 "arith.y"
+    { yyval = yyvsp[-2] & yyvsp[0]; ;}
+    break;
+
+  case 9:
+#line 88 "arith.y"
+    { yyval = yyvsp[-2] == yyvsp[0]; ;}
+    break;
+
+  case 10:
+#line 89 "arith.y"
+    { yyval = yyvsp[-2] > yyvsp[0]; ;}
+    break;
+
+  case 11:
+#line 90 "arith.y"
+    { yyval = yyvsp[-2] >= yyvsp[0]; ;}
+    break;
+
+  case 12:
+#line 91 "arith.y"
+    { yyval = yyvsp[-2] < yyvsp[0]; ;}
+    break;
+
+  case 13:
+#line 92 "arith.y"
+    { yyval = yyvsp[-2] <= yyvsp[0]; ;}
+    break;
+
+  case 14:
+#line 93 "arith.y"
+    { yyval = yyvsp[-2] != yyvsp[0]; ;}
+    break;
+
+  case 15:
+#line 94 "arith.y"
+    { yyval = yyvsp[-2] << yyvsp[0]; ;}
+    break;
+
+  case 16:
+#line 95 "arith.y"
+    { yyval = yyvsp[-2] >> yyvsp[0]; ;}
+    break;
+
+  case 17:
+#line 96 "arith.y"
+    { yyval = yyvsp[-2] + yyvsp[0]; ;}
+    break;
+
+  case 18:
+#line 97 "arith.y"
+    { yyval = yyvsp[-2] - yyvsp[0]; ;}
+    break;
+
+  case 19:
+#line 98 "arith.y"
+    { yyval = yyvsp[-2] * yyvsp[0]; ;}
+    break;
+
+  case 20:
+#line 99 "arith.y"
+    {
+			if (yyvsp[0] == 0)
+				yyerror("division by zero");
+			yyval = yyvsp[-2] / yyvsp[0];
+			;}
+    break;
+
+  case 21:
+#line 104 "arith.y"
+    {
+			if (yyvsp[0] == 0)
+				yyerror("division by zero");
+			yyval = yyvsp[-2] % yyvsp[0];
+			;}
+    break;
+
+  case 22:
+#line 109 "arith.y"
+    { yyval = !(yyvsp[0]); ;}
+    break;
+
+  case 23:
+#line 110 "arith.y"
+    { yyval = ~(yyvsp[0]); ;}
+    break;
+
+  case 24:
+#line 111 "arith.y"
+    { yyval = -(yyvsp[0]); ;}
+    break;
+
+  case 25:
+#line 112 "arith.y"
+    { yyval = yyvsp[0]; ;}
+    break;
+
+
+    }
+
+/* Line 1010 of yacc.c.  */
+#line 1276 "arith.c"
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+	{
+	  YYSIZE_T yysize = 0;
+	  int yytype = YYTRANSLATE (yychar);
+	  const char* yyprefix;
+	  char *yymsg;
+	  int yyx;
+
+	  /* Start YYX at -YYN if negative to avoid negative indexes in
+	     YYCHECK.  */
+	  int yyxbegin = yyn < 0 ? -yyn : 0;
+
+	  /* Stay within bounds of both yycheck and yytname.  */
+	  int yychecklim = YYLAST - yyn;
+	  int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+	  int yycount = 0;
+
+	  yyprefix = ", expecting ";
+	  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	      {
+		yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+		yycount += 1;
+		if (yycount == 5)
+		  {
+		    yysize = 0;
+		    break;
+		  }
+	      }
+	  yysize += (sizeof ("syntax error, unexpected ")
+		     + yystrlen (yytname[yytype]));
+	  yymsg = (char *) YYSTACK_ALLOC (yysize);
+	  if (yymsg != 0)
+	    {
+	      char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+	      yyp = yystpcpy (yyp, yytname[yytype]);
+
+	      if (yycount < 5)
+		{
+		  yyprefix = ", expecting ";
+		  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+		    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+		      {
+			yyp = yystpcpy (yyp, yyprefix);
+			yyp = yystpcpy (yyp, yytname[yyx]);
+			yyprefix = " or ";
+		      }
+		}
+	      yyerror (yymsg);
+	      YYSTACK_FREE (yymsg);
+	    }
+	  else
+	    yyerror ("syntax error; also virtual memory exhausted");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* If at end of input, pop the error token,
+	     then the rest of the stack, then return failure.  */
+	  if (yychar == YYEOF)
+	     for (;;)
+	       {
+		 YYPOPSTACK;
+		 if (yyssp == yyss)
+		   YYABORT;
+		 YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+		 yydestruct (yystos[*yyssp], yyvsp);
+	       }
+        }
+      else
+	{
+	  YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+	  yydestruct (yytoken, &yylval);
+	  yychar = YYEMPTY;
+
+	}
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+  /* Pacify GCC when the user code never invokes YYERROR and the label
+     yyerrorlab therefore never appears in user code.  */
+  if (0)
+     goto yyerrorlab;
+#endif
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+#line 115 "arith.y"
+
+int
+arith(s)
+	const char *s;
+{
+	long result;
+
+	arith_buf = arith_startbuf = s;
+
+	INTOFF;
+	result = yyparse();
+	arith_lex_reset();	/* reprime lex */
+	INTON;
+
+	return (result);
+}
+
+
+/*
+ *  The exp(1) builtin.
+ */
+int
+expcmd(argc, argv)
+	int argc;
+	char **argv;
+{
+	const char *p;
+	char *concat;
+	char **ap;
+	long i;
+
+	if (argc > 1) {
+		p = argv[1];
+		if (argc > 2) {
+			/*
+			 * concatenate arguments
+			 */
+			STARTSTACKSTR(concat);
+			ap = argv + 2;
+			for (;;) {
+				while (*p)
+					STPUTC(*p++, concat);
+				if ((p = *ap++) == NULL)
+					break;
+				STPUTC(' ', concat);
+			}
+			STPUTC('\0', concat);
+			p = grabstackstr(concat);
+		}
+	} else
+		p = "";
+
+	i = arith(p);
+
+	out1fmt("%ld\n", i);
+	return (! i);
+}
+
+/*************************/
+#ifdef TEST_ARITH
+#include <stdio.h>
+main(argc, argv)
+	char *argv[];
+{
+	printf("%d\n", exp(argv[1]));
+}
+error(s)
+	char *s;
+{
+	fprintf(stderr, "exp: %s\n", s);
+	exit(1);
+}
+#endif
+
+void
+yyerror(s)
+	const char *s;
+{
+
+//	yyerrok;
+	yyclearin;
+	arith_lex_reset();	/* reprime lex */
+	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+	/* NOTREACHED */
+}
+
+
diff --git a/sh/arith.h b/sh/arith.h
new file mode 100644
index 0000000..f70c093
--- /dev/null
+++ b/sh/arith.h
@@ -0,0 +1,25 @@
+#define ARITH_NUM 258
+#define ARITH_LPAREN 259
+#define ARITH_RPAREN 260
+#define ARITH_OR 261
+#define ARITH_AND 262
+#define ARITH_BOR 263
+#define ARITH_BXOR 264
+#define ARITH_BAND 265
+#define ARITH_NE 266
+#define ARITH_EQ 267
+#define ARITH_LE 268
+#define ARITH_GE 269
+#define ARITH_GT 270
+#define ARITH_LT 271
+#define ARITH_RSHIFT 272
+#define ARITH_LSHIFT 273
+#define ARITH_SUB 274
+#define ARITH_ADD 275
+#define ARITH_REM 276
+#define ARITH_DIV 277
+#define ARITH_MUL 278
+#define ARITH_BNOT 279
+#define ARITH_NOT 280
+#define ARITH_UNARYPLUS 281
+#define ARITH_UNARYMINUS 282
diff --git a/sh/arith.y b/sh/arith.y
new file mode 100644
index 0000000..d51ed38
--- /dev/null
+++ b/sh/arith.y
@@ -0,0 +1,199 @@
+%{
+/*	$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)arith.y	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "expand.h"
+#include "shell.h"
+#include "error.h"
+#include "output.h"
+#include "memalloc.h"
+
+const char *arith_buf, *arith_startbuf;
+
+void yyerror(const char *);
+#ifdef TESTARITH
+int main(int , char *[]);
+int error(char *);
+#endif
+
+%}
+%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN
+
+%left ARITH_OR
+%left ARITH_AND
+%left ARITH_BOR
+%left ARITH_BXOR
+%left ARITH_BAND
+%left ARITH_EQ ARITH_NE
+%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
+%left ARITH_LSHIFT ARITH_RSHIFT
+%left ARITH_ADD ARITH_SUB
+%left ARITH_MUL ARITH_DIV ARITH_REM
+%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
+%%
+
+exp:	expr {
+			return ($1);
+		}
+	;
+
+
+expr:	ARITH_LPAREN expr ARITH_RPAREN { $$ = $2; }
+	| expr ARITH_OR expr	{ $$ = $1 ? $1 : $3 ? $3 : 0; }
+	| expr ARITH_AND expr	{ $$ = $1 ? ( $3 ? $3 : 0 ) : 0; }
+	| expr ARITH_BOR expr	{ $$ = $1 | $3; }
+	| expr ARITH_BXOR expr	{ $$ = $1 ^ $3; }
+	| expr ARITH_BAND expr	{ $$ = $1 & $3; }
+	| expr ARITH_EQ expr	{ $$ = $1 == $3; }
+	| expr ARITH_GT expr	{ $$ = $1 > $3; }
+	| expr ARITH_GE expr	{ $$ = $1 >= $3; }
+	| expr ARITH_LT expr	{ $$ = $1 < $3; }
+	| expr ARITH_LE expr	{ $$ = $1 <= $3; }
+	| expr ARITH_NE expr	{ $$ = $1 != $3; }
+	| expr ARITH_LSHIFT expr { $$ = $1 << $3; }
+	| expr ARITH_RSHIFT expr { $$ = $1 >> $3; }
+	| expr ARITH_ADD expr	{ $$ = $1 + $3; }
+	| expr ARITH_SUB expr	{ $$ = $1 - $3; }
+	| expr ARITH_MUL expr	{ $$ = $1 * $3; }
+	| expr ARITH_DIV expr	{
+			if ($3 == 0)
+				yyerror("division by zero");
+			$$ = $1 / $3;
+			}
+	| expr ARITH_REM expr   {
+			if ($3 == 0)
+				yyerror("division by zero");
+			$$ = $1 % $3;
+			}
+	| ARITH_NOT expr	{ $$ = !($2); }
+	| ARITH_BNOT expr	{ $$ = ~($2); }
+	| ARITH_SUB expr %prec ARITH_UNARYMINUS { $$ = -($2); }
+	| ARITH_ADD expr %prec ARITH_UNARYPLUS { $$ = $2; }
+	| ARITH_NUM
+	;
+%%
+int
+arith(s)
+	const char *s;
+{
+	long result;
+
+	arith_buf = arith_startbuf = s;
+
+	INTOFF;
+	result = yyparse();
+	arith_lex_reset();	/* reprime lex */
+	INTON;
+
+	return (result);
+}
+
+
+/*
+ *  The exp(1) builtin.
+ */
+int
+expcmd(argc, argv)
+	int argc;
+	char **argv;
+{
+	const char *p;
+	char *concat;
+	char **ap;
+	long i;
+
+	if (argc > 1) {
+		p = argv[1];
+		if (argc > 2) {
+			/*
+			 * concatenate arguments
+			 */
+			STARTSTACKSTR(concat);
+			ap = argv + 2;
+			for (;;) {
+				while (*p)
+					STPUTC(*p++, concat);
+				if ((p = *ap++) == NULL)
+					break;
+				STPUTC(' ', concat);
+			}
+			STPUTC('\0', concat);
+			p = grabstackstr(concat);
+		}
+	} else
+		p = "";
+
+	i = arith(p);
+
+	out1fmt("%ld\n", i);
+	return (! i);
+}
+
+/*************************/
+#ifdef TEST_ARITH
+#include <stdio.h>
+main(argc, argv)
+	char *argv[];
+{
+	printf("%d\n", exp(argv[1]));
+}
+error(s)
+	char *s;
+{
+	fprintf(stderr, "exp: %s\n", s);
+	exit(1);
+}
+#endif
+
+void
+yyerror(s)
+	const char *s;
+{
+
+//	yyerrok;
+	yyclearin;
+	arith_lex_reset();	/* reprime lex */
+	error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+	/* NOTREACHED */
+}
diff --git a/sh/arith_lex.c b/sh/arith_lex.c
new file mode 100644
index 0000000..9a132dd
--- /dev/null
+++ b/sh/arith_lex.c
@@ -0,0 +1,1890 @@
+#line 2 "arith_lex.c"
+
+#line 4 "arith_lex.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	yyleng = (size_t) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 29
+#define YY_END_OF_BUFFER 30
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[39] =
+    {   0,
+        0,    0,   30,   28,    1,    1,   27,   23,   12,    6,
+        7,   21,   24,   25,   22,    3,    4,   17,   28,   15,
+        5,   11,   10,   26,   14,    9,    3,    0,    4,   19,
+       18,   13,   16,   20,    5,    8,    2,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    1,    1,    1,    5,    6,    1,    7,
+        8,    9,   10,    1,   11,    1,   12,   13,   14,   14,
+       14,   14,   14,   14,   14,   15,   15,    1,    1,   16,
+       17,   18,    1,    1,   19,   19,   19,   19,   19,   19,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   20,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   20,
+        1,    1,    1,   21,   20,    1,   19,   19,   19,   19,
+
+       19,   19,   20,   20,   20,   20,   20,   20,   20,   20,
+       20,   20,   20,   20,   20,   20,   20,   20,   20,   22,
+       20,   20,    1,   23,    1,   24,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[25] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    2,    2,    2,    1,    1,    1,    2,    3,
+        1,    3,    1,    1
+    } ;
+
+static yyconst flex_int16_t yy_base[41] =
+    {   0,
+        0,    0,   47,   48,   48,   48,   29,   48,   39,   48,
+       48,   48,   48,   48,   48,   12,   14,   14,   27,   15,
+        0,   48,   20,   48,   48,   48,   22,    0,   24,   48,
+       48,   48,   48,   48,    0,   48,    0,   48,   38,   40
+    } ;
+
+static yyconst flex_int16_t yy_def[41] =
+    {   0,
+       38,    1,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       39,   38,   38,   38,   38,   38,   38,   40,   38,   38,
+       38,   38,   38,   38,   39,   38,   40,    0,   38,   38
+    } ;
+
+static yyconst flex_int16_t yy_nxt[73] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       14,   15,   16,   17,   17,   18,   19,   20,   21,   21,
+       22,   21,   23,   24,   27,   27,   29,   29,   29,   30,
+       31,   33,   34,   28,   27,   27,   29,   29,   29,   35,
+       35,   37,   36,   32,   26,   25,   38,    3,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38
+    } ;
+
+static yyconst flex_int16_t yy_chk[73] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,   16,   16,   17,   17,   17,   18,
+       18,   20,   20,   16,   27,   27,   29,   29,   29,   39,
+       39,   40,   23,   19,    9,    7,    3,   38,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,   38
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "arith_lex.l"
+#line 2 "arith_lex.l"
+/*	$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)arith_lex.l	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $");
+#endif
+#endif /* not lint */
+
+#include <unistd.h>
+#include "arith.h"
+#include "error.h"
+#include "expand.h"
+#include "var.h"
+
+extern int yylval;
+extern char *arith_buf, *arith_startbuf;
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) \
+	result = (*buf = *arith_buf++) ? 1 : YY_NULL;
+#define YY_NO_UNPUT
+#line 526 "arith_lex.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+    
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+		{ \
+		int c = '*'; \
+		size_t n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else \
+		{ \
+		errno=0; \
+		while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+			{ \
+			if( errno != EINTR) \
+				{ \
+				YY_FATAL_ERROR( "input in flex scanner failed" ); \
+				break; \
+				} \
+			errno=0; \
+			clearerr(yyin); \
+			} \
+		}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+    
+#line 60 "arith_lex.l"
+
+#line 679 "arith_lex.c"
+
+	if ( (yy_init) )
+		{
+		(yy_init) = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer(yyin,YY_BUF_SIZE );
+		}
+
+		yy_load_buffer_state( );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of yytext. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				(yy_last_accepting_state) = yy_current_state;
+				(yy_last_accepting_cpos) = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 39 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 48 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = (yy_hold_char);
+			yy_cp = (yy_last_accepting_cpos);
+			yy_current_state = (yy_last_accepting_state);
+			goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 61 "arith_lex.l"
+{ ; }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 62 "arith_lex.l"
+{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 63 "arith_lex.l"
+{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 64 "arith_lex.l"
+{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 65 "arith_lex.l"
+{ char *v = lookupvar(yytext);
+			if (v) {
+				yylval = strtol(v, &v, 0);
+				if (*v == 0)
+					return ARITH_NUM;
+			}
+			error("arith: syntax error: \"%s\"", arith_startbuf);
+		}
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 73 "arith_lex.l"
+{ return(ARITH_LPAREN); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 74 "arith_lex.l"
+{ return(ARITH_RPAREN); }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 75 "arith_lex.l"
+{ return(ARITH_OR); }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 76 "arith_lex.l"
+{ return(ARITH_AND); }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 77 "arith_lex.l"
+{ return(ARITH_BOR); }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 78 "arith_lex.l"
+{ return(ARITH_BXOR); }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 79 "arith_lex.l"
+{ return(ARITH_BAND); }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 80 "arith_lex.l"
+{ return(ARITH_EQ); }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 81 "arith_lex.l"
+{ return(ARITH_NE); }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 82 "arith_lex.l"
+{ return(ARITH_GT); }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 83 "arith_lex.l"
+{ return(ARITH_GE); }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 84 "arith_lex.l"
+{ return(ARITH_LT); }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 85 "arith_lex.l"
+{ return(ARITH_LE); }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 86 "arith_lex.l"
+{ return(ARITH_LSHIFT); }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 87 "arith_lex.l"
+{ return(ARITH_RSHIFT); }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 88 "arith_lex.l"
+{ return(ARITH_MUL); }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 89 "arith_lex.l"
+{ return(ARITH_DIV); }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 90 "arith_lex.l"
+{ return(ARITH_REM); }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 91 "arith_lex.l"
+{ return(ARITH_ADD); }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 92 "arith_lex.l"
+{ return(ARITH_SUB); }
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 93 "arith_lex.l"
+{ return(ARITH_BNOT); }
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 94 "arith_lex.l"
+{ return(ARITH_NOT); }
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 95 "arith_lex.l"
+{ error("arith: syntax error: \"%s\"", arith_startbuf); }
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 96 "arith_lex.l"
+ECHO;
+	YY_BREAK
+#line 915 "arith_lex.c"
+case YY_STATE_EOF(INITIAL):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( yywrap( ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = (yytext_ptr);
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart(yyin  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+    
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			(yy_last_accepting_state) = yy_current_state;
+			(yy_last_accepting_cpos) = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 39 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	register int yy_is_jam;
+    	register char *yy_cp = (yy_c_buf_p);
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		(yy_last_accepting_state) = yy_current_state;
+		(yy_last_accepting_cpos) = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 39 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 38);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+	register char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+	/* undo effects of setting up yytext */
+	*yy_cp = (yy_hold_char);
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = (yy_n_chars) + 2;
+		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		register char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+	(yytext_ptr) = yy_bp;
+	(yy_hold_char) = *yy_cp;
+	(yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (yy_c_buf_p) - (yytext_ptr);
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart(yyin );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap( ) )
+						return EOF;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve yytext */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+	}
+
+	yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+	yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state( );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer(b,file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree((void *) b->yy_ch_buf  );
+
+	yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+    
+	yy_flush_buffer(b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack();
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state( );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state( );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+	int num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+				
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer(b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yy_str )
+{
+    
+	return yy_scan_bytes(yy_str,strlen(yy_str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * bytes, int  len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) yyalloc(n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer(buf,n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = (yy_hold_char); \
+		(yy_c_buf_p) = yytext + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer(YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	yyfree((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+	register int i;
+    	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+	register int n;
+    	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+	return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+	free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+#line 96 "arith_lex.l"
+
+
+
+void
+arith_lex_reset() {
+#ifdef YY_NEW_FILE
+	YY_NEW_FILE;
+#endif
+}
+
diff --git a/sh/arith_lex.l b/sh/arith_lex.l
new file mode 100644
index 0000000..79116fc
--- /dev/null
+++ b/sh/arith_lex.l
@@ -0,0 +1,103 @@
+%{
+/*	$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)arith_lex.l	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: arith_lex.l,v 1.12.6.1 2005/04/07 11:38:58 tron Exp $");
+#endif
+#endif /* not lint */
+
+#include <unistd.h>
+#include "arith.h"
+#include "error.h"
+#include "expand.h"
+#include "var.h"
+
+extern int yylval;
+extern char *arith_buf, *arith_startbuf;
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) \
+	result = (*buf = *arith_buf++) ? 1 : YY_NULL;
+#define YY_NO_UNPUT
+%}
+%option noyywrap
+
+%%
+[ \t\n]	{ ; }
+0x[0-9a-fA-F]+	{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+0[0-7]*		{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+[1-9][0-9]*	{ yylval = strtol(yytext, 0, 0); return(ARITH_NUM); }
+[A-Za-z_][A-Za-z_0-9]*	{ char *v = lookupvar(yytext);
+			if (v) {
+				yylval = strtol(v, &v, 0);
+				if (*v == 0)
+					return ARITH_NUM;
+			}
+			error("arith: syntax error: \"%s\"", arith_startbuf);
+		}
+"("	{ return(ARITH_LPAREN); }
+")"	{ return(ARITH_RPAREN); }
+"||"	{ return(ARITH_OR); }
+"&&"	{ return(ARITH_AND); }
+"|"	{ return(ARITH_BOR); }
+"^"	{ return(ARITH_BXOR); }
+"&"	{ return(ARITH_BAND); }
+"=="	{ return(ARITH_EQ); }
+"!="	{ return(ARITH_NE); }
+">"	{ return(ARITH_GT); }
+">="	{ return(ARITH_GE); }
+"<"	{ return(ARITH_LT); }
+"<="	{ return(ARITH_LE); }
+"<<"	{ return(ARITH_LSHIFT); }
+">>"	{ return(ARITH_RSHIFT); }
+"*"	{ return(ARITH_MUL); }
+"/"	{ return(ARITH_DIV); }
+"%"	{ return(ARITH_REM); }
+"+"	{ return(ARITH_ADD); }
+"-"	{ return(ARITH_SUB); }
+"~"	{ return(ARITH_BNOT); }
+"!"	{ return(ARITH_NOT); }
+.	{ error("arith: syntax error: \"%s\"", arith_startbuf); }
+%%
+
+void
+arith_lex_reset() {
+#ifdef YY_NEW_FILE
+	YY_NEW_FILE;
+#endif
+}
diff --git a/sh/bltin/bltin.h b/sh/bltin/bltin.h
new file mode 100644
index 0000000..b8f9d75
--- /dev/null
+++ b/sh/bltin/bltin.h
@@ -0,0 +1,94 @@
+/*	$NetBSD: bltin.h,v 1.11 2003/08/07 09:05:40 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)bltin.h	8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * This file is included by programs which are optionally built into the
+ * shell.  If SHELL is defined, we try to map the standard UNIX library
+ * routines to ash routines using defines.
+ */
+
+#include "../shell.h"
+#include "../mystring.h"
+#ifdef SHELL
+#include "../output.h"
+#include "../error.h"
+#undef stdout
+#undef stderr
+#undef putc
+#undef putchar
+#undef fileno
+#define stdout out1
+#define stderr out2
+#define printf out1fmt
+#define putc(c, file)	outc(c, file)
+#define putchar(c)	out1c(c)
+#define FILE struct output
+#define fprintf outfmt
+#define fputs outstr
+#define fflush flushout
+#define fileno(f) ((f)->fd)
+#define INITARGS(argv)
+#define	err sh_err
+#define	verr sh_verr
+#define	errx sh_errx
+#define	verrx sh_verrx
+#define	warn sh_warn
+#define	vwarn sh_vwarn
+#define	warnx sh_warnx
+#define	vwarnx sh_vwarnx
+#define exit sh_exit
+#define setprogname(s)
+#define getprogname() commandname
+#define setlocate(l,s) 0
+
+#define getenv(p) bltinlookup((p),0)
+
+#else
+#undef NULL
+#include <stdio.h>
+#undef main
+#define INITARGS(argv)	if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
+#endif
+
+pointer stalloc(int);
+void error(const char *, ...);
+void sh_warnx(const char *, ...);
+void sh_exit(int) __attribute__((__noreturn__));
+
+int echocmd(int, char **);
+
+
+extern const char *commandname;
diff --git a/sh/bltin/echo.1 b/sh/bltin/echo.1
new file mode 100644
index 0000000..7e71fa3
--- /dev/null
+++ b/sh/bltin/echo.1
@@ -0,0 +1,109 @@
+.\"	$NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc Exp $
+.\"
+.\" Copyright (c) 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Kenneth Almquist.
+.\" Copyright 1989 by Kenneth Almquist
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"	@(#)echo.1	8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt ECHO 1
+.Os
+.Sh NAME
+.Nm echo
+.Nd produce message in a shell script
+.Sh SYNOPSIS
+.Nm
+.Op Fl n | Fl e
+.Ar args ...
+.Sh DESCRIPTION
+.Nm
+prints its arguments on the standard output, separated by spaces.
+Unless the
+.Fl n
+option is present, a newline is output following the arguments.
+The
+.Fl e
+option causes
+.Nm
+to treat the escape sequences specially, as described in the following
+paragraph.
+The
+.Fl e
+option is the default, and is provided solely for compatibility with
+other systems.
+Only one of the options
+.Fl n
+and
+.Fl e
+may be given.
+.Pp
+If any of the following sequences of characters is encountered during
+output, the sequence is not output.  Instead, the specified action is
+performed:
+.Bl -tag -width indent
+.It Li \eb
+A backspace character is output.
+.It Li \ec
+Subsequent output is suppressed.  This is normally used at the end of the
+last argument to suppress the trailing newline that
+.Nm
+would otherwise output.
+.It Li \ef
+Output a form feed.
+.It Li \en
+Output a newline character.
+.It Li \er
+Output a carriage return.
+.It Li \et
+Output a (horizontal) tab character.
+.It Li \ev
+Output a vertical tab.
+.It Li \e0 Ns Ar digits
+Output the character whose value is given by zero to three digits.
+If there are zero digits, a nul character is output.
+.It Li \e\e
+Output a backslash.
+.El
+.Sh HINTS
+Remember that backslash is special to the shell and needs to be escaped.
+To output a message to standard error, say
+.Pp
+.D1  echo message \*[Gt]\*[Am]2
+.Sh BUGS
+The octal character escape mechanism
+.Pq Li \e0 Ns Ar digits
+differs from the
+C language mechanism.
+.Pp
+There is no way to force
+.Nm
+to treat its arguments literally, rather than interpreting them as
+options and escape sequences.
diff --git a/sh/bltin/echo.c b/sh/bltin/echo.c
new file mode 100644
index 0000000..bed7535
--- /dev/null
+++ b/sh/bltin/echo.c
@@ -0,0 +1,116 @@
+/*	$NetBSD: echo.c,v 1.12 2005/02/06 04:43:43 perry Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)echo.c	8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Echo command.
+ *
+ * echo is steeped in tradition - several of them!
+ * netbsd has supported 'echo [-n | -e] args' in spite of -e not being
+ * documented anywhere.
+ * Posix requires that -n be supported, output from strings containing
+ * \ is implementation defined
+ * The Single Unix Spec requires that \ escapes be treated as if -e
+ * were set, but that -n not be treated as an option.
+ * (ksh supports 'echo [-eEn] args', but not -- so that it is actually
+ * impossible to actually output '-n')
+ *
+ * It is suggested that 'printf "%b" "string"' be used to get \ sequences
+ * expanded.  printf is now a builtin of netbsd's sh and csh.
+ */
+
+//#define main echocmd
+
+#include "bltin.h"
+
+int
+echocmd(int argc, char **argv)
+{
+	char **ap;
+	char *p;
+	char c;
+	int count;
+	int nflag = 0;
+	int eflag = 0;
+
+	ap = argv;
+	if (argc)
+		ap++;
+
+	if ((p = *ap) != NULL) {
+		if (equal(p, "-n")) {
+			nflag = 1;
+			ap++;
+		} else if (equal(p, "-e")) {
+			eflag = 1;
+			ap++;
+		}
+	}
+
+	while ((p = *ap++) != NULL) {
+		while ((c = *p++) != '\0') {
+			if (c == '\\' && eflag) {
+				switch (*p++) {
+				case 'a':  c = '\a';  break;	/* bell */
+				case 'b':  c = '\b';  break;
+				case 'c':  return 0;		/* exit */
+				case 'e':  c =  033;  break;	/* escape */
+				case 'f':  c = '\f';  break;
+				case 'n':  c = '\n';  break;
+				case 'r':  c = '\r';  break;
+				case 't':  c = '\t';  break;
+				case 'v':  c = '\v';  break;
+				case '\\':  break;		/* c = '\\' */
+				case '0':
+					c = 0;
+					count = 3;
+					while (--count >= 0 && (unsigned)(*p - '0') < 8)
+						c = (c << 3) + (*p++ - '0');
+					break;
+				default:
+					/* Output the '/' and char following */
+					p--;
+					break;
+				}
+			}
+			putchar(c);
+		}
+		if (*ap)
+			putchar(' ');
+	}
+	if (! nflag)
+		putchar('\n');
+	return 0;
+}
diff --git a/sh/builtins.c b/sh/builtins.c
new file mode 100644
index 0000000..344dbd6
--- /dev/null
+++ b/sh/builtins.c
@@ -0,0 +1,61 @@
+/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#include "shell.h"
+#include "builtins.h"
+
+const struct builtincmd builtincmd[] = {
+
+	{ "command",	bltincmd },
+	{ "bg",	bgcmd },
+	{ "cd",	cdcmd },
+	{ "chdir",	cdcmd },
+	{ "echo",	echocmd },
+	{ "exp",	expcmd },
+	{ "let",	expcmd },
+	{ "false",	falsecmd },
+#if WITH_HISTORY
+	{ "fc",	histcmd },
+	{ "inputrc",	inputrc },
+#endif
+	{ "fg",	fgcmd },
+	{ "getopts",	getoptscmd },
+	{ "hash",	hashcmd },
+	{ "jobid",	jobidcmd },
+	{ "jobs",	jobscmd },
+	{ "local",	localcmd },
+#ifndef SMALL
+#endif
+	{ "pwd",	pwdcmd },
+	{ "read",	readcmd },
+	{ "setvar",	setvarcmd },
+	{ "true",	truecmd },
+	{ "type",	typecmd },
+	{ "umask",	umaskcmd },
+	{ "unalias",	unaliascmd },
+	{ "wait",	waitcmd },
+	{ "alias",	aliascmd },
+	{ "ulimit",	ulimitcmd },
+	{ "wordexp",	wordexpcmd },
+	{ 0, 0 },
+};
+
+const struct builtincmd splbltincmd[] = {
+	{ "break",	breakcmd },
+	{ "continue",	breakcmd },
+	{ ".",	dotcmd },
+	{ "eval",	evalcmd },
+	{ "exec",	execcmd },
+	{ "exit",	exitcmd },
+	{ "export",	exportcmd },
+	{ "readonly",	exportcmd },
+	{ "return",	returncmd },
+	{ "set",	setcmd },
+	{ "shift",	shiftcmd },
+	{ "times",	timescmd },
+	{ "trap",	trapcmd },
+	{ ":",	truecmd },
+	{ "unset",	unsetcmd },
+	{ 0, 0 },
+};
diff --git a/sh/builtins.def b/sh/builtins.def
new file mode 100644
index 0000000..18e56c6
--- /dev/null
+++ b/sh/builtins.def
@@ -0,0 +1,94 @@
+#!/bin/sh -
+#	$NetBSD: builtins.def,v 1.21 2004/07/13 15:05:59 seb Exp $
+#
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)builtins.def	8.4 (Berkeley) 5/4/95
+
+#
+# This file lists all the builtin commands.  The first column is the name
+# of a C routine.
+# The -j flag specifies that this command is to be excluded from systems
+# without job control.
+# The -h flag specifies that this command is to be excluded from systems
+# based on the SMALL compile-time symbol.
+# The -s flag specifies that this is a posix 'special builtin' command.
+# The -u flag specifies that this is a posix 'standard utility'.
+# The rest of the line specifies the command name or names used to run
+# the command.
+
+bltincmd	-u command
+bgcmd -j	-u bg
+breakcmd	-s break -s continue
+cdcmd		-u cd chdir
+dotcmd		-s .
+echocmd		echo
+evalcmd		-s eval
+execcmd		-s exec
+exitcmd		-s exit
+expcmd		exp let
+exportcmd	-s export -s readonly
+falsecmd	-u false
+#if WITH_HISTORY
+histcmd -h	-u fc
+inputrc		inputrc
+#endif
+fgcmd -j	-u fg
+getoptscmd	-u getopts
+hashcmd		hash
+jobidcmd	jobid
+jobscmd		-u jobs
+localcmd	local
+#ifndef SMALL
+##printfcmd	printf
+#endif
+pwdcmd		-u pwd
+readcmd		-u read
+returncmd	-s return
+setcmd		-s set
+setvarcmd	setvar
+shiftcmd	-s shift
+timescmd	-s times
+trapcmd		-s trap
+truecmd		-s : -u true
+typecmd		type
+umaskcmd	-u umask
+unaliascmd	-u unalias
+unsetcmd	-s unset
+waitcmd		-u wait
+aliascmd	-u alias
+ulimitcmd	ulimit
+##testcmd		test [
+##killcmd		-u kill		# mandated by posix for 'kill %job'
+wordexpcmd	wordexp
+#newgrp		-u newgrp	# optional command in posix
+
+#exprcmd	expr
diff --git a/sh/builtins.h b/sh/builtins.h
new file mode 100644
index 0000000..1f9e45a
--- /dev/null
+++ b/sh/builtins.h
@@ -0,0 +1,56 @@
+/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#include <sys/cdefs.h>
+
+struct builtincmd {
+      const char *name;
+      int (*builtin)(int, char **);
+};
+
+extern const struct builtincmd builtincmd[];
+extern const struct builtincmd splbltincmd[];
+
+
+int bltincmd(int, char **);
+int bgcmd(int, char **);
+int breakcmd(int, char **);
+int cdcmd(int, char **);
+int dotcmd(int, char **);
+int echocmd(int, char **);
+int evalcmd(int, char **);
+int execcmd(int, char **);
+int exitcmd(int, char **);
+int expcmd(int, char **);
+int exportcmd(int, char **);
+int falsecmd(int, char **);
+#if WITH_HISTORY
+int histcmd(int, char **);
+int inputrc(int, char **);
+#endif
+int fgcmd(int, char **);
+int getoptscmd(int, char **);
+int hashcmd(int, char **);
+int jobidcmd(int, char **);
+int jobscmd(int, char **);
+int localcmd(int, char **);
+#ifndef SMALL
+#endif
+int pwdcmd(int, char **);
+int readcmd(int, char **);
+int returncmd(int, char **);
+int setcmd(int, char **);
+int setvarcmd(int, char **);
+int shiftcmd(int, char **);
+int timescmd(int, char **);
+int trapcmd(int, char **);
+int truecmd(int, char **);
+int typecmd(int, char **);
+int umaskcmd(int, char **);
+int unaliascmd(int, char **);
+int unsetcmd(int, char **);
+int waitcmd(int, char **);
+int aliascmd(int, char **);
+int ulimitcmd(int, char **);
+int wordexpcmd(int, char **);
diff --git a/sh/cd.c b/sh/cd.c
new file mode 100644
index 0000000..4ab599b
--- /dev/null
+++ b/sh/cd.c
@@ -0,0 +1,446 @@
+/*	$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * The cd and pwd commands.
+ */
+
+#include "shell.h"
+#include "var.h"
+#include "nodes.h"	/* for jobs.h */
+#include "jobs.h"
+#include "options.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "exec.h"
+#include "redir.h"
+#include "mystring.h"
+#include "show.h"
+#include "cd.h"
+
+STATIC int docd(char *, int);
+STATIC char *getcomponent(void);
+STATIC void updatepwd(char *);
+STATIC void find_curdir(int noerror);
+
+char *curdir = NULL;		/* current working directory */
+char *prevdir;			/* previous working directory */
+STATIC char *cdcomppath;
+
+int
+cdcmd(int argc, char **argv)
+{
+	const char *dest;
+	const char *path;
+	char *p, *d;
+	struct stat statb;
+	int print = cdprint;	/* set -cdprint to enable */
+
+	nextopt(nullstr);
+
+	/*
+	 * Try (quite hard) to have 'curdir' defined, nothing has set
+	 * it on entry to the shell, but we want 'cd fred; cd -' to work.
+	 */
+	getpwd(1);
+	dest = *argptr;
+	if (dest == NULL) {
+		dest = bltinlookup("HOME", 1);
+		if (dest == NULL)
+			error("HOME not set");
+	} else {
+		if (argptr[1]) {
+			/* Do 'ksh' style substitution */
+			if (!curdir)
+				error("PWD not set");
+			p = strstr(curdir, dest);
+			if (!p)
+				error("bad substitution");
+			d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1);
+			memcpy(d, curdir, p - curdir);
+			strcpy(d + (p - curdir), argptr[1]);
+			strcat(d, p + strlen(dest));
+			dest = d;
+			print = 1;
+		}
+	}
+
+	if (dest[0] == '-' && dest[1] == '\0') {
+		dest = prevdir ? prevdir : curdir;
+		print = 1;
+	}
+	if (*dest == '\0')
+	        dest = ".";
+	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
+		path = nullstr;
+	while ((p = padvance(&path, dest)) != NULL) {
+		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+			if (!print) {
+				/*
+				 * XXX - rethink
+				 */
+				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
+					p += 2;
+				print = strcmp(p, dest);
+			}
+			if (docd(p, print) >= 0)
+				return 0;
+
+		}
+	}
+	error("can't cd to %s", dest);
+	/* NOTREACHED */
+}
+
+
+/*
+ * Actually do the chdir.  In an interactive shell, print the
+ * directory name if "print" is nonzero.
+ */
+
+STATIC int
+docd(char *dest, int print)
+{
+	char *p;
+	char *q;
+	char *component;
+	struct stat statb;
+	int first;
+	int badstat;
+
+	TRACE(("docd(\"%s\", %d) called\n", dest, print));
+
+	/*
+	 *  Check each component of the path. If we find a symlink or
+	 *  something we can't stat, clear curdir to force a getcwd()
+	 *  next time we get the value of the current directory.
+	 */
+	badstat = 0;
+	cdcomppath = stalloc(strlen(dest) + 1);
+	scopy(dest, cdcomppath);
+	STARTSTACKSTR(p);
+	if (*dest == '/') {
+		STPUTC('/', p);
+		cdcomppath++;
+	}
+	first = 1;
+	while ((q = getcomponent()) != NULL) {
+		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
+			continue;
+		if (! first)
+			STPUTC('/', p);
+		first = 0;
+		component = q;
+		while (*q)
+			STPUTC(*q++, p);
+		if (equal(component, ".."))
+			continue;
+		STACKSTRNUL(p);
+		if ((lstat(stackblock(), &statb) < 0)
+		    || (S_ISLNK(statb.st_mode)))  {
+			/* print = 1; */
+			badstat = 1;
+			break;
+		}
+	}
+
+	INTOFF;
+	if (chdir(dest) < 0) {
+		INTON;
+		return -1;
+	}
+	updatepwd(badstat ? NULL : dest);
+	INTON;
+	if (print && iflag && curdir)
+		out1fmt("%s\n", curdir);
+	return 0;
+}
+
+
+/*
+ * Get the next component of the path name pointed to by cdcomppath.
+ * This routine overwrites the string pointed to by cdcomppath.
+ */
+
+STATIC char *
+getcomponent()
+{
+	char *p;
+	char *start;
+
+	if ((p = cdcomppath) == NULL)
+		return NULL;
+	start = cdcomppath;
+	while (*p != '/' && *p != '\0')
+		p++;
+	if (*p == '\0') {
+		cdcomppath = NULL;
+	} else {
+		*p++ = '\0';
+		cdcomppath = p;
+	}
+	return start;
+}
+
+
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.  We also call hashcd to let the routines in exec.c know
+ * that the current directory has changed.
+ */
+
+STATIC void
+updatepwd(char *dir)
+{
+	char *new;
+	char *p;
+
+	hashcd();				/* update command hash table */
+
+	/*
+	 * If our argument is NULL, we don't know the current directory
+	 * any more because we traversed a symbolic link or something
+	 * we couldn't stat().
+	 */
+	if (dir == NULL || curdir == NULL)  {
+		if (prevdir)
+			ckfree(prevdir);
+		INTOFF;
+		prevdir = curdir;
+		curdir = NULL;
+		getpwd(1);
+		INTON;
+		if (curdir)
+			setvar("PWD", curdir, VEXPORT);
+		else
+			unsetvar("PWD", 0);
+		return;
+	}
+	cdcomppath = stalloc(strlen(dir) + 1);
+	scopy(dir, cdcomppath);
+	STARTSTACKSTR(new);
+	if (*dir != '/') {
+		p = curdir;
+		while (*p)
+			STPUTC(*p++, new);
+		if (p[-1] == '/')
+			STUNPUTC(new);
+	}
+	while ((p = getcomponent()) != NULL) {
+		if (equal(p, "..")) {
+			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
+		} else if (*p != '\0' && ! equal(p, ".")) {
+			STPUTC('/', new);
+			while (*p)
+				STPUTC(*p++, new);
+		}
+	}
+	if (new == stackblock())
+		STPUTC('/', new);
+	STACKSTRNUL(new);
+	INTOFF;
+	if (prevdir)
+		ckfree(prevdir);
+	prevdir = curdir;
+	curdir = savestr(stackblock());
+	setvar("PWD", curdir, VEXPORT);
+	INTON;
+}
+
+/*
+ * Posix says the default should be 'pwd -L' (as below), however
+ * the 'cd' command (above) does something much nearer to the
+ * posix 'cd -P' (not the posix default of 'cd -L').
+ * If 'cd' is changed to support -P/L then the default here
+ * needs to be revisited if the historic behaviour is to be kept.
+ */
+
+int
+pwdcmd(int argc, char **argv)
+{
+	int i;
+	char opt = 'L';
+
+	while ((i = nextopt("LP")) != '\0')
+		opt = i;
+	if (*argptr)
+		error("unexpected argument");
+
+	if (opt == 'L')
+		getpwd(0);
+	else
+		find_curdir(0);
+
+	setvar("PWD", curdir, VEXPORT);
+	out1str(curdir);
+	out1c('\n');
+	return 0;
+}
+
+
+
+
+#define MAXPWD 256
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+void
+getpwd(int noerror)
+{
+	char *pwd;
+	struct stat stdot, stpwd;
+	static int first = 1;
+
+	if (curdir)
+		return;
+
+	if (first) {
+		first = 0;
+		pwd = getenv("PWD");
+		if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
+		    stat(pwd, &stpwd) != -1 &&
+		    stdot.st_dev == stpwd.st_dev &&
+		    stdot.st_ino == stpwd.st_ino) {
+			curdir = savestr(pwd);
+			return;
+		}
+	}
+
+	find_curdir(noerror);
+
+	return;
+}
+
+STATIC void
+find_curdir(int noerror)
+{
+	int i;
+	char *pwd;
+
+	/*
+	 * Things are a bit complicated here; we could have just used
+	 * getcwd, but traditionally getcwd is implemented using popen
+	 * to /bin/pwd. This creates a problem for us, since we cannot
+	 * keep track of the job if it is being ran behind our backs.
+	 * So we re-implement getcwd(), and we suppress interrupts
+	 * throughout the process. This is not completely safe, since
+	 * the user can still break out of it by killing the pwd program.
+	 * We still try to use getcwd for systems that we know have a
+	 * c implementation of getcwd, that does not open a pipe to
+	 * /bin/pwd.
+	 */
+#if defined(__NetBSD__) || defined(__SVR4) || defined(__linux__)
+	for (i = MAXPWD;; i *= 2) {
+		pwd = stalloc(i);
+		if (getcwd(pwd, i) != NULL) {
+			curdir = savestr(pwd);
+			return;
+		}
+		stunalloc(pwd);
+		if (errno == ERANGE)
+			continue;
+		if (!noerror)
+			error("getcwd() failed: %s", strerror(errno));
+		return;
+	}
+#else
+	{
+		char *p;
+		int status;
+		struct job *jp;
+		int pip[2];
+
+		pwd = stalloc(MAXPWD);
+		INTOFF;
+		if (pipe(pip) < 0)
+			error("Pipe call failed");
+		jp = makejob((union node *)NULL, 1);
+		if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
+			(void) close(pip[0]);
+			if (pip[1] != 1) {
+				close(1);
+				copyfd(pip[1], 1);
+				close(pip[1]);
+			}
+			(void) execl("/bin/pwd", "pwd", (char *)0);
+			sh_warn("Cannot exec /bin/pwd");
+			exit(1);
+		}
+		(void) close(pip[1]);
+		pip[1] = -1;
+		p = pwd;
+		while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0
+		     || (i == -1 && errno == EINTR)) {
+			if (i > 0)
+				p += i;
+		}
+		(void) close(pip[0]);
+		pip[0] = -1;
+		status = waitforjob(jp);
+		if (status != 0)
+			error((char *)0);
+		if (i < 0 || p == pwd || p[-1] != '\n') {
+			if (noerror) {
+				INTON;
+				return;
+			}
+			error("pwd command failed");
+		}
+		p[-1] = '\0';
+		INTON;
+		curdir = savestr(pwd);
+		return;
+	}
+#endif
+}
diff --git a/sh/cd.h b/sh/cd.h
new file mode 100644
index 0000000..a4dcc01
--- /dev/null
+++ b/sh/cd.h
@@ -0,0 +1,35 @@
+/*	$NetBSD: cd.h,v 1.4 2003/08/07 09:05:30 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1995
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+void	getpwd(int);
+int	cdcmd(int, char **);
+int	pwdcmd(int, char **);
diff --git a/sh/error.c b/sh/error.c
new file mode 100644
index 0000000..8cbed19
--- /dev/null
+++ b/sh/error.c
@@ -0,0 +1,366 @@
+/*	$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)error.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Errors and exceptions.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "shell.h"
+#include "main.h"
+#include "options.h"
+#include "output.h"
+#include "error.h"
+#include "show.h"
+
+#define signal bsd_signal
+/*
+ * Code to handle exceptions in C.
+ */
+
+struct jmploc *handler;
+int exception;
+volatile int suppressint;
+volatile int intpending;
+char *commandname;
+
+
+static void exverror(int, const char *, va_list)
+    __attribute__((__noreturn__));
+
+/*
+ * Called to raise an exception.  Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler.  The type of exception is
+ * stored in the global variable "exception".
+ */
+
+void
+exraise(int e)
+{
+	if (handler == NULL)
+		abort();
+	exception = e;
+	longjmp(handler->loc, 1);
+}
+
+
+/*
+ * Called from trap.c when a SIGINT is received.  (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.)  Suppressint is nonzero when interrupts
+ * are held using the INTOFF macro.  The call to _exit is necessary because
+ * there is a short period after a fork before the signal handlers are
+ * set to the appropriate value for the child.  (The test for iflag is
+ * just defensive programming.)
+ */
+
+void
+onint(void)
+{
+	sigset_t nsigset;
+
+	if (suppressint) {
+		intpending = 1;
+		return;
+	}
+	intpending = 0;
+	sigemptyset(&nsigset);
+	sigprocmask(SIG_SETMASK, &nsigset, NULL);
+	if (rootshell && iflag)
+		exraise(EXINT);
+	else {
+		signal(SIGINT, SIG_DFL);
+		raise(SIGINT);
+	}
+	/* NOTREACHED */
+}
+
+static void
+exvwarning(int sv_errno, const char *msg, va_list ap)
+{
+	/* Partially emulate line buffered output so that:
+	 *	printf '%d\n' 1 a 2
+	 * and
+	 *	printf '%d %d %d\n' 1 a 2
+	 * both generate sensible text when stdout and stderr are merged.
+	 */
+	if (output.nextc != output.buf && output.nextc[-1] == '\n')
+		flushout(&output);
+	if (commandname)
+		outfmt(&errout, "%s: ", commandname);
+	if (msg != NULL) {
+		doformat(&errout, msg, ap);
+		if (sv_errno >= 0)
+			outfmt(&errout, ": ");
+	}
+	if (sv_errno >= 0)
+		outfmt(&errout, "%s", strerror(sv_errno));
+	out2c('\n');
+	flushout(&errout);
+}
+
+/*
+ * Exverror is called to raise the error exception.  If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+static void
+exverror(int cond, const char *msg, va_list ap)
+{
+	CLEAR_PENDING_INT;
+	INTOFF;
+
+#ifdef DEBUG
+	if (msg) {
+		TRACE(("exverror(%d, \"", cond));
+		TRACEV((msg, ap));
+		TRACE(("\") pid=%d\n", getpid()));
+	} else
+		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
+#endif
+	if (msg)
+		exvwarning(-1, msg, ap);
+
+	flushall();
+	exraise(cond);
+	/* NOTREACHED */
+}
+
+
+void
+error(const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	exverror(EXERROR, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+
+void
+exerror(int cond, const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	exverror(cond, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+
+void
+sh_exit(int rval)
+{
+	exerrno = rval & 255;
+	exraise(EXEXEC);
+}
+
+void
+sh_err(int status, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	exvwarning(errno, fmt, ap);
+	va_end(ap);
+	sh_exit(status);
+}
+
+void
+sh_verr(int status, const char *fmt, va_list ap)
+{
+	exvwarning(errno, fmt, ap);
+	sh_exit(status);
+}
+
+void
+sh_errx(int status, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	exvwarning(-1, fmt, ap);
+	va_end(ap);
+	sh_exit(status);
+}
+
+void
+sh_verrx(int status, const char *fmt, va_list ap)
+{
+	exvwarning(-1, fmt, ap);
+	sh_exit(status);
+}
+
+void
+sh_warn(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	exvwarning(errno, fmt, ap);
+	va_end(ap);
+}
+
+void
+sh_vwarn(const char *fmt, va_list ap)
+{
+	exvwarning(errno, fmt, ap);
+}
+
+void
+sh_warnx(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	exvwarning(-1, fmt, ap);
+	va_end(ap);
+}
+
+void
+sh_vwarnx(const char *fmt, va_list ap)
+{
+	exvwarning(-1, fmt, ap);
+}
+
+
+/*
+ * Table of error messages.
+ */
+
+struct errname {
+	short errcode;		/* error number */
+	short action;		/* operation which encountered the error */
+	const char *msg;	/* text describing the error */
+};
+
+
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+
+STATIC const struct errname errormsg[] = {
+	{ EINTR,	ALL,	"interrupted" },
+	{ EACCES,	ALL,	"permission denied" },
+	{ EIO,		ALL,	"I/O error" },
+	{ EEXIST,	ALL,	"file exists" },
+	{ ENOENT,	E_OPEN,	"no such file" },
+	{ ENOENT,	E_CREAT,"directory nonexistent" },
+	{ ENOENT,	E_EXEC,	"not found" },
+	{ ENOTDIR,	E_OPEN,	"no such file" },
+	{ ENOTDIR,	E_CREAT,"directory nonexistent" },
+	{ ENOTDIR,	E_EXEC,	"not found" },
+	{ EISDIR,	ALL,	"is a directory" },
+#ifdef EMFILE
+	{ EMFILE,	ALL,	"too many open files" },
+#endif
+	{ ENFILE,	ALL,	"file table overflow" },
+	{ ENOSPC,	ALL,	"file system full" },
+#ifdef EDQUOT
+	{ EDQUOT,	ALL,	"disk quota exceeded" },
+#endif
+#ifdef ENOSR
+	{ ENOSR,	ALL,	"no streams resources" },
+#endif
+	{ ENXIO,	ALL,	"no such device or address" },
+	{ EROFS,	ALL,	"read-only file system" },
+	{ ETXTBSY,	ALL,	"text busy" },
+#ifdef EAGAIN
+	{ EAGAIN,	E_EXEC,	"not enough memory" },
+#endif
+	{ ENOMEM,	ALL,	"not enough memory" },
+#ifdef ENOLINK
+	{ ENOLINK,	ALL,	"remote access failed" },
+#endif
+#ifdef EMULTIHOP
+	{ EMULTIHOP,	ALL,	"remote access failed" },
+#endif
+#ifdef ECOMM
+	{ ECOMM,	ALL,	"remote access failed" },
+#endif
+#ifdef ESTALE
+	{ ESTALE,	ALL,	"remote access failed" },
+#endif
+#ifdef ETIMEDOUT
+	{ ETIMEDOUT,	ALL,	"remote access failed" },
+#endif
+#ifdef ELOOP
+	{ ELOOP,	ALL,	"symbolic link loop" },
+#endif
+	{ E2BIG,	E_EXEC,	"argument list too long" },
+#ifdef ELIBACC
+	{ ELIBACC,	E_EXEC,	"shared library missing" },
+#endif
+	{ 0,		0,	NULL },
+};
+
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+
+const char *
+errmsg(int e, int action)
+{
+	struct errname const *ep;
+	static char buf[12];
+
+	for (ep = errormsg ; ep->errcode ; ep++) {
+		if (ep->errcode == e && (ep->action & action) != 0)
+			return ep->msg;
+	}
+	fmtstr(buf, sizeof buf, "error %d", e);
+	return buf;
+}
diff --git a/sh/error.h b/sh/error.h
new file mode 100644
index 0000000..8e70ca4
--- /dev/null
+++ b/sh/error.h
@@ -0,0 +1,117 @@
+/*	$NetBSD: error.h,v 1.16 2003/08/07 09:05:30 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)error.h	8.2 (Berkeley) 5/4/95
+ */
+
+#include <stdarg.h>
+
+/*
+ * Types of operations (passed to the errmsg routine).
+ */
+
+#define E_OPEN 01	/* opening a file */
+#define E_CREAT 02	/* creating a file */
+#define E_EXEC 04	/* executing a program */
+
+
+/*
+ * We enclose jmp_buf in a structure so that we can declare pointers to
+ * jump locations.  The global variable handler contains the location to
+ * jump to when an exception occurs, and the global variable exception
+ * contains a code identifying the exeception.  To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+
+#include <setjmp.h>
+
+struct jmploc {
+	jmp_buf loc;
+};
+
+extern struct jmploc *handler;
+extern int exception;
+extern int exerrno;	/* error for EXEXEC */
+
+/* exceptions */
+#define EXINT 0		/* SIGINT received */
+#define EXERROR 1	/* a generic error */
+#define EXSHELLPROC 2	/* execute a shell procedure */
+#define EXEXEC 3	/* command execution failed */
+
+
+/*
+ * These macros allow the user to suspend the handling of interrupt signals
+ * over a period of time.  This is similar to SIGHOLD to or sigblock, but
+ * much more efficient and portable.  (But hacking the kernel is so much
+ * more fun than worrying about efficiency and portability. :-))
+ */
+
+extern volatile int suppressint;
+extern volatile int intpending;
+
+#define INTOFF suppressint++
+#define INTON { if (--suppressint == 0 && intpending) onint(); }
+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+#define CLEAR_PENDING_INT intpending = 0
+#define int_pending() intpending
+
+void exraise(int) __attribute__((__noreturn__));
+void onint(void);
+void error(const char *, ...) __attribute__((__noreturn__));
+void exerror(int, const char *, ...) __attribute__((__noreturn__));
+const char *errmsg(int, int);
+
+void sh_err(int, const char *, ...) __attribute__((__noreturn__));
+void sh_verr(int, const char *, va_list) __attribute__((__noreturn__));
+void sh_errx(int, const char *, ...) __attribute__((__noreturn__));
+void sh_verrx(int, const char *, va_list) __attribute__((__noreturn__));
+void sh_warn(const char *, ...);
+void sh_vwarn(const char *, va_list);
+void sh_warnx(const char *, ...);
+void sh_vwarnx(const char *, va_list);
+
+void sh_exit(int) __attribute__((__noreturn__));
+
+
+/*
+ * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
+ * so we use _setjmp instead.
+ */
+
+#if defined(BSD) && !defined(__SVR4) && !defined(__linux__)
+#define setjmp(jmploc)	_setjmp(jmploc)
+#define longjmp(jmploc, val)	_longjmp(jmploc, val)
+#endif
diff --git a/sh/eval.c b/sh/eval.c
new file mode 100644
index 0000000..9acfd64
--- /dev/null
+++ b/sh/eval.c
@@ -0,0 +1,1257 @@
+/*	$NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
+#else
+__RCSID("$NetBSD: eval.c,v 1.81.2.1 2005/06/13 22:03:51 tron Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#ifdef __linux__
+#include <fcntl.h>
+#else
+#include <sys/fcntl.h>
+#endif
+#include <sys/times.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/*
+ * Evaluate a command.
+ */
+
+#include "shell.h"
+#include "nodes.h"
+#include "syntax.h"
+#include "expand.h"
+#include "parser.h"
+#include "jobs.h"
+#include "eval.h"
+#include "builtins.h"
+#include "options.h"
+#include "exec.h"
+#include "redir.h"
+#include "input.h"
+#include "output.h"
+#include "trap.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "show.h"
+#include "mystring.h"
+#include "main.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01		/* exit after evaluating tree */
+#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04		/* command executing within back quotes */
+
+int evalskip;			/* set if we are skipping commands */
+STATIC int skipcount;		/* number of levels to skip */
+MKINIT int loopnest;		/* current loop nesting level */
+int funcnest;			/* depth of function calls */
+
+
+char *commandname;
+struct strlist *cmdenviron;
+int exitstatus;			/* exit status of last command */
+int back_exitstatus;		/* exit status of backquoted command */
+
+
+STATIC void evalloop(union node *, int);
+STATIC void evalfor(union node *, int);
+STATIC void evalcase(union node *, int);
+STATIC void evalsubshell(union node *, int);
+STATIC void expredir(union node *);
+STATIC void evalpipe(union node *);
+STATIC void evalcommand(union node *, int, struct backcmd *);
+STATIC void prehash(union node *);
+
+
+/*
+ * Called to reset things after an exception.
+ */
+
+#ifdef mkinit
+INCLUDE "eval.h"
+
+RESET {
+	evalskip = 0;
+	loopnest = 0;
+	funcnest = 0;
+}
+
+SHELLPROC {
+	exitstatus = 0;
+}
+#endif
+
+static int
+sh_pipe(int fds[2])
+{
+	int nfd;
+
+	if (pipe(fds))
+		return -1;
+
+	if (fds[0] < 3) {
+		nfd = fcntl(fds[0], F_DUPFD, 3);
+		if (nfd != -1) {
+			close(fds[0]);
+			fds[0] = nfd;
+		}
+	}
+
+	if (fds[1] < 3) {
+		nfd = fcntl(fds[1], F_DUPFD, 3);
+		if (nfd != -1) {
+			close(fds[1]);
+			fds[1] = nfd;
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * The eval commmand.
+ */
+
+int
+evalcmd(int argc, char **argv)
+{
+        char *p;
+        char *concat;
+        char **ap;
+
+        if (argc > 1) {
+                p = argv[1];
+                if (argc > 2) {
+                        STARTSTACKSTR(concat);
+                        ap = argv + 2;
+                        for (;;) {
+                                while (*p)
+                                        STPUTC(*p++, concat);
+                                if ((p = *ap++) == NULL)
+                                        break;
+                                STPUTC(' ', concat);
+                        }
+                        STPUTC('\0', concat);
+                        p = grabstackstr(concat);
+                }
+                evalstring(p, EV_TESTED);
+        }
+        return exitstatus;
+}
+
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+
+void
+evalstring(char *s, int flag)
+{
+	union node *n;
+	struct stackmark smark;
+
+	setstackmark(&smark);
+	setinputstring(s, 1);
+
+	while ((n = parsecmd(0)) != NEOF) {
+		evaltree(n, flag);
+		popstackmark(&smark);
+	}
+	popfile();
+	popstackmark(&smark);
+}
+
+
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+
+void
+evaltree(union node *n, int flags)
+{
+	if (n == NULL) {
+		TRACE(("evaltree(NULL) called\n"));
+		exitstatus = 0;
+		goto out;
+	}
+#ifdef WITH_HISTORY
+	displayhist = 1;	/* show history substitutions done with fc */
+#endif
+	TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
+	    getpid(), n, n->type, flags));
+	switch (n->type) {
+	case NSEMI:
+		evaltree(n->nbinary.ch1, flags & EV_TESTED);
+		if (evalskip)
+			goto out;
+		evaltree(n->nbinary.ch2, flags);
+		break;
+	case NAND:
+		evaltree(n->nbinary.ch1, EV_TESTED);
+		if (evalskip || exitstatus != 0)
+			goto out;
+		evaltree(n->nbinary.ch2, flags);
+		break;
+	case NOR:
+		evaltree(n->nbinary.ch1, EV_TESTED);
+		if (evalskip || exitstatus == 0)
+			goto out;
+		evaltree(n->nbinary.ch2, flags);
+		break;
+	case NREDIR:
+		expredir(n->nredir.redirect);
+		redirect(n->nredir.redirect, REDIR_PUSH);
+		evaltree(n->nredir.n, flags);
+		popredir();
+		break;
+	case NSUBSHELL:
+		evalsubshell(n, flags);
+		break;
+	case NBACKGND:
+		evalsubshell(n, flags);
+		break;
+	case NIF: {
+		evaltree(n->nif.test, EV_TESTED);
+		if (evalskip)
+			goto out;
+		if (exitstatus == 0)
+			evaltree(n->nif.ifpart, flags);
+		else if (n->nif.elsepart)
+			evaltree(n->nif.elsepart, flags);
+		else
+			exitstatus = 0;
+		break;
+	}
+	case NWHILE:
+	case NUNTIL:
+		evalloop(n, flags);
+		break;
+	case NFOR:
+		evalfor(n, flags);
+		break;
+	case NCASE:
+		evalcase(n, flags);
+		break;
+	case NDEFUN:
+		defun(n->narg.text, n->narg.next);
+		exitstatus = 0;
+		break;
+	case NNOT:
+		evaltree(n->nnot.com, EV_TESTED);
+		exitstatus = !exitstatus;
+		break;
+	case NPIPE:
+		evalpipe(n);
+		break;
+	case NCMD:
+		evalcommand(n, flags, (struct backcmd *)NULL);
+		break;
+	default:
+		out1fmt("Node type = %d\n", n->type);
+		flushout(&output);
+		break;
+	}
+out:
+	if (pendingsigs)
+		dotrap();
+	if ((flags & EV_EXIT) != 0)
+		exitshell(exitstatus);
+}
+
+
+STATIC void
+evalloop(union node *n, int flags)
+{
+	int status;
+
+	loopnest++;
+	status = 0;
+	for (;;) {
+		evaltree(n->nbinary.ch1, EV_TESTED);
+		if (evalskip) {
+skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+		if (n->type == NWHILE) {
+			if (exitstatus != 0)
+				break;
+		} else {
+			if (exitstatus == 0)
+				break;
+		}
+		evaltree(n->nbinary.ch2, flags & EV_TESTED);
+		status = exitstatus;
+		if (evalskip)
+			goto skipping;
+	}
+	loopnest--;
+	exitstatus = status;
+}
+
+
+
+STATIC void
+evalfor(union node *n, int flags)
+{
+	struct arglist arglist;
+	union node *argp;
+	struct strlist *sp;
+	struct stackmark smark;
+	int status = 0;
+
+	setstackmark(&smark);
+	arglist.lastp = &arglist.list;
+	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
+		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+		if (evalskip)
+			goto out;
+	}
+	*arglist.lastp = NULL;
+
+	loopnest++;
+	for (sp = arglist.list ; sp ; sp = sp->next) {
+		setvar(n->nfor.var, sp->text, 0);
+		evaltree(n->nfor.body, flags & EV_TESTED);
+		status = exitstatus;
+		if (evalskip) {
+			if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+	}
+	loopnest--;
+	exitstatus = status;
+out:
+	popstackmark(&smark);
+}
+
+
+
+STATIC void
+evalcase(union node *n, int flags)
+{
+	union node *cp;
+	union node *patp;
+	struct arglist arglist;
+	struct stackmark smark;
+	int status = 0;
+
+	setstackmark(&smark);
+	arglist.lastp = &arglist.list;
+	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
+		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
+			if (casematch(patp, arglist.list->text)) {
+				if (evalskip == 0) {
+					evaltree(cp->nclist.body, flags);
+					status = exitstatus;
+				}
+				goto out;
+			}
+		}
+	}
+out:
+	exitstatus = status;
+	popstackmark(&smark);
+}
+
+
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+STATIC void
+evalsubshell(union node *n, int flags)
+{
+	struct job *jp;
+	int backgnd = (n->type == NBACKGND);
+
+	expredir(n->nredir.redirect);
+	INTOFF;
+	jp = makejob(n, 1);
+	if (forkshell(jp, n, backgnd) == 0) {
+		INTON;
+		if (backgnd)
+			flags &=~ EV_TESTED;
+		redirect(n->nredir.redirect, 0);
+		/* never returns */
+		evaltree(n->nredir.n, flags | EV_EXIT);
+	}
+	if (! backgnd)
+		exitstatus = waitforjob(jp);
+	INTON;
+}
+
+
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+STATIC void
+expredir(union node *n)
+{
+	union node *redir;
+
+	for (redir = n ; redir ; redir = redir->nfile.next) {
+		struct arglist fn;
+		fn.lastp = &fn.list;
+		switch (redir->type) {
+		case NFROMTO:
+		case NFROM:
+		case NTO:
+		case NCLOBBER:
+		case NAPPEND:
+			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+			redir->nfile.expfname = fn.list->text;
+			break;
+		case NFROMFD:
+		case NTOFD:
+			if (redir->ndup.vname) {
+				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+				fixredir(redir, fn.list->text, 1);
+			}
+			break;
+		}
+	}
+}
+
+
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+
+STATIC void
+evalpipe(union node *n)
+{
+	struct job *jp;
+	struct nodelist *lp;
+	int pipelen;
+	int prevfd;
+	int pip[2];
+
+	TRACE(("evalpipe(0x%lx) called\n", (long)n));
+	pipelen = 0;
+	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+		pipelen++;
+	INTOFF;
+	jp = makejob(n, pipelen);
+	prevfd = -1;
+	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+		prehash(lp->n);
+		pip[1] = -1;
+		if (lp->next) {
+			if (sh_pipe(pip) < 0) {
+				close(prevfd);
+				error("Pipe call failed");
+			}
+		}
+		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+			INTON;
+			if (prevfd > 0) {
+				close(0);
+				copyfd(prevfd, 0);
+				close(prevfd);
+			}
+			if (pip[1] >= 0) {
+				close(pip[0]);
+				if (pip[1] != 1) {
+					close(1);
+					copyfd(pip[1], 1);
+					close(pip[1]);
+				}
+			}
+			evaltree(lp->n, EV_EXIT);
+		}
+		if (prevfd >= 0)
+			close(prevfd);
+		prevfd = pip[0];
+		close(pip[1]);
+	}
+	if (n->npipe.backgnd == 0) {
+		exitstatus = waitforjob(jp);
+		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
+	}
+	INTON;
+}
+
+
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+	int pip[2];
+	struct job *jp;
+	struct stackmark smark;		/* unnecessary */
+
+	setstackmark(&smark);
+	result->fd = -1;
+	result->buf = NULL;
+	result->nleft = 0;
+	result->jp = NULL;
+	if (n == NULL) {
+		goto out;
+	}
+#ifdef notyet
+	/*
+	 * For now we disable executing builtins in the same
+	 * context as the shell, because we are not keeping
+	 * enough state to recover from changes that are
+	 * supposed only to affect subshells. eg. echo "`cd /`"
+	 */
+	if (n->type == NCMD) {
+		exitstatus = oexitstatus;
+		evalcommand(n, EV_BACKCMD, result);
+	} else
+#endif
+	{
+		INTOFF;
+		if (sh_pipe(pip) < 0)
+			error("Pipe call failed");
+		jp = makejob(n, 1);
+		if (forkshell(jp, n, FORK_NOJOB) == 0) {
+			FORCEINTON;
+			close(pip[0]);
+			if (pip[1] != 1) {
+				close(1);
+				copyfd(pip[1], 1);
+				close(pip[1]);
+			}
+			eflag = 0;
+			evaltree(n, EV_EXIT);
+			/* NOTREACHED */
+		}
+		close(pip[1]);
+		result->fd = pip[0];
+		result->jp = jp;
+		INTON;
+	}
+out:
+	popstackmark(&smark);
+	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+		result->fd, result->buf, result->nleft, result->jp));
+}
+
+static const char *
+syspath(void)
+{
+	static char *sys_path = NULL;
+#ifndef __linux__    
+	static int mib[] = {CTL_USER, USER_CS_PATH};
+#endif
+	static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
+
+	if (sys_path == NULL) {
+#ifndef __linux__
+		size_t len;
+		if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
+		    (sys_path = ckmalloc(len + 5)) != NULL &&
+		    sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
+			memcpy(sys_path, "PATH=", 5);
+		} else
+#endif
+		{
+			ckfree(sys_path);
+			/* something to keep things happy */
+			sys_path = def_path;
+		}
+	}
+	return sys_path;
+}
+
+static int
+parse_command_args(int argc, char **argv, int *use_syspath)
+{
+	int sv_argc = argc;
+	char *cp, c;
+
+	*use_syspath = 0;
+
+	for (;;) {
+		argv++;
+		if (--argc == 0)
+			break;
+		cp = *argv;
+		if (*cp++ != '-')
+			break;
+		if (*cp == '-' && cp[1] == 0) {
+			argv++;
+			argc--;
+			break;
+		}
+		while ((c = *cp++)) {
+			switch (c) {
+			case 'p':
+				*use_syspath = 1;
+				break;
+			default:
+				/* run 'typecmd' for other options */
+				return 0;
+			}
+		}
+	}
+	return sv_argc - argc;
+}
+
+int vforked = 0;
+
+/*
+ * Execute a simple command.
+ */
+
+STATIC void
+evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
+{
+	struct stackmark smark;
+	union node *argp;
+	struct arglist arglist;
+	struct arglist varlist;
+	char **argv;
+	int argc;
+	char **envp;
+	int varflag;
+	struct strlist *sp;
+	int mode;
+	int pip[2];
+	struct cmdentry cmdentry;
+	struct job *jp;
+	struct jmploc jmploc;
+	struct jmploc *volatile savehandler;
+	char *volatile savecmdname;
+	volatile struct shparam saveparam;
+	struct localvar *volatile savelocalvars;
+	volatile int e;
+	char *lastarg;
+	const char *path = pathval();
+	volatile int temp_path;
+#if __GNUC__
+	/* Avoid longjmp clobbering */
+	(void) &argv;
+	(void) &argc;
+	(void) &lastarg;
+	(void) &flags;
+#endif
+
+	vforked = 0;
+	/* First expand the arguments. */
+	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+	setstackmark(&smark);
+	back_exitstatus = 0;
+
+	arglist.lastp = &arglist.list;
+	varflag = 1;
+	/* Expand arguments, ignoring the initial 'name=value' ones */
+	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
+		char *p = argp->narg.text;
+		if (varflag && is_name(*p)) {
+			do {
+				p++;
+			} while (is_in_name(*p));
+			if (*p == '=')
+				continue;
+		}
+		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+		varflag = 0;
+	}
+	*arglist.lastp = NULL;
+
+	expredir(cmd->ncmd.redirect);
+
+	/* Now do the initial 'name=value' ones we skipped above */
+	varlist.lastp = &varlist.list;
+	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
+		char *p = argp->narg.text;
+		if (!is_name(*p))
+			break;
+		do
+			p++;
+		while (is_in_name(*p));
+		if (*p != '=')
+			break;
+		expandarg(argp, &varlist, EXP_VARTILDE);
+	}
+	*varlist.lastp = NULL;
+
+	argc = 0;
+	for (sp = arglist.list ; sp ; sp = sp->next)
+		argc++;
+	argv = stalloc(sizeof (char *) * (argc + 1));
+
+	for (sp = arglist.list ; sp ; sp = sp->next) {
+		TRACE(("evalcommand arg: %s\n", sp->text));
+		*argv++ = sp->text;
+	}
+	*argv = NULL;
+	lastarg = NULL;
+	if (iflag && funcnest == 0 && argc > 0)
+		lastarg = argv[-1];
+	argv -= argc;
+
+	/* Print the command if xflag is set. */
+	if (xflag) {
+		char sep = 0;
+		out2str(ps4val());
+		for (sp = varlist.list ; sp ; sp = sp->next) {
+			if (sep != 0)
+				outc(sep, &errout);
+			out2str(sp->text);
+			sep = ' ';
+		}
+		for (sp = arglist.list ; sp ; sp = sp->next) {
+			if (sep != 0)
+				outc(sep, &errout);
+			out2str(sp->text);
+			sep = ' ';
+		}
+		outc('\n', &errout);
+		flushout(&errout);
+	}
+
+	/* Now locate the command. */
+	if (argc == 0) {
+		cmdentry.cmdtype = CMDSPLBLTIN;
+		cmdentry.u.bltin = bltincmd;
+	} else {
+		static const char PATH[] = "PATH=";
+		int cmd_flags = DO_ERR;
+
+		/*
+		 * Modify the command lookup path, if a PATH= assignment
+		 * is present
+		 */
+		for (sp = varlist.list; sp; sp = sp->next)
+			if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
+				path = sp->text + sizeof(PATH) - 1;
+
+		do {
+			int argsused, use_syspath;
+			find_command(argv[0], &cmdentry, cmd_flags, path);
+			if (cmdentry.cmdtype == CMDUNKNOWN) {
+				exitstatus = 127;
+				flushout(&errout);
+				goto out;
+			}
+
+			/* implement the 'command' builtin here */
+			if (cmdentry.cmdtype != CMDBUILTIN ||
+			    cmdentry.u.bltin != bltincmd)
+				break;
+			cmd_flags |= DO_NOFUNC;
+			argsused = parse_command_args(argc, argv, &use_syspath);
+			if (argsused == 0) {
+				/* use 'type' builting to display info */
+				cmdentry.u.bltin = typecmd;
+				break;
+			}
+			argc -= argsused;
+			argv += argsused;
+			if (use_syspath)
+				path = syspath() + 5;
+		} while (argc != 0);
+		if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
+			/* posix mandates that 'command <splbltin>' act as if
+			   <splbltin> was a normal builtin */
+			cmdentry.cmdtype = CMDBUILTIN;
+	}
+
+	/* Fork off a child process if necessary. */
+	if (cmd->ncmd.backgnd
+	 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
+	 || ((flags & EV_BACKCMD) != 0
+	    && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
+		 || cmdentry.u.bltin == dotcmd
+		 || cmdentry.u.bltin == evalcmd))) {
+		INTOFF;
+		jp = makejob(cmd, 1);
+		mode = cmd->ncmd.backgnd;
+		if (flags & EV_BACKCMD) {
+			mode = FORK_NOJOB;
+			if (sh_pipe(pip) < 0)
+				error("Pipe call failed");
+		}
+#ifdef DO_SHAREDVFORK
+		/* It is essential that if DO_SHAREDVFORK is defined that the
+		 * child's address space is actually shared with the parent as
+		 * we rely on this.
+		 */
+		if (cmdentry.cmdtype == CMDNORMAL) {
+			pid_t	pid;
+
+			savelocalvars = localvars;
+			localvars = NULL;
+			vforked = 1;
+			switch (pid = vfork()) {
+			case -1:
+				TRACE(("Vfork failed, errno=%d\n", errno));
+				INTON;
+				error("Cannot vfork");
+				break;
+			case 0:
+				/* Make sure that exceptions only unwind to
+				 * after the vfork(2)
+				 */
+				if (setjmp(jmploc.loc)) {
+					if (exception == EXSHELLPROC) {
+						/* We can't progress with the vfork,
+						 * so, set vforked = 2 so the parent
+						 * knows, and _exit();
+						 */
+						vforked = 2;
+						_exit(0);
+					} else {
+						_exit(exerrno);
+					}
+				}
+				savehandler = handler;
+				handler = &jmploc;
+				listmklocal(varlist.list, VEXPORT | VNOFUNC);
+				forkchild(jp, cmd, mode, vforked);
+				break;
+			default:
+				handler = savehandler;	/* restore from vfork(2) */
+				poplocalvars();
+				localvars = savelocalvars;
+				if (vforked == 2) {
+					vforked = 0;
+
+					(void)waitpid(pid, NULL, 0);
+					/* We need to progress in a normal fork fashion */
+					goto normal_fork;
+				}
+				vforked = 0;
+				forkparent(jp, cmd, mode, pid);
+				goto parent;
+			}
+		} else {
+normal_fork:
+#endif
+			if (forkshell(jp, cmd, mode) != 0)
+				goto parent;	/* at end of routine */
+			FORCEINTON;
+#ifdef DO_SHAREDVFORK
+		}
+#endif
+		if (flags & EV_BACKCMD) {
+			if (!vforked) {
+				FORCEINTON;
+			}
+			close(pip[0]);
+			if (pip[1] != 1) {
+				close(1);
+				copyfd(pip[1], 1);
+				close(pip[1]);
+			}
+		}
+		flags |= EV_EXIT;
+	}
+
+	/* This is the child process if a fork occurred. */
+	/* Execute the command. */
+	switch (cmdentry.cmdtype) {
+	case CMDFUNCTION:
+#ifdef DEBUG
+		trputs("Shell function:  ");  trargs(argv);
+#endif
+		redirect(cmd->ncmd.redirect, REDIR_PUSH);
+		saveparam = shellparam;
+		shellparam.malloc = 0;
+		shellparam.reset = 1;
+		shellparam.nparam = argc - 1;
+		shellparam.p = argv + 1;
+		shellparam.optnext = NULL;
+		INTOFF;
+		savelocalvars = localvars;
+		localvars = NULL;
+		INTON;
+		if (setjmp(jmploc.loc)) {
+			if (exception == EXSHELLPROC) {
+				freeparam((volatile struct shparam *)
+				    &saveparam);
+			} else {
+				freeparam(&shellparam);
+				shellparam = saveparam;
+			}
+			poplocalvars();
+			localvars = savelocalvars;
+			handler = savehandler;
+			longjmp(handler->loc, 1);
+		}
+		savehandler = handler;
+		handler = &jmploc;
+		listmklocal(varlist.list, 0);
+		/* stop shell blowing its stack */
+		if (++funcnest > 1000)
+			error("too many nested function calls");
+		evaltree(cmdentry.u.func, flags & EV_TESTED);
+		funcnest--;
+		INTOFF;
+		poplocalvars();
+		localvars = savelocalvars;
+		freeparam(&shellparam);
+		shellparam = saveparam;
+		handler = savehandler;
+		popredir();
+		INTON;
+		if (evalskip == SKIPFUNC) {
+			evalskip = 0;
+			skipcount = 0;
+		}
+		if (flags & EV_EXIT)
+			exitshell(exitstatus);
+		break;
+
+	case CMDBUILTIN:
+	case CMDSPLBLTIN:
+#ifdef DEBUG
+		trputs("builtin command:  ");  trargs(argv);
+#endif
+		mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
+		if (flags == EV_BACKCMD) {
+			memout.nleft = 0;
+			memout.nextc = memout.buf;
+			memout.bufsize = 64;
+			mode |= REDIR_BACKQ;
+		}
+		e = -1;
+		savehandler = handler;
+		savecmdname = commandname;
+		handler = &jmploc;
+		if (!setjmp(jmploc.loc)) {
+			/* We need to ensure the command hash table isn't
+			 * corruped by temporary PATH assignments.
+			 * However we must ensure the 'local' command works!
+			 */
+			if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
+			    cmdentry.u.bltin == typecmd)) {
+				savelocalvars = localvars;
+				localvars = 0;
+				mklocal(path - 5 /* PATH= */, 0);
+				temp_path = 1;
+			} else
+				temp_path = 0;
+			redirect(cmd->ncmd.redirect, mode);
+
+			/* exec is a special builtin, but needs this list... */
+			cmdenviron = varlist.list;
+			/* we must check 'readonly' flag for all builtins */
+			listsetvar(varlist.list,
+				cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
+			commandname = argv[0];
+			/* initialize nextopt */
+			argptr = argv + 1;
+			optptr = NULL;
+			/* and getopt */
+#ifndef __linux__
+			optreset = 1;
+#endif
+			optind = 1;
+			exitstatus = cmdentry.u.bltin(argc, argv);
+		} else {
+			e = exception;
+			exitstatus = e == EXINT ? SIGINT + 128 :
+					e == EXEXEC ? exerrno : 2;
+		}
+		handler = savehandler;
+		flushall();
+		out1 = &output;
+		out2 = &errout;
+		freestdout();
+		if (temp_path) {
+			poplocalvars();
+			localvars = savelocalvars;
+		}
+		cmdenviron = NULL;
+		if (e != EXSHELLPROC) {
+			commandname = savecmdname;
+			if (flags & EV_EXIT)
+				exitshell(exitstatus);
+		}
+		if (e != -1) {
+			if ((e != EXERROR && e != EXEXEC)
+			    || cmdentry.cmdtype == CMDSPLBLTIN)
+				exraise(e);
+			FORCEINTON;
+		}
+		if (cmdentry.u.bltin != execcmd)
+			popredir();
+		if (flags == EV_BACKCMD) {
+			backcmd->buf = memout.buf;
+			backcmd->nleft = memout.nextc - memout.buf;
+			memout.buf = NULL;
+		}
+		break;
+
+	default:
+#ifdef DEBUG
+		trputs("normal command:  ");  trargs(argv);
+#endif
+		clearredir(vforked);
+		redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
+		if (!vforked)
+			for (sp = varlist.list ; sp ; sp = sp->next)
+				setvareq(sp->text, VEXPORT|VSTACK);
+		envp = environment();
+		shellexec(argv, envp, path, cmdentry.u.index, vforked);
+		break;
+	}
+	goto out;
+
+parent:	/* parent process gets here (if we forked) */
+	if (mode == FORK_FG) {	/* argument to fork */
+		exitstatus = waitforjob(jp);
+	} else if (mode == FORK_NOJOB) {
+		backcmd->fd = pip[0];
+		close(pip[1]);
+		backcmd->jp = jp;
+	}
+	FORCEINTON;
+
+out:
+	if (lastarg)
+		/* dsl: I think this is intended to be used to support
+		 * '_' in 'vi' command mode during line editing...
+		 * However I implemented that within libedit itself.
+		 */
+		setvar("_", lastarg, 0);
+	popstackmark(&smark);
+
+	if (eflag && exitstatus && !(flags & EV_TESTED))
+		exitshell(exitstatus);
+}
+
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+STATIC void
+prehash(union node *n)
+{
+	struct cmdentry entry;
+
+	if (n->type == NCMD && n->ncmd.args)
+		if (goodname(n->ncmd.args->narg.text))
+			find_command(n->ncmd.args->narg.text, &entry, 0,
+				     pathval());
+}
+
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given.
+ */
+
+int
+bltincmd(int argc, char **argv)
+{
+	/*
+	 * Preserve exitstatus of a previous possible redirection
+	 * as POSIX mandates
+	 */
+	return back_exitstatus;
+}
+
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+
+int
+breakcmd(int argc, char **argv)
+{
+	int n = argc > 1 ? number(argv[1]) : 1;
+
+	if (n > loopnest)
+		n = loopnest;
+	if (n > 0) {
+		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+		skipcount = n;
+	}
+	return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+int
+returncmd(int argc, char **argv)
+{
+	int ret = argc > 1 ? number(argv[1]) : exitstatus;
+
+	if (funcnest) {
+		evalskip = SKIPFUNC;
+		skipcount = 1;
+		return ret;
+	}
+	else {
+		/* Do what ksh does; skip the rest of the file */
+		evalskip = SKIPFILE;
+		skipcount = 1;
+		return ret;
+	}
+}
+
+
+int
+falsecmd(int argc, char **argv)
+{
+	return 1;
+}
+
+
+int
+truecmd(int argc, char **argv)
+{
+	return 0;
+}
+
+
+int
+execcmd(int argc, char **argv)
+{
+	if (argc > 1) {
+		struct strlist *sp;
+
+		iflag = 0;		/* exit on error */
+		mflag = 0;
+		optschanged();
+		for (sp = cmdenviron; sp; sp = sp->next)
+			setvareq(sp->text, VEXPORT|VSTACK);
+		shellexec(argv + 1, environment(), pathval(), 0, 0);
+	}
+	return 0;
+}
+
+static int
+conv_time(clock_t ticks, char *seconds, size_t l)
+{
+	static clock_t tpm = 0;
+	clock_t mins;
+	int i;
+
+	mins = ticks / tpm;
+	snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
+
+	if (seconds[0] == '6' && seconds[1] == '0') {
+		/* 59.99995 got rounded up... */
+		mins++;
+		strlcpy(seconds, "0.0", l);
+		return mins;
+	}
+
+	/* suppress trailing zeros */
+	i = strlen(seconds) - 1;
+	for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
+		seconds[i] = 0;
+	return mins;
+}
+
+int
+timescmd(int argc, char **argv)
+{
+	struct tms tms;
+	int u, s, cu, cs;
+	char us[8], ss[8], cus[8], css[8];
+
+	nextopt("");
+
+	times(&tms);
+
+	u = conv_time(tms.tms_utime, us, sizeof(us));
+	s = conv_time(tms.tms_stime, ss, sizeof(ss));
+	cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
+	cs = conv_time(tms.tms_cstime, css, sizeof(css));
+
+	outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
+		u, us, s, ss, cu, cus, cs, css);
+
+	return 0;
+}
diff --git a/sh/eval.h b/sh/eval.h
new file mode 100644
index 0000000..155bc44
--- /dev/null
+++ b/sh/eval.h
@@ -0,0 +1,64 @@
+/*	$NetBSD: eval.h,v 1.14 2003/08/07 09:05:31 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)eval.h	8.2 (Berkeley) 5/4/95
+ */
+
+extern char *commandname;	/* currently executing command */
+extern int exitstatus;		/* exit status of last command */
+extern int back_exitstatus;	/* exit status of backquoted command */
+extern struct strlist *cmdenviron;  /* environment for builtin command */
+
+
+struct backcmd {		/* result of evalbackcmd */
+	int fd;			/* file descriptor to read from */
+	char *buf;		/* buffer */
+	int nleft;		/* number of chars in buffer */
+	struct job *jp;		/* job structure for command */
+};
+
+void evalstring(char *, int);
+union node;	/* BLETCH for ansi C */
+void evaltree(union node *, int);
+void evalbackcmd(union node *, struct backcmd *);
+
+/* in_function returns nonzero if we are currently evaluating a function */
+#define in_function()	funcnest
+extern int funcnest;
+extern int evalskip;
+
+/* reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK	1
+#define SKIPCONT	2
+#define SKIPFUNC	3
+#define SKIPFILE	4
diff --git a/sh/exec.c b/sh/exec.c
new file mode 100644
index 0000000..fe3613f
--- /dev/null
+++ b/sh/exec.c
@@ -0,0 +1,1063 @@
+/*	$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
+#else
+__RCSID("$NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "parser.h"
+#include "redir.h"
+#include "eval.h"
+#include "exec.h"
+#include "builtins.h"
+#include "var.h"
+#include "options.h"
+#include "input.h"
+#include "output.h"
+#include "syntax.h"
+#include "memalloc.h"
+#include "error.h"
+#include "init.h"
+#include "mystring.h"
+#include "show.h"
+#include "jobs.h"
+#include "alias.h"
+
+
+#define CMDTABLESIZE 31		/* should be prime */
+#define ARB 1			/* actual size determined at run time */
+
+
+
+struct tblentry {
+	struct tblentry *next;	/* next entry in hash chain */
+	union param param;	/* definition of builtin function */
+	short cmdtype;		/* index identifying command */
+	char rehash;		/* if set, cd done since entry created */
+	char cmdname[ARB];	/* name of command */
+};
+
+
+STATIC struct tblentry *cmdtable[CMDTABLESIZE];
+STATIC int builtinloc = -1;		/* index in path of %builtin, or -1 */
+int exerrno = 0;			/* Last exec error */
+
+
+STATIC void tryexec(char *, char **, char **, int);
+STATIC void execinterp(char **, char **);
+STATIC void printentry(struct tblentry *, int);
+STATIC void clearcmdentry(int);
+STATIC struct tblentry *cmdlookup(const char *, int);
+STATIC void delete_cmd_entry(void);
+
+
+extern char *const parsekwd[];
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+
+void
+shellexec(char **argv, char **envp, const char *path, int idx, int vforked)
+{
+	char *cmdname;
+	int e;
+
+	if (strchr(argv[0], '/') != NULL) {
+		tryexec(argv[0], argv, envp, vforked);
+		e = errno;
+	} else {
+		e = ENOENT;
+		while ((cmdname = padvance(&path, argv[0])) != NULL) {
+			if (--idx < 0 && pathopt == NULL) {
+				tryexec(cmdname, argv, envp, vforked);
+				if (errno != ENOENT && errno != ENOTDIR)
+					e = errno;
+			}
+			stunalloc(cmdname);
+		}
+	}
+
+	/* Map to POSIX errors */
+	switch (e) {
+	case EACCES:
+		exerrno = 126;
+		break;
+	case ENOENT:
+		exerrno = 127;
+		break;
+	default:
+		exerrno = 2;
+		break;
+	}
+	TRACE(("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n",
+		argv[0], e, vforked, suppressint ));
+	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
+	/* NOTREACHED */
+}
+
+
+STATIC void
+tryexec(char *cmd, char **argv, char **envp, int vforked)
+{
+	int e;
+#ifndef BSD
+	char *p;
+#endif
+
+#ifdef SYSV
+	do {
+		execve(cmd, argv, envp);
+	} while (errno == EINTR);
+#else
+	execve(cmd, argv, envp);
+#endif
+	e = errno;
+	if (e == ENOEXEC) {
+		if (vforked) {
+			/* We are currently vfork(2)ed, so raise an
+			 * exception, and evalcommand will try again
+			 * with a normal fork(2).
+			 */
+			exraise(EXSHELLPROC);
+		}
+		initshellproc();
+		setinputfile(cmd, 0);
+		commandname = arg0 = savestr(argv[0]);
+#if !defined(BSD) && !defined(__linux__)
+		pgetc(); pungetc();		/* fill up input buffer */
+		p = parsenextc;
+		if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
+			argv[0] = cmd;
+			execinterp(argv, envp);
+		}
+#endif
+		setparam(argv + 1);
+		exraise(EXSHELLPROC);
+	}
+	errno = e;
+}
+
+
+#if !defined(BSD) && !defined(__linux__)
+/*
+ * Execute an interpreter introduced by "#!", for systems where this
+ * feature has not been built into the kernel.  If the interpreter is
+ * the shell, return (effectively ignoring the "#!").  If the execution
+ * of the interpreter fails, exit.
+ *
+ * This code peeks inside the input buffer in order to avoid actually
+ * reading any input.  It would benefit from a rewrite.
+ */
+
+#define NEWARGS 5
+
+STATIC void
+execinterp(char **argv, char **envp)
+{
+	int n;
+	char *inp;
+	char *outp;
+	char c;
+	char *p;
+	char **ap;
+	char *newargs[NEWARGS];
+	int i;
+	char **ap2;
+	char **new;
+
+	n = parsenleft - 2;
+	inp = parsenextc + 2;
+	ap = newargs;
+	for (;;) {
+		while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
+			inp++;
+		if (n < 0)
+			goto bad;
+		if ((c = *inp++) == '\n')
+			break;
+		if (ap == &newargs[NEWARGS])
+bad:		  error("Bad #! line");
+		STARTSTACKSTR(outp);
+		do {
+			STPUTC(c, outp);
+		} while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
+		STPUTC('\0', outp);
+		n++, inp--;
+		*ap++ = grabstackstr(outp);
+	}
+	if (ap == newargs + 1) {	/* if no args, maybe no exec is needed */
+		p = newargs[0];
+		for (;;) {
+			if (equal(p, "sh") || equal(p, "ash")) {
+				return;
+			}
+			while (*p != '/') {
+				if (*p == '\0')
+					goto break2;
+				p++;
+			}
+			p++;
+		}
+break2:;
+	}
+	i = (char *)ap - (char *)newargs;		/* size in bytes */
+	if (i == 0)
+		error("Bad #! line");
+	for (ap2 = argv ; *ap2++ != NULL ; );
+	new = ckmalloc(i + ((char *)ap2 - (char *)argv));
+	ap = newargs, ap2 = new;
+	while ((i -= sizeof (char **)) >= 0)
+		*ap2++ = *ap++;
+	ap = argv;
+	while (*ap2++ = *ap++);
+	shellexec(new, envp, pathval(), 0);
+	/* NOTREACHED */
+}
+#endif
+
+
+
+/*
+ * Do a path search.  The variable path (passed by reference) should be
+ * set to the start of the path before the first call; padvance will update
+ * this value as it proceeds.  Successive calls to padvance will return
+ * the possible path expansions in sequence.  If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+
+const char *pathopt;
+
+char *
+padvance(const char **path, const char *name)
+{
+	const char *p;
+	char *q;
+	const char *start;
+	int len;
+
+	if (*path == NULL)
+		return NULL;
+	start = *path;
+	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+	len = p - start + strlen(name) + 2;	/* "2" is for '/' and '\0' */
+	while (stackblocksize() < len)
+		growstackblock();
+	q = stackblock();
+	if (p != start) {
+		memcpy(q, start, p - start);
+		q += p - start;
+		*q++ = '/';
+	}
+	strcpy(q, name);
+	pathopt = NULL;
+	if (*p == '%') {
+		pathopt = ++p;
+		while (*p && *p != ':')  p++;
+	}
+	if (*p == ':')
+		*path = p + 1;
+	else
+		*path = NULL;
+	return stalloc(len);
+}
+
+
+
+/*** Command hashing code ***/
+
+
+int
+hashcmd(int argc, char **argv)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+	int c;
+	int verbose;
+	struct cmdentry entry;
+	char *name;
+
+	verbose = 0;
+	while ((c = nextopt("rv")) != '\0') {
+		if (c == 'r') {
+			clearcmdentry(0);
+		} else if (c == 'v') {
+			verbose++;
+		}
+	}
+	if (*argptr == NULL) {
+		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+				if (verbose || cmdp->cmdtype == CMDNORMAL)
+					printentry(cmdp, verbose);
+			}
+		}
+		return 0;
+	}
+	while ((name = *argptr) != NULL) {
+		if ((cmdp = cmdlookup(name, 0)) != NULL
+		 && (cmdp->cmdtype == CMDNORMAL
+		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+			delete_cmd_entry();
+		find_command(name, &entry, DO_ERR, pathval());
+		if (verbose) {
+			if (entry.cmdtype != CMDUNKNOWN) {	/* if no error msg */
+				cmdp = cmdlookup(name, 0);
+				printentry(cmdp, verbose);
+			}
+			flushall();
+		}
+		argptr++;
+	}
+	return 0;
+}
+
+
+STATIC void
+printentry(struct tblentry *cmdp, int verbose)
+{
+	int idx;
+	const char *path;
+	char *name;
+
+	switch (cmdp->cmdtype) {
+	case CMDNORMAL:
+		idx = cmdp->param.index;
+		path = pathval();
+		do {
+			name = padvance(&path, cmdp->cmdname);
+			stunalloc(name);
+		} while (--idx >= 0);
+		out1str(name);
+		break;
+	case CMDSPLBLTIN:
+		out1fmt("special builtin %s", cmdp->cmdname);
+		break;
+	case CMDBUILTIN:
+		out1fmt("builtin %s", cmdp->cmdname);
+		break;
+	case CMDFUNCTION:
+		out1fmt("function %s", cmdp->cmdname);
+		if (verbose) {
+			struct procstat ps;
+			INTOFF;
+			commandtext(&ps, cmdp->param.func);
+			INTON;
+			out1str("() { ");
+			out1str(ps.cmd);
+			out1str("; }");
+		}
+		break;
+	default:
+		error("internal error: %s cmdtype %d", cmdp->cmdname, cmdp->cmdtype);
+	}
+	if (cmdp->rehash)
+		out1c('*');
+	out1c('\n');
+}
+
+
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+
+void
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
+{
+	struct tblentry *cmdp, loc_cmd;
+	int idx;
+	int prev;
+	char *fullname;
+	struct stat statb;
+	int e;
+	int (*bltin)(int,char **);
+
+	/* If name contains a slash, don't use PATH or hash table */
+	if (strchr(name, '/') != NULL) {
+		if (act & DO_ABS) {
+			while (stat(name, &statb) < 0) {
+#ifdef SYSV
+				if (errno == EINTR)
+					continue;
+#endif
+				if (errno != ENOENT && errno != ENOTDIR)
+					e = errno;
+				entry->cmdtype = CMDUNKNOWN;
+				entry->u.index = -1;
+				return;
+			}
+			entry->cmdtype = CMDNORMAL;
+			entry->u.index = -1;
+			return;
+		}
+		entry->cmdtype = CMDNORMAL;
+		entry->u.index = 0;
+		return;
+	}
+
+	if (path != pathval())
+		act |= DO_ALTPATH;
+
+	if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL)
+		act |= DO_ALTBLTIN;
+
+	/* If name is in the table, check answer will be ok */
+	if ((cmdp = cmdlookup(name, 0)) != NULL) {
+		do {
+			switch (cmdp->cmdtype) {
+			case CMDNORMAL:
+				if (act & DO_ALTPATH) {
+					cmdp = NULL;
+					continue;
+				}
+				break;
+			case CMDFUNCTION:
+				if (act & DO_NOFUNC) {
+					cmdp = NULL;
+					continue;
+				}
+				break;
+			case CMDBUILTIN:
+				if ((act & DO_ALTBLTIN) || builtinloc >= 0) {
+					cmdp = NULL;
+					continue;
+				}
+				break;
+			}
+			/* if not invalidated by cd, we're done */
+			if (cmdp->rehash == 0)
+				goto success;
+		} while (0);
+	}
+
+	/* If %builtin not in path, check for builtin next */
+	if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) &&
+	    (bltin = find_builtin(name)) != 0)
+		goto builtin_success;
+
+	/* We have to search path. */
+	prev = -1;		/* where to start */
+	if (cmdp) {		/* doing a rehash */
+		if (cmdp->cmdtype == CMDBUILTIN)
+			prev = builtinloc;
+		else
+			prev = cmdp->param.index;
+	}
+
+	e = ENOENT;
+	idx = -1;
+loop:
+	while ((fullname = padvance(&path, name)) != NULL) {
+		stunalloc(fullname);
+		idx++;
+		if (pathopt) {
+			if (prefix("builtin", pathopt)) {
+				if ((bltin = find_builtin(name)) == 0)
+					goto loop;
+				goto builtin_success;
+			} else if (prefix("func", pathopt)) {
+				/* handled below */
+			} else {
+				/* ignore unimplemented options */
+				goto loop;
+			}
+		}
+		/* if rehash, don't redo absolute path names */
+		if (fullname[0] == '/' && idx <= prev) {
+			if (idx < prev)
+				goto loop;
+			TRACE(("searchexec \"%s\": no change\n", name));
+			goto success;
+		}
+		while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+			if (errno == EINTR)
+				continue;
+#endif
+			if (errno != ENOENT && errno != ENOTDIR)
+				e = errno;
+			goto loop;
+		}
+		e = EACCES;	/* if we fail, this will be the error */
+		if (!S_ISREG(statb.st_mode))
+			goto loop;
+		if (pathopt) {		/* this is a %func directory */
+			if (act & DO_NOFUNC)
+				goto loop;
+			stalloc(strlen(fullname) + 1);
+			readcmdfile(fullname);
+			if ((cmdp = cmdlookup(name, 0)) == NULL ||
+			    cmdp->cmdtype != CMDFUNCTION)
+				error("%s not defined in %s", name, fullname);
+			stunalloc(fullname);
+			goto success;
+		}
+#ifdef notdef
+		/* XXX this code stops root executing stuff, and is buggy
+		   if you need a group from the group list. */
+		if (statb.st_uid == geteuid()) {
+			if ((statb.st_mode & 0100) == 0)
+				goto loop;
+		} else if (statb.st_gid == getegid()) {
+			if ((statb.st_mode & 010) == 0)
+				goto loop;
+		} else {
+			if ((statb.st_mode & 01) == 0)
+				goto loop;
+		}
+#endif
+		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+		INTOFF;
+		if (act & DO_ALTPATH) {
+			stalloc(strlen(fullname) + 1);
+			cmdp = &loc_cmd;
+		} else
+			cmdp = cmdlookup(name, 1);
+		cmdp->cmdtype = CMDNORMAL;
+		cmdp->param.index = idx;
+		INTON;
+		goto success;
+	}
+
+	/* We failed.  If there was an entry for this command, delete it */
+	if (cmdp)
+		delete_cmd_entry();
+	if (act & DO_ERR)
+		outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+	entry->cmdtype = CMDUNKNOWN;
+	return;
+
+builtin_success:
+	INTOFF;
+	if (act & DO_ALTPATH)
+		cmdp = &loc_cmd;
+	else
+		cmdp = cmdlookup(name, 1);
+	if (cmdp->cmdtype == CMDFUNCTION)
+		/* DO_NOFUNC must have been set */
+		cmdp = &loc_cmd;
+	cmdp->cmdtype = CMDBUILTIN;
+	cmdp->param.bltin = bltin;
+	INTON;
+success:
+	cmdp->rehash = 0;
+	entry->cmdtype = cmdp->cmdtype;
+	entry->u = cmdp->param;
+}
+
+
+
+/*
+ * Search the table of builtin commands.
+ */
+
+int
+(*find_builtin(name))(int, char **)
+	char *name;
+{
+	const struct builtincmd *bp;
+
+	for (bp = builtincmd ; bp->name ; bp++) {
+		if (*bp->name == *name && equal(bp->name, name))
+			return bp->builtin;
+	}
+	return 0;
+}
+
+int
+(*find_splbltin(name))(int, char **)
+	char *name;
+{
+	const struct builtincmd *bp;
+
+	for (bp = splbltincmd ; bp->name ; bp++) {
+		if (*bp->name == *name && equal(bp->name, name))
+			return bp->builtin;
+	}
+	return 0;
+}
+
+/*
+ * At shell startup put special builtins into hash table.
+ * ensures they are executed first (see posix).
+ * We stop functions being added with the same name
+ * (as they are impossible to call)
+ */
+
+void
+hash_special_builtins(void)
+{
+	const struct builtincmd *bp;
+	struct tblentry *cmdp;
+
+	for (bp = splbltincmd ; bp->name ; bp++) {
+		cmdp = cmdlookup(bp->name, 1);
+		cmdp->cmdtype = CMDSPLBLTIN;
+		cmdp->param.bltin = bp->builtin;
+	}
+}
+
+
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+
+void
+hashcd(void)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+			if (cmdp->cmdtype == CMDNORMAL
+			 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+				cmdp->rehash = 1;
+		}
+	}
+}
+
+
+
+/*
+ * Fix command hash table when PATH changed.
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.
+ * Called with interrupts off.
+ */
+
+void
+changepath(const char *newval)
+{
+	const char *old, *new;
+	int idx;
+	int firstchange;
+	int bltin;
+
+	old = pathval();
+	new = newval;
+	firstchange = 9999;	/* assume no change */
+	idx = 0;
+	bltin = -1;
+	for (;;) {
+		if (*old != *new) {
+			firstchange = idx;
+			if ((*old == '\0' && *new == ':')
+			 || (*old == ':' && *new == '\0'))
+				firstchange++;
+			old = new;	/* ignore subsequent differences */
+		}
+		if (*new == '\0')
+			break;
+		if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
+			bltin = idx;
+		if (*new == ':') {
+			idx++;
+		}
+		new++, old++;
+	}
+	if (builtinloc < 0 && bltin >= 0)
+		builtinloc = bltin;		/* zap builtins */
+	if (builtinloc >= 0 && bltin < 0)
+		firstchange = 0;
+	clearcmdentry(firstchange);
+	builtinloc = bltin;
+}
+
+
+/*
+ * Clear out command entries.  The argument specifies the first entry in
+ * PATH which has changed.
+ */
+
+STATIC void
+clearcmdentry(int firstchange)
+{
+	struct tblentry **tblp;
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	INTOFF;
+	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+		pp = tblp;
+		while ((cmdp = *pp) != NULL) {
+			if ((cmdp->cmdtype == CMDNORMAL &&
+			     cmdp->param.index >= firstchange)
+			 || (cmdp->cmdtype == CMDBUILTIN &&
+			     builtinloc >= firstchange)) {
+				*pp = cmdp->next;
+				ckfree(cmdp);
+			} else {
+				pp = &cmdp->next;
+			}
+		}
+	}
+	INTON;
+}
+
+
+/*
+ * Delete all functions.
+ */
+
+#ifdef mkinit
+MKINIT void deletefuncs(void);
+MKINIT void hash_special_builtins(void);
+
+INIT {
+	hash_special_builtins();
+}
+
+SHELLPROC {
+	deletefuncs();
+}
+#endif
+
+void
+deletefuncs(void)
+{
+	struct tblentry **tblp;
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	INTOFF;
+	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+		pp = tblp;
+		while ((cmdp = *pp) != NULL) {
+			if (cmdp->cmdtype == CMDFUNCTION) {
+				*pp = cmdp->next;
+				freefunc(cmdp->param.func);
+				ckfree(cmdp);
+			} else {
+				pp = &cmdp->next;
+			}
+		}
+	}
+	INTON;
+}
+
+
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ */
+
+struct tblentry **lastcmdentry;
+
+
+STATIC struct tblentry *
+cmdlookup(const char *name, int add)
+{
+	int hashval;
+	const char *p;
+	struct tblentry *cmdp;
+	struct tblentry **pp;
+
+	p = name;
+	hashval = *p << 4;
+	while (*p)
+		hashval += *p++;
+	hashval &= 0x7FFF;
+	pp = &cmdtable[hashval % CMDTABLESIZE];
+	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+		if (equal(cmdp->cmdname, name))
+			break;
+		pp = &cmdp->next;
+	}
+	if (add && cmdp == NULL) {
+		INTOFF;
+		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
+					+ strlen(name) + 1);
+		cmdp->next = NULL;
+		cmdp->cmdtype = CMDUNKNOWN;
+		cmdp->rehash = 0;
+		strcpy(cmdp->cmdname, name);
+		INTON;
+	}
+	lastcmdentry = pp;
+	return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+
+STATIC void
+delete_cmd_entry(void)
+{
+	struct tblentry *cmdp;
+
+	INTOFF;
+	cmdp = *lastcmdentry;
+	*lastcmdentry = cmdp->next;
+	ckfree(cmdp);
+	INTON;
+}
+
+
+
+#ifdef notdef
+void
+getcmdentry(char *name, struct cmdentry *entry)
+{
+	struct tblentry *cmdp = cmdlookup(name, 0);
+
+	if (cmdp) {
+		entry->u = cmdp->param;
+		entry->cmdtype = cmdp->cmdtype;
+	} else {
+		entry->cmdtype = CMDUNKNOWN;
+		entry->u.index = 0;
+	}
+}
+#endif
+
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
+
+STATIC void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+	struct tblentry *cmdp;
+
+	INTOFF;
+	cmdp = cmdlookup(name, 1);
+	if (cmdp->cmdtype != CMDSPLBLTIN) {
+		if (cmdp->cmdtype == CMDFUNCTION) {
+			freefunc(cmdp->param.func);
+		}
+		cmdp->cmdtype = entry->cmdtype;
+		cmdp->param = entry->u;
+	}
+	INTON;
+}
+
+
+/*
+ * Define a shell function.
+ */
+
+void
+defun(char *name, union node *func)
+{
+	struct cmdentry entry;
+
+	INTOFF;
+	entry.cmdtype = CMDFUNCTION;
+	entry.u.func = copyfunc(func);
+	addcmdentry(name, &entry);
+	INTON;
+}
+
+
+/*
+ * Delete a function if it exists.
+ */
+
+int
+unsetfunc(char *name)
+{
+	struct tblentry *cmdp;
+
+	if ((cmdp = cmdlookup(name, 0)) != NULL &&
+	    cmdp->cmdtype == CMDFUNCTION) {
+		freefunc(cmdp->param.func);
+		delete_cmd_entry();
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Locate and print what a word is...
+ * also used for 'command -[v|V]'
+ */
+
+int
+typecmd(int argc, char **argv)
+{
+	struct cmdentry entry;
+	struct tblentry *cmdp;
+	char * const *pp;
+	struct alias *ap;
+	int err = 0;
+	char *arg;
+	int c;
+	int V_flag = 0;
+	int v_flag = 0;
+	int p_flag = 0;
+
+	while ((c = nextopt("vVp")) != 0) {
+		switch (c) {
+		case 'v': v_flag = 1; break;
+		case 'V': V_flag = 1; break;
+		case 'p': p_flag = 1; break;
+		}
+	}
+
+	if (p_flag && (v_flag || V_flag))
+		error("cannot specify -p with -v or -V");
+
+	while ((arg = *argptr++)) {
+		if (!v_flag)
+			out1str(arg);
+		/* First look at the keywords */
+		for (pp = parsekwd; *pp; pp++)
+			if (**pp == *arg && equal(*pp, arg))
+				break;
+
+		if (*pp) {
+			if (v_flag)
+				err = 1;
+			else
+				out1str(" is a shell keyword\n");
+			continue;
+		}
+
+		/* Then look at the aliases */
+		if ((ap = lookupalias(arg, 1)) != NULL) {
+			if (!v_flag)
+				out1fmt(" is an alias for \n");
+			out1fmt("%s\n", ap->val);
+			continue;
+		}
+
+		/* Then check if it is a tracked alias */
+		if ((cmdp = cmdlookup(arg, 0)) != NULL) {
+			entry.cmdtype = cmdp->cmdtype;
+			entry.u = cmdp->param;
+		} else {
+			/* Finally use brute force */
+			find_command(arg, &entry, DO_ABS, pathval());
+		}
+
+		switch (entry.cmdtype) {
+		case CMDNORMAL: {
+			if (strchr(arg, '/') == NULL) {
+				const char *path = pathval();
+				char *name;
+				int j = entry.u.index;
+				do {
+					name = padvance(&path, arg);
+					stunalloc(name);
+				} while (--j >= 0);
+				if (!v_flag)
+					out1fmt(" is%s ",
+					    cmdp ? " a tracked alias for" : "");
+				out1fmt("%s\n", name);
+			} else {
+				if (access(arg, X_OK) == 0) {
+					if (!v_flag)
+						out1fmt(" is ");
+					out1fmt("%s\n", arg);
+				} else {
+					if (!v_flag)
+						out1fmt(": %s\n",
+						    strerror(errno));
+					else
+						err = 126;
+				}
+			}
+ 			break;
+		}
+		case CMDFUNCTION:
+			if (!v_flag)
+				out1str(" is a shell function\n");
+			else
+				out1fmt("%s\n", arg);
+			break;
+
+		case CMDBUILTIN:
+			if (!v_flag)
+				out1str(" is a shell builtin\n");
+			else
+				out1fmt("%s\n", arg);
+			break;
+
+		case CMDSPLBLTIN:
+			if (!v_flag)
+				out1str(" is a special shell builtin\n");
+			else
+				out1fmt("%s\n", arg);
+			break;
+
+		default:
+			if (!v_flag)
+				out1str(": not found\n");
+			err = 127;
+			break;
+		}
+	}
+	return err;
+}
diff --git a/sh/exec.h b/sh/exec.h
new file mode 100644
index 0000000..26fd09c
--- /dev/null
+++ b/sh/exec.h
@@ -0,0 +1,79 @@
+/*	$NetBSD: exec.h,v 1.21 2003/08/07 09:05:31 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)exec.h	8.3 (Berkeley) 6/8/95
+ */
+
+/* values of cmdtype */
+#define CMDUNKNOWN	-1	/* no entry in table for command */
+#define CMDNORMAL	0	/* command is an executable program */
+#define CMDFUNCTION	1	/* command is a shell function */
+#define CMDBUILTIN	2	/* command is a shell builtin */
+#define CMDSPLBLTIN	3	/* command is a special shell builtin */
+
+
+struct cmdentry {
+	int cmdtype;
+	union param {
+		int index;
+		int (*bltin)(int, char**);
+		union node *func;
+	} u;
+};
+
+
+/* action to find_command() */
+#define DO_ERR		0x01	/* prints errors */
+#define DO_ABS		0x02	/* checks absolute paths */
+#define DO_NOFUNC	0x04	/* don't return shell functions, for command */
+#define DO_ALTPATH	0x08	/* using alternate path */
+#define DO_ALTBLTIN	0x20	/* %builtin in alt. path */
+
+extern const char *pathopt;	/* set by padvance */
+
+void shellexec(char **, char **, const char *, int, int)
+    __attribute__((__noreturn__));
+char *padvance(const char **, const char *);
+int hashcmd(int, char **);
+void find_command(char *, struct cmdentry *, int, const char *);
+int (*find_builtin(char *))(int, char **);
+int (*find_splbltin(char *))(int, char **);
+void hashcd(void);
+void changepath(const char *);
+void deletefuncs(void);
+void getcmdentry(char *, struct cmdentry *);
+void addcmdentry(char *, struct cmdentry *);
+void defun(char *, union node *);
+int unsetfunc(char *);
+int typecmd(int, char **);
+void hash_special_builtins(void);
diff --git a/sh/expand.c b/sh/expand.c
new file mode 100644
index 0000000..d3462fc
--- /dev/null
+++ b/sh/expand.c
@@ -0,0 +1,1559 @@
+/*	$NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
+#else
+__RCSID("$NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Routines to expand arguments to commands.  We have to deal with
+ * backquotes, shell variables, and file metacharacters.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "eval.h"
+#include "expand.h"
+#include "syntax.h"
+#include "parser.h"
+#include "jobs.h"
+#include "options.h"
+#include "var.h"
+#include "input.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "show.h"
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+
+struct ifsregion {
+	struct ifsregion *next;	/* next region in list */
+	int begoff;		/* offset of start of region */
+	int endoff;		/* offset of end of region */
+	int inquotes;		/* search for nul bytes only */
+};
+
+
+char *expdest;			/* output of current string */
+struct nodelist *argbackq;	/* list of back quote expressions */
+struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
+struct ifsregion *ifslastp;	/* last struct in list */
+struct arglist exparg;		/* holds expanded arg list */
+
+STATIC void argstr(char *, int);
+STATIC char *exptilde(char *, int);
+STATIC void expbackq(union node *, int, int);
+STATIC int subevalvar(char *, char *, int, int, int, int);
+STATIC char *evalvar(char *, int);
+STATIC int varisset(char *, int);
+STATIC void varvalue(char *, int, int, int);
+STATIC void recordregion(int, int, int);
+STATIC void removerecordregions(int); 
+STATIC void ifsbreakup(char *, struct arglist *);
+STATIC void ifsfree(void);
+STATIC void expandmeta(struct strlist *, int);
+STATIC void expmeta(char *, char *);
+STATIC void addfname(char *);
+STATIC struct strlist *expsort(struct strlist *);
+STATIC struct strlist *msort(struct strlist *, int);
+STATIC int pmatch(char *, char *, int);
+STATIC char *cvtnum(int, char *);
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+
+void
+expandhere(union node *arg, int fd)
+{
+	herefd = fd;
+	expandarg(arg, (struct arglist *)NULL, 0);
+	xwrite(fd, stackblock(), expdest - stackblock());
+}
+
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+
+void
+expandarg(union node *arg, struct arglist *arglist, int flag)
+{
+	struct strlist *sp;
+	char *p;
+
+	argbackq = arg->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifsfirst.next = NULL;
+	ifslastp = NULL;
+	argstr(arg->narg.text, flag);
+	if (arglist == NULL) {
+		return;			/* here document expanded */
+	}
+	STPUTC('\0', expdest);
+	p = grabstackstr(expdest);
+	exparg.lastp = &exparg.list;
+	/*
+	 * TODO - EXP_REDIR
+	 */
+	if (flag & EXP_FULL) {
+		ifsbreakup(p, &exparg);
+		*exparg.lastp = NULL;
+		exparg.lastp = &exparg.list;
+		expandmeta(exparg.list, flag);
+	} else {
+		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+			rmescapes(p);
+		sp = (struct strlist *)stalloc(sizeof (struct strlist));
+		sp->text = p;
+		*exparg.lastp = sp;
+		exparg.lastp = &sp->next;
+	}
+	ifsfree();
+	*exparg.lastp = NULL;
+	if (exparg.list) {
+		*arglist->lastp = exparg.list;
+		arglist->lastp = exparg.lastp;
+	}
+}
+
+
+
+/*
+ * Perform variable and command substitution.
+ * If EXP_FULL is set, output CTLESC characters to allow for further processing.
+ * Otherwise treat $@ like $* since no splitting will be performed.
+ */
+
+STATIC void
+argstr(char *p, int flag)
+{
+	char c;
+	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
+	int firsteq = 1;
+	const char *ifs = 0;
+	int ifs_split = EXP_IFS_SPLIT;
+
+	if (flag & EXP_IFS_SPLIT)
+		ifs = ifsset() ? ifsval() : " \t\n";
+
+	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
+		p = exptilde(p, flag);
+	for (;;) {
+		switch (c = *p++) {
+		case '\0':
+		case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
+			return;
+		case CTLQUOTEMARK:
+			/* "$@" syntax adherence hack */
+			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
+				break;
+			if ((flag & EXP_FULL) != 0)
+				STPUTC(c, expdest);
+			ifs_split = 0;
+			break;
+		case CTLQUOTEEND:
+			ifs_split = EXP_IFS_SPLIT;
+			break;
+		case CTLESC:
+			if (quotes)
+				STPUTC(c, expdest);
+			c = *p++;
+			STPUTC(c, expdest);
+			break;
+		case CTLVAR:
+			p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
+			break;
+		case CTLBACKQ:
+		case CTLBACKQ|CTLQUOTE:
+			expbackq(argbackq->n, c & CTLQUOTE, flag);
+			argbackq = argbackq->next;
+			break;
+		case CTLENDARI:
+			expari(flag);
+			break;
+		case ':':
+		case '=':
+			/*
+			 * sort of a hack - expand tildes in variable
+			 * assignments (after the first '=' and after ':'s).
+			 */
+			STPUTC(c, expdest);
+			if (flag & EXP_VARTILDE && *p == '~') {
+				if (c == '=') {
+					if (firsteq)
+						firsteq = 0;
+					else
+						break;
+				}
+				p = exptilde(p, flag);
+			}
+			break;
+		default:
+			STPUTC(c, expdest);
+			if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) {
+				/* We need to get the output split here... */
+				recordregion(expdest - stackblock() - 1,
+						expdest - stackblock(), 0);
+			}
+			break;
+		}
+	}
+}
+
+STATIC char *
+exptilde(char *p, int flag)
+{
+	char c, *startp = p;
+	const char *home;
+	int quotes = flag & (EXP_FULL | EXP_CASE);
+
+	while ((c = *p) != '\0') {
+		switch(c) {
+		case CTLESC:
+			return (startp);
+		case CTLQUOTEMARK:
+			return (startp);
+		case ':':
+			if (flag & EXP_VARTILDE)
+				goto done;
+			break;
+		case '/':
+			goto done;
+		}
+		p++;
+	}
+done:
+	*p = '\0';
+	if (*(startp+1) == '\0') {
+		if ((home = lookupvar("HOME")) == NULL)
+			goto lose;
+	} else
+        	goto lose;
+	if (*home == '\0')
+		goto lose;
+	*p = c;
+	while ((c = *home++) != '\0') {
+		if (quotes && SQSYNTAX[(int)c] == CCTL)
+			STPUTC(CTLESC, expdest);
+		STPUTC(c, expdest);
+	}
+	return (p);
+lose:
+	*p = c;
+	return (startp);
+}
+
+
+STATIC void 
+removerecordregions(int endoff)
+{
+	if (ifslastp == NULL)
+		return;
+
+	if (ifsfirst.endoff > endoff) {
+		while (ifsfirst.next != NULL) {
+			struct ifsregion *ifsp;
+			INTOFF;
+			ifsp = ifsfirst.next->next;
+			ckfree(ifsfirst.next);
+			ifsfirst.next = ifsp;
+			INTON;
+		}
+		if (ifsfirst.begoff > endoff)
+			ifslastp = NULL;
+		else {
+			ifslastp = &ifsfirst;
+			ifsfirst.endoff = endoff;
+		}
+		return;
+	}
+	
+	ifslastp = &ifsfirst;
+	while (ifslastp->next && ifslastp->next->begoff < endoff)
+		ifslastp=ifslastp->next;
+	while (ifslastp->next != NULL) {
+		struct ifsregion *ifsp;
+		INTOFF;
+		ifsp = ifslastp->next->next;
+		ckfree(ifslastp->next);
+		ifslastp->next = ifsp;
+		INTON;
+	}
+	if (ifslastp->endoff > endoff)
+		ifslastp->endoff = endoff;
+}
+
+
+/*
+ * Expand arithmetic expression.  Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+void
+expari(int flag)
+{
+	char *p, *start;
+	int result;
+	int begoff;
+	int quotes = flag & (EXP_FULL | EXP_CASE);
+	int quoted;
+
+	/*	ifsfree(); */
+
+	/*
+	 * This routine is slightly over-complicated for
+	 * efficiency.  First we make sure there is
+	 * enough space for the result, which may be bigger
+	 * than the expression if we add exponentation.  Next we
+	 * scan backwards looking for the start of arithmetic.  If the
+	 * next previous character is a CTLESC character, then we
+	 * have to rescan starting from the beginning since CTLESC
+	 * characters have to be processed left to right.
+	 */
+#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
+#error "integers with more than 10 digits are not supported"
+#endif
+	CHECKSTRSPACE(12 - 2, expdest);
+	USTPUTC('\0', expdest);
+	start = stackblock();
+	p = expdest - 1;
+	while (*p != CTLARI && p >= start)
+		--p;
+	if (*p != CTLARI)
+		error("missing CTLARI (shouldn't happen)");
+	if (p > start && *(p-1) == CTLESC)
+		for (p = start; *p != CTLARI; p++)
+			if (*p == CTLESC)
+				p++;
+
+	if (p[1] == '"')
+		quoted=1;
+	else
+		quoted=0;
+	begoff = p - start;
+	removerecordregions(begoff);
+	if (quotes)
+		rmescapes(p+2);
+	result = arith(p+2);
+	fmtstr(p, 12, "%d", result);
+
+	while (*p++)
+		;
+
+	if (quoted == 0)
+		recordregion(begoff, p - 1 - start, 0);
+	result = expdest - p + 1;
+	STADJUST(-result, expdest);
+}
+
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+
+STATIC void
+expbackq(union node *cmd, int quoted, int flag)
+{
+	struct backcmd in;
+	int i;
+	char buf[128];
+	char *p;
+	char *dest = expdest;
+	struct ifsregion saveifs, *savelastp;
+	struct nodelist *saveargbackq;
+	char lastc;
+	int startloc = dest - stackblock();
+	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+	int saveherefd;
+	int quotes = flag & (EXP_FULL | EXP_CASE);
+
+	INTOFF;
+	saveifs = ifsfirst;
+	savelastp = ifslastp;
+	saveargbackq = argbackq;
+	saveherefd = herefd;
+	herefd = -1;
+	p = grabstackstr(dest);
+	evalbackcmd(cmd, &in);
+	ungrabstackstr(p, dest);
+	ifsfirst = saveifs;
+	ifslastp = savelastp;
+	argbackq = saveargbackq;
+	herefd = saveherefd;
+
+	p = in.buf;
+	lastc = '\0';
+	for (;;) {
+		if (--in.nleft < 0) {
+			if (in.fd < 0)
+				break;
+			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+			TRACE(("expbackq: read returns %d\n", i));
+			if (i <= 0)
+				break;
+			p = buf;
+			in.nleft = i - 1;
+		}
+		lastc = *p++;
+		if (lastc != '\0') {
+			if (quotes && syntax[(int)lastc] == CCTL)
+				STPUTC(CTLESC, dest);
+			STPUTC(lastc, dest);
+		}
+	}
+
+	/* Eat all trailing newlines */
+	p = stackblock() + startloc;
+	while (dest > p && dest[-1] == '\n')
+		STUNPUTC(dest);
+
+	if (in.fd >= 0)
+		close(in.fd);
+	if (in.buf)
+		ckfree(in.buf);
+	if (in.jp)
+		back_exitstatus = waitforjob(in.jp);
+	if (quoted == 0)
+		recordregion(startloc, dest - stackblock(), 0);
+	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+		(dest - stackblock()) - startloc,
+		(dest - stackblock()) - startloc,
+		stackblock() + startloc));
+	expdest = dest;
+	INTON;
+}
+
+
+
+STATIC int
+subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags)
+{
+	char *startp;
+	char *loc = NULL;
+	char *q;
+	int c = 0;
+	int saveherefd = herefd;
+	struct nodelist *saveargbackq = argbackq;
+	int amount;
+
+	herefd = -1;
+	argstr(p, 0);
+	STACKSTRNUL(expdest);
+	herefd = saveherefd;
+	argbackq = saveargbackq;
+	startp = stackblock() + startloc;
+	if (str == NULL)
+	    str = stackblock() + strloc;
+
+	switch (subtype) {
+	case VSASSIGN:
+		setvar(str, startp, 0);
+		amount = startp - expdest;
+		STADJUST(amount, expdest);
+		varflags &= ~VSNUL;
+		if (c != 0)
+			*loc = c;
+		return 1;
+
+	case VSQUESTION:
+		if (*p != CTLENDVAR) {
+			outfmt(&errout, "%s\n", startp);
+			error((char *)NULL);
+		}
+		error("%.*s: parameter %snot set", p - str - 1,
+		      str, (varflags & VSNUL) ? "null or "
+					      : nullstr);
+		/* NOTREACHED */
+
+	case VSTRIMLEFT:
+		for (loc = startp; loc < str; loc++) {
+			c = *loc;
+			*loc = '\0';
+			if (patmatch(str, startp, varflags & VSQUOTE))
+				goto recordleft;
+			*loc = c;
+			if ((varflags & VSQUOTE) && *loc == CTLESC)
+			        loc++;
+		}
+		return 0;
+
+	case VSTRIMLEFTMAX:
+		for (loc = str - 1; loc >= startp;) {
+			c = *loc;
+			*loc = '\0';
+			if (patmatch(str, startp, varflags & VSQUOTE))
+				goto recordleft;
+			*loc = c;
+			loc--;
+			if ((varflags & VSQUOTE) && loc > startp &&
+			    *(loc - 1) == CTLESC) {
+				for (q = startp; q < loc; q++)
+					if (*q == CTLESC)
+						q++;
+				if (q > loc)
+					loc--;
+			}
+		}
+		return 0;
+
+	case VSTRIMRIGHT:
+	        for (loc = str - 1; loc >= startp;) {
+			if (patmatch(str, loc, varflags & VSQUOTE))
+				goto recordright;
+			loc--;
+			if ((varflags & VSQUOTE) && loc > startp &&
+			    *(loc - 1) == CTLESC) { 
+				for (q = startp; q < loc; q++)
+					if (*q == CTLESC)
+						q++;
+				if (q > loc)
+					loc--;
+			}
+		}
+		return 0;
+
+	case VSTRIMRIGHTMAX:
+		for (loc = startp; loc < str - 1; loc++) {
+			if (patmatch(str, loc, varflags & VSQUOTE))
+				goto recordright;
+			if ((varflags & VSQUOTE) && *loc == CTLESC)
+			        loc++;
+		}
+		return 0;
+
+	default:
+		abort();
+	}
+
+recordleft:
+	*loc = c;
+	amount = ((str - 1) - (loc - startp)) - expdest;
+	STADJUST(amount, expdest);
+	while (loc != str - 1)
+		*startp++ = *loc++;
+	return 1;
+
+recordright:
+	amount = loc - expdest;
+	STADJUST(amount, expdest);
+	STPUTC('\0', expdest);
+	STADJUST(-1, expdest);
+	return 1;
+}
+
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+
+STATIC char *
+evalvar(char *p, int flag)
+{
+	int subtype;
+	int varflags;
+	char *var;
+	char *val;
+	int patloc;
+	int c;
+	int set;
+	int special;
+	int startloc;
+	int varlen;
+	int apply_ifs;
+	int quotes = flag & (EXP_FULL | EXP_CASE);
+
+	varflags = (unsigned char)*p++;
+	subtype = varflags & VSTYPE;
+	var = p;
+	special = !is_name(*p);
+	p = strchr(p, '=') + 1;
+
+again: /* jump here after setting a variable with ${var=text} */
+	if (special) {
+		set = varisset(var, varflags & VSNUL);
+		val = NULL;
+	} else {
+		val = lookupvar(var);
+		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+			val = NULL;
+			set = 0;
+		} else
+			set = 1;
+	}
+
+	varlen = 0;
+	startloc = expdest - stackblock();
+
+	if (!set && uflag) {
+		switch (subtype) {
+		case VSNORMAL:
+		case VSTRIMLEFT:
+		case VSTRIMLEFTMAX:
+		case VSTRIMRIGHT:
+		case VSTRIMRIGHTMAX:
+		case VSLENGTH:
+			error("%.*s: parameter not set", p - var - 1, var);
+			/* NOTREACHED */
+		}
+	}
+
+	if (set && subtype != VSPLUS) {
+		/* insert the value of the variable */
+		if (special) {
+			varvalue(var, varflags & VSQUOTE, subtype, flag);
+			if (subtype == VSLENGTH) {
+				varlen = expdest - stackblock() - startloc;
+				STADJUST(-varlen, expdest);
+			}
+		} else {
+			char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
+								  : BASESYNTAX;
+
+			if (subtype == VSLENGTH) {
+				for (;*val; val++)
+					varlen++;
+			} else {
+				while (*val) {
+					if (quotes && syntax[(int)*val] == CCTL)
+						STPUTC(CTLESC, expdest);
+					STPUTC(*val++, expdest);
+				}
+
+			}
+		}
+	}
+
+
+	apply_ifs = ((varflags & VSQUOTE) == 0 ||
+		(*var == '@' && shellparam.nparam != 1));
+
+	switch (subtype) {
+	case VSLENGTH:
+		expdest = cvtnum(varlen, expdest);
+		break;
+
+	case VSNORMAL:
+		break;
+
+	case VSPLUS:
+		set = !set;
+		/* FALLTHROUGH */
+	case VSMINUS:
+		if (!set) {
+		        argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
+			/*
+			 * ${x-a b c} doesn't get split, but removing the
+			 * 'apply_ifs = 0' apparantly breaks ${1+"$@"}..
+			 * ${x-'a b' c} should generate 2 args.
+			 */
+			/* We should have marked stuff already */
+			apply_ifs = 0;
+		}
+		break;
+
+	case VSTRIMLEFT:
+	case VSTRIMLEFTMAX:
+	case VSTRIMRIGHT:
+	case VSTRIMRIGHTMAX:
+		if (!set)
+			break;
+		/*
+		 * Terminate the string and start recording the pattern
+		 * right after it
+		 */
+		STPUTC('\0', expdest);
+		patloc = expdest - stackblock();
+		if (subevalvar(p, NULL, patloc, subtype,
+			       startloc, varflags) == 0) {
+			int amount = (expdest - stackblock() - patloc) + 1;
+			STADJUST(-amount, expdest);
+		}
+		/* Remove any recorded regions beyond start of variable */
+		removerecordregions(startloc);
+		apply_ifs = 1;
+		break;
+
+	case VSASSIGN:
+	case VSQUESTION:
+		if (set)
+			break;
+		if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
+			varflags &= ~VSNUL;
+			/* 
+			 * Remove any recorded regions beyond 
+			 * start of variable 
+			 */
+			removerecordregions(startloc);
+			goto again;
+		}
+		apply_ifs = 0;
+		break;
+
+	default:
+		abort();
+	}
+
+	if (apply_ifs)
+		recordregion(startloc, expdest - stackblock(),
+			     varflags & VSQUOTE);
+
+	if (subtype != VSNORMAL) {	/* skip to end of alternative */
+		int nesting = 1;
+		for (;;) {
+			if ((c = *p++) == CTLESC)
+				p++;
+			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+				if (set)
+					argbackq = argbackq->next;
+			} else if (c == CTLVAR) {
+				if ((*p++ & VSTYPE) != VSNORMAL)
+					nesting++;
+			} else if (c == CTLENDVAR) {
+				if (--nesting == 0)
+					break;
+			}
+		}
+	}
+	return p;
+}
+
+
+
+/*
+ * Test whether a specialized variable is set.
+ */
+
+STATIC int
+varisset(char *name, int nulok)
+{
+	if (*name == '!')
+		return backgndpid != -1;
+	else if (*name == '@' || *name == '*') {
+		if (*shellparam.p == NULL)
+			return 0;
+
+		if (nulok) {
+			char **av;
+
+			for (av = shellparam.p; *av; av++)
+				if (**av != '\0')
+					return 1;
+			return 0;
+		}
+	} else if (is_digit(*name)) {
+		char *ap;
+		int num = atoi(name);
+
+		if (num > shellparam.nparam)
+			return 0;
+
+		if (num == 0)
+			ap = arg0;
+		else
+			ap = shellparam.p[num - 1];
+
+		if (nulok && (ap == NULL || *ap == '\0'))
+			return 0;
+	}
+	return 1;
+}
+
+
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+
+STATIC void
+varvalue(char *name, int quoted, int subtype, int flag)
+{
+	int num;
+	char *p;
+	int i;
+	char sep;
+	char **ap;
+	char const *syntax;
+
+#define STRTODEST(p) \
+	do {\
+	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
+		syntax = quoted? DQSYNTAX : BASESYNTAX; \
+		while (*p) { \
+			if (syntax[(int)*p] == CCTL) \
+				STPUTC(CTLESC, expdest); \
+			STPUTC(*p++, expdest); \
+		} \
+	} else \
+		while (*p) \
+			STPUTC(*p++, expdest); \
+	} while (0)
+
+
+	switch (*name) {
+	case '$':
+		num = rootpid;
+		goto numvar;
+	case '?':
+		num = exitstatus;
+		goto numvar;
+	case '#':
+		num = shellparam.nparam;
+		goto numvar;
+	case '!':
+		num = backgndpid;
+numvar:
+		expdest = cvtnum(num, expdest);
+		break;
+	case '-':
+		for (i = 0; optlist[i].name; i++) {
+			if (optlist[i].val)
+				STPUTC(optlist[i].letter, expdest);
+		}
+		break;
+	case '@':
+		if (flag & EXP_FULL && quoted) {
+			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+				STRTODEST(p);
+				if (*ap)
+					STPUTC('\0', expdest);
+			}
+			break;
+		}
+		/* fall through */
+	case '*':
+		if (ifsset() != 0)
+			sep = ifsval()[0];
+		else
+			sep = ' ';
+		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+			STRTODEST(p);
+			if (*ap && sep)
+				STPUTC(sep, expdest);
+		}
+		break;
+	case '0':
+		p = arg0;
+		STRTODEST(p);
+		break;
+	default:
+		if (is_digit(*name)) {
+			num = atoi(name);
+			if (num > 0 && num <= shellparam.nparam) {
+				p = shellparam.p[num - 1];
+				STRTODEST(p);
+			}
+		}
+		break;
+	}
+}
+
+
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+
+STATIC void
+recordregion(int start, int end, int inquotes)
+{
+	struct ifsregion *ifsp;
+
+	if (ifslastp == NULL) {
+		ifsp = &ifsfirst;
+	} else {
+		if (ifslastp->endoff == start
+		    && ifslastp->inquotes == inquotes) {
+			/* extend previous area */
+			ifslastp->endoff = end;
+			return;
+		}
+		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
+		ifslastp->next = ifsp;
+	}
+	ifslastp = ifsp;
+	ifslastp->next = NULL;
+	ifslastp->begoff = start;
+	ifslastp->endoff = end;
+	ifslastp->inquotes = inquotes;
+}
+
+
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list.  The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+STATIC void
+ifsbreakup(char *string, struct arglist *arglist)
+{
+	struct ifsregion *ifsp;
+	struct strlist *sp;
+	char *start;
+	char *p;
+	char *q;
+	const char *ifs;
+	const char *ifsspc;
+	int inquotes;
+
+	start = string;
+	ifsspc = NULL;
+	inquotes = 0;
+
+	if (ifslastp == NULL) {
+		/* Return entire argument, IFS doesn't apply to any of it */
+		sp = (struct strlist *)stalloc(sizeof *sp);
+		sp->text = start;
+		*arglist->lastp = sp;
+		arglist->lastp = &sp->next;
+		return;
+	}
+
+	ifs = ifsset() ? ifsval() : " \t\n";
+
+	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
+		p = string + ifsp->begoff;
+		inquotes = ifsp->inquotes;
+		ifsspc = NULL;
+		while (p < string + ifsp->endoff) {
+			q = p;
+			if (*p == CTLESC)
+				p++;
+			if (inquotes) {
+				/* Only NULs (probably from "$@") end args */
+				if (*p != 0) {
+					p++;
+					continue;
+				}
+			} else {
+				if (!strchr(ifs, *p)) {
+					p++;
+					continue;
+				}
+				ifsspc = strchr(" \t\n", *p);
+
+				/* Ignore IFS whitespace at start */
+				if (q == start && ifsspc != NULL) {
+					p++;
+					start = p;
+					continue;
+				}
+			}
+
+			/* Save this argument... */
+			*q = '\0';
+			sp = (struct strlist *)stalloc(sizeof *sp);
+			sp->text = start;
+			*arglist->lastp = sp;
+			arglist->lastp = &sp->next;
+			p++;
+
+			if (ifsspc != NULL) {
+				/* Ignore further trailing IFS whitespace */
+				for (; p < string + ifsp->endoff; p++) {
+					q = p;
+					if (*p == CTLESC)
+						p++;
+					if (strchr(ifs, *p) == NULL) {
+						p = q;
+						break;
+					}
+					if (strchr(" \t\n", *p) == NULL) {
+						p++;
+						break;
+					}
+				}
+			}
+			start = p;
+		}
+	}
+
+	/*
+	 * Save anything left as an argument.
+	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
+	 * generating 2 arguments, the second of which is empty.
+	 * Some recent clarification of the Posix spec say that it
+	 * should only generate one....
+	 */
+	if (*start /* || (!ifsspc && start > string) */) {
+		sp = (struct strlist *)stalloc(sizeof *sp);
+		sp->text = start;
+		*arglist->lastp = sp;
+		arglist->lastp = &sp->next;
+	}
+}
+
+STATIC void
+ifsfree(void)
+{
+	while (ifsfirst.next != NULL) {
+		struct ifsregion *ifsp;
+		INTOFF;
+		ifsp = ifsfirst.next->next;
+		ckfree(ifsfirst.next);
+		ifsfirst.next = ifsp;
+		INTON;
+	}
+	ifslastp = NULL;
+	ifsfirst.next = NULL;
+}
+
+
+
+/*
+ * Expand shell metacharacters.  At this point, the only control characters
+ * should be escapes.  The results are stored in the list exparg.
+ */
+
+char *expdir;
+
+
+STATIC void
+expandmeta(struct strlist *str, int flag)
+{
+	char *p;
+	struct strlist **savelastp;
+	struct strlist *sp;
+	char c;
+	/* TODO - EXP_REDIR */
+
+	while (str) {
+		if (fflag)
+			goto nometa;
+		p = str->text;
+		for (;;) {			/* fast check for meta chars */
+			if ((c = *p++) == '\0')
+				goto nometa;
+			if (c == '*' || c == '?' || c == '[' || c == '!')
+				break;
+		}
+		savelastp = exparg.lastp;
+		INTOFF;
+		if (expdir == NULL) {
+			int i = strlen(str->text);
+			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+		}
+
+		expmeta(expdir, str->text);
+		ckfree(expdir);
+		expdir = NULL;
+		INTON;
+		if (exparg.lastp == savelastp) {
+			/*
+			 * no matches
+			 */
+nometa:
+			*exparg.lastp = str;
+			rmescapes(str->text);
+			exparg.lastp = &str->next;
+		} else {
+			*exparg.lastp = NULL;
+			*savelastp = sp = expsort(*savelastp);
+			while (sp->next != NULL)
+				sp = sp->next;
+			exparg.lastp = &sp->next;
+		}
+		str = str->next;
+	}
+}
+
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+
+STATIC void
+expmeta(char *enddir, char *name)
+{
+	char *p;
+	const char *cp;
+	char *q;
+	char *start;
+	char *endname;
+	int metaflag;
+	struct stat statb;
+	DIR *dirp;
+	struct dirent *dp;
+	int atend;
+	int matchdot;
+
+	metaflag = 0;
+	start = name;
+	for (p = name ; ; p++) {
+		if (*p == '*' || *p == '?')
+			metaflag = 1;
+		else if (*p == '[') {
+			q = p + 1;
+			if (*q == '!')
+				q++;
+			for (;;) {
+				while (*q == CTLQUOTEMARK)
+					q++;
+				if (*q == CTLESC)
+					q++;
+				if (*q == '/' || *q == '\0')
+					break;
+				if (*++q == ']') {
+					metaflag = 1;
+					break;
+				}
+			}
+		} else if (*p == '!' && p[1] == '!'	&& (p == name || p[-1] == '/')) {
+			metaflag = 1;
+		} else if (*p == '\0')
+			break;
+		else if (*p == CTLQUOTEMARK)
+			continue;
+		else if (*p == CTLESC)
+			p++;
+		if (*p == '/') {
+			if (metaflag)
+				break;
+			start = p + 1;
+		}
+	}
+	if (metaflag == 0) {	/* we've reached the end of the file name */
+		if (enddir != expdir)
+			metaflag++;
+		for (p = name ; ; p++) {
+			if (*p == CTLQUOTEMARK)
+				continue;
+			if (*p == CTLESC)
+				p++;
+			*enddir++ = *p;
+			if (*p == '\0')
+				break;
+		}
+		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+			addfname(expdir);
+		return;
+	}
+	endname = p;
+	if (start != name) {
+		p = name;
+		while (p < start) {
+			while (*p == CTLQUOTEMARK)
+				p++;
+			if (*p == CTLESC)
+				p++;
+			*enddir++ = *p++;
+		}
+	}
+	if (enddir == expdir) {
+		cp = ".";
+	} else if (enddir == expdir + 1 && *expdir == '/') {
+		cp = "/";
+	} else {
+		cp = expdir;
+		enddir[-1] = '\0';
+	}
+	if ((dirp = opendir(cp)) == NULL)
+		return;
+	if (enddir != expdir)
+		enddir[-1] = '/';
+	if (*endname == 0) {
+		atend = 1;
+	} else {
+		atend = 0;
+		*endname++ = '\0';
+	}
+	matchdot = 0;
+	p = start;
+	while (*p == CTLQUOTEMARK)
+		p++;
+	if (*p == CTLESC)
+		p++;
+	if (*p == '.')
+		matchdot++;
+	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
+		if (dp->d_name[0] == '.' && ! matchdot)
+			continue;
+		if (patmatch(start, dp->d_name, 0)) {
+			if (atend) {
+				scopy(dp->d_name, enddir);
+				addfname(expdir);
+			} else {
+				for (p = enddir, cp = dp->d_name;
+				     (*p++ = *cp++) != '\0';)
+					continue;
+				p[-1] = '/';
+				expmeta(p, endname);
+			}
+		}
+	}
+	closedir(dirp);
+	if (! atend)
+		endname[-1] = '/';
+}
+
+
+/*
+ * Add a file name to the list.
+ */
+
+STATIC void
+addfname(char *name)
+{
+	char *p;
+	struct strlist *sp;
+
+	p = stalloc(strlen(name) + 1);
+	scopy(name, p);
+	sp = (struct strlist *)stalloc(sizeof *sp);
+	sp->text = p;
+	*exparg.lastp = sp;
+	exparg.lastp = &sp->next;
+}
+
+
+/*
+ * Sort the results of file name expansion.  It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+
+STATIC struct strlist *
+expsort(struct strlist *str)
+{
+	int len;
+	struct strlist *sp;
+
+	len = 0;
+	for (sp = str ; sp ; sp = sp->next)
+		len++;
+	return msort(str, len);
+}
+
+
+STATIC struct strlist *
+msort(struct strlist *list, int len)
+{
+	struct strlist *p, *q = NULL;
+	struct strlist **lpp;
+	int half;
+	int n;
+
+	if (len <= 1)
+		return list;
+	half = len >> 1;
+	p = list;
+	for (n = half ; --n >= 0 ; ) {
+		q = p;
+		p = p->next;
+	}
+	q->next = NULL;			/* terminate first half of list */
+	q = msort(list, half);		/* sort first half of list */
+	p = msort(p, len - half);		/* sort second half */
+	lpp = &list;
+	for (;;) {
+		if (strcmp(p->text, q->text) < 0) {
+			*lpp = p;
+			lpp = &p->next;
+			if ((p = *lpp) == NULL) {
+				*lpp = q;
+				break;
+			}
+		} else {
+			*lpp = q;
+			lpp = &q->next;
+			if ((q = *lpp) == NULL) {
+				*lpp = p;
+				break;
+			}
+		}
+	}
+	return list;
+}
+
+
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+
+int
+patmatch(char *pattern, char *string, int squoted)
+{
+#ifdef notdef
+	if (pattern[0] == '!' && pattern[1] == '!')
+		return 1 - pmatch(pattern + 2, string);
+	else
+#endif
+		return pmatch(pattern, string, squoted);
+}
+
+
+STATIC int
+pmatch(char *pattern, char *string, int squoted)
+{
+	char *p, *q;
+	char c;
+
+	p = pattern;
+	q = string;
+	for (;;) {
+		switch (c = *p++) {
+		case '\0':
+			goto breakloop;
+		case CTLESC:
+			if (squoted && *q == CTLESC)
+				q++;
+			if (*q++ != *p++)
+				return 0;
+			break;
+		case CTLQUOTEMARK:
+			continue;
+		case '?':
+			if (squoted && *q == CTLESC)
+				q++;
+			if (*q++ == '\0')
+				return 0;
+			break;
+		case '*':
+			c = *p;
+			while (c == CTLQUOTEMARK || c == '*')
+				c = *++p;
+			if (c != CTLESC &&  c != CTLQUOTEMARK &&
+			    c != '?' && c != '*' && c != '[') {
+				while (*q != c) {
+					if (squoted && *q == CTLESC &&
+					    q[1] == c)
+						break;
+					if (*q == '\0')
+						return 0;
+					if (squoted && *q == CTLESC)
+						q++;
+					q++;
+				}
+			}
+			do {
+				if (pmatch(p, q, squoted))
+					return 1;
+				if (squoted && *q == CTLESC)
+					q++;
+			} while (*q++ != '\0');
+			return 0;
+		case '[': {
+			char *endp;
+			int invert, found;
+			char chr;
+
+			endp = p;
+			if (*endp == '!')
+				endp++;
+			for (;;) {
+				while (*endp == CTLQUOTEMARK)
+					endp++;
+				if (*endp == '\0')
+					goto dft;		/* no matching ] */
+				if (*endp == CTLESC)
+					endp++;
+				if (*++endp == ']')
+					break;
+			}
+			invert = 0;
+			if (*p == '!') {
+				invert++;
+				p++;
+			}
+			found = 0;
+			chr = *q++;
+			if (squoted && chr == CTLESC)
+				chr = *q++;
+			if (chr == '\0')
+				return 0;
+			c = *p++;
+			do {
+				if (c == CTLQUOTEMARK)
+					continue;
+				if (c == CTLESC)
+					c = *p++;
+				if (*p == '-' && p[1] != ']') {
+					p++;
+					while (*p == CTLQUOTEMARK)
+						p++;
+					if (*p == CTLESC)
+						p++;
+					if (chr >= c && chr <= *p)
+						found = 1;
+					p++;
+				} else {
+					if (chr == c)
+						found = 1;
+				}
+			} while ((c = *p++) != ']');
+			if (found == invert)
+				return 0;
+			break;
+		}
+dft:	        default:
+			if (squoted && *q == CTLESC)
+				q++;
+			if (*q++ != c)
+				return 0;
+			break;
+		}
+	}
+breakloop:
+	if (*q != '\0')
+		return 0;
+	return 1;
+}
+
+
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+
+void
+rmescapes(char *str)
+{
+	char *p, *q;
+
+	p = str;
+	while (*p != CTLESC && *p != CTLQUOTEMARK) {
+		if (*p++ == '\0')
+			return;
+	}
+	q = p;
+	while (*p) {
+		if (*p == CTLQUOTEMARK) {
+			p++;
+			continue;
+		}
+		if (*p == CTLESC)
+			p++;
+		*q++ = *p++;
+	}
+	*q = '\0';
+}
+
+
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+
+int
+casematch(union node *pattern, char *val)
+{
+	struct stackmark smark;
+	int result;
+	char *p;
+
+	setstackmark(&smark);
+	argbackq = pattern->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifslastp = NULL;
+	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+	STPUTC('\0', expdest);
+	p = grabstackstr(expdest);
+	result = patmatch(p, val, 0);
+	popstackmark(&smark);
+	return result;
+}
+
+/*
+ * Our own itoa().
+ */
+
+STATIC char *
+cvtnum(int num, char *buf)
+{
+	char temp[32];
+	int neg = num < 0;
+	char *p = temp + 31;
+
+	temp[31] = '\0';
+
+	do {
+		*--p = num % 10 + '0';
+	} while ((num /= 10) != 0);
+
+	if (neg)
+		*--p = '-';
+
+	while (*p)
+		STPUTC(*p++, buf);
+	return buf;
+}
+
+/*
+ * Do most of the work for wordexp(3).
+ */
+
+int
+wordexpcmd(int argc, char **argv)
+{
+	size_t len;
+	int i;
+
+	out1fmt("%d", argc - 1);
+	out1c('\0');
+	for (i = 1, len = 0; i < argc; i++)
+		len += strlen(argv[i]);
+	out1fmt("%zd", len);
+	out1c('\0');
+	for (i = 1; i < argc; i++) {
+		out1str(argv[i]);
+		out1c('\0');
+	}
+	return (0);
+}
diff --git a/sh/expand.h b/sh/expand.h
new file mode 100644
index 0000000..1ea876d
--- /dev/null
+++ b/sh/expand.h
@@ -0,0 +1,72 @@
+/*	$NetBSD: expand.h,v 1.16 2004/07/13 15:05:59 seb Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)expand.h	8.2 (Berkeley) 5/4/95
+ */
+
+struct strlist {
+	struct strlist *next;
+	char *text;
+};
+
+
+struct arglist {
+	struct strlist *list;
+	struct strlist **lastp;
+};
+
+/*
+ * expandarg() flags
+ */
+#define EXP_FULL	0x1	/* perform word splitting & file globbing */
+#define EXP_TILDE	0x2	/* do normal tilde expansion */
+#define	EXP_VARTILDE	0x4	/* expand tildes in an assignment */
+#define	EXP_REDIR	0x8	/* file glob for a redirection (1 match only) */
+#define EXP_CASE	0x10	/* keeps quotes around for CASE pattern */
+#define EXP_IFS_SPLIT	0x20	/* need to record arguments for ifs breakup */
+
+
+union node;
+void expandhere(union node *, int);
+void expandarg(union node *, struct arglist *, int);
+void expari(int);
+int patmatch(char *, char *, int);
+void rmescapes(char *);
+int casematch(union node *, char *);
+int wordexpcmd(int, char **);
+
+/* From arith.y */
+int arith(const char *);
+int expcmd(int , char **);
+void arith_lex_reset(void);
+int yylex(void);
diff --git a/sh/funcs/cmv b/sh/funcs/cmv
new file mode 100644
index 0000000..667f846
--- /dev/null
+++ b/sh/funcs/cmv
@@ -0,0 +1,50 @@
+#	$NetBSD: cmv,v 1.7 1995/05/11 21:31:05 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)cmv	8.2 (Berkeley) 5/4/95
+
+# Conditional move--don't replace an existing file.
+
+cmv() {
+	if test $# != 2
+	then	echo "cmv: arg count"
+		return 2
+	fi
+	if test -f "$2" -o -w "$2"
+	then	echo "$2 exists"
+		return 2
+	fi
+	/bin/mv "$1" "$2"
+}
diff --git a/sh/funcs/dirs b/sh/funcs/dirs
new file mode 100644
index 0000000..68bb317
--- /dev/null
+++ b/sh/funcs/dirs
@@ -0,0 +1,74 @@
+#	$NetBSD: dirs,v 1.7 1995/05/11 21:31:08 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)dirs	8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+	SAVE=`pwd`
+	if [ "$1" = "" ] 
+	then	if [ "$DSTACK" = "" ]
+		then	echo "pushd: directory stack empty."
+			return 1
+		fi
+		set $DSTACK
+		cd $1 || return
+		shift 1
+		DSTACK="$*"
+	else	cd $1 > /dev/null || return
+	fi
+	DSTACK="$SAVE $DSTACK"
+	dirs
+}
+
+popd () {
+	if [ "$DSTACK" = "" ] 
+	then	echo "popd: directory stack empty."
+		return 1
+	fi
+	set $DSTACK
+	cd $1
+	shift
+	DSTACK=$*
+	dirs
+}
+
+dirs () {
+	echo "`pwd` $DSTACK"
+	return 0
+}
diff --git a/sh/funcs/kill b/sh/funcs/kill
new file mode 100644
index 0000000..75b0180
--- /dev/null
+++ b/sh/funcs/kill
@@ -0,0 +1,50 @@
+#	$NetBSD: kill,v 1.7 1995/05/11 21:31:10 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)kill	8.2 (Berkeley) 5/4/95
+
+# Convert job names to process ids and then run /bin/kill.
+
+kill() {
+	local args x
+	args=
+	for x in "$@"
+	do	case $x in
+		%*)	x=`jobid "$x"` ;;
+		esac
+		args="$args $x"
+	done
+	/bin/kill $args
+}
diff --git a/sh/funcs/login b/sh/funcs/login
new file mode 100644
index 0000000..7ae08b2
--- /dev/null
+++ b/sh/funcs/login
@@ -0,0 +1,39 @@
+#	$NetBSD: login,v 1.7 1995/05/11 21:31:11 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)login	8.2 (Berkeley) 5/4/95
+
+# replaces the login builtin in the BSD shell
+login () exec login "$@"
diff --git a/sh/funcs/newgrp b/sh/funcs/newgrp
new file mode 100644
index 0000000..796a4f1
--- /dev/null
+++ b/sh/funcs/newgrp
@@ -0,0 +1,38 @@
+#	$NetBSD: newgrp,v 1.7 1995/05/11 21:31:12 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)newgrp	8.2 (Berkeley) 5/4/95
+
+newgrp() exec newgrp "$@"
diff --git a/sh/funcs/popd b/sh/funcs/popd
new file mode 100644
index 0000000..b2b65d5
--- /dev/null
+++ b/sh/funcs/popd
@@ -0,0 +1,74 @@
+#	$NetBSD: popd,v 1.7 1995/05/11 21:31:13 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)popd	8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+	SAVE=`pwd`
+	if [ "$1" = "" ] 
+	then	if [ "$DSTACK" = "" ]
+		then	echo "pushd: directory stack empty."
+			return 1
+		fi
+		set $DSTACK
+		cd $1 || return
+		shift 1
+		DSTACK="$*"
+	else	cd $1 > /dev/null || return
+	fi
+	DSTACK="$SAVE $DSTACK"
+	dirs
+}
+
+popd () {
+	if [ "$DSTACK" = "" ] 
+	then	echo "popd: directory stack empty."
+		return 1
+	fi
+	set $DSTACK
+	cd $1
+	shift
+	DSTACK=$*
+	dirs
+}
+
+dirs () {
+	echo "`pwd` $DSTACK"
+	return 0
+}
diff --git a/sh/funcs/pushd b/sh/funcs/pushd
new file mode 100644
index 0000000..b393038
--- /dev/null
+++ b/sh/funcs/pushd
@@ -0,0 +1,74 @@
+#	$NetBSD: pushd,v 1.7 1995/05/11 21:31:15 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)pushd	8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+	SAVE=`pwd`
+	if [ "$1" = "" ] 
+	then	if [ "$DSTACK" = "" ]
+		then	echo "pushd: directory stack empty."
+			return 1
+		fi
+		set $DSTACK
+		cd $1 || return
+		shift 1
+		DSTACK="$*"
+	else	cd $1 > /dev/null || return
+	fi
+	DSTACK="$SAVE $DSTACK"
+	dirs
+}
+
+popd () {
+	if [ "$DSTACK" = "" ] 
+	then	echo "popd: directory stack empty."
+		return 1
+	fi
+	set $DSTACK
+	cd $1
+	shift
+	DSTACK=$*
+	dirs
+}
+
+dirs () {
+	echo "`pwd` $DSTACK"
+	return 0
+}
diff --git a/sh/funcs/suspend b/sh/funcs/suspend
new file mode 100644
index 0000000..8a4197d
--- /dev/null
+++ b/sh/funcs/suspend
@@ -0,0 +1,42 @@
+#	$NetBSD: suspend,v 1.7 1995/05/11 21:31:17 christos Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+#    must display the following acknowledgement:
+#	This product includes software developed by the University of
+#	California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)suspend	8.2 (Berkeley) 5/4/95
+
+suspend() {
+	local -
+	set +j
+	kill -TSTP 0
+}
diff --git a/sh/histedit.c b/sh/histedit.c
new file mode 100644
index 0000000..4bb2b34
--- /dev/null
+++ b/sh/histedit.c
@@ -0,0 +1,540 @@
+/*	$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)histedit.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+/*
+ * Editline and history functions (and glue).
+ */
+#include "shell.h"
+#include "parser.h"
+#include "var.h"
+#include "options.h"
+#include "main.h"
+#include "output.h"
+#include "mystring.h"
+#include "myhistedit.h"
+#include "error.h"
+#ifndef SMALL
+#include "eval.h"
+#include "memalloc.h"
+
+#define MAXHISTLOOPS	4	/* max recursions through fc */
+#define DEFEDITOR	"ed"	/* default editor *should* be $EDITOR */
+
+History *hist;	/* history cookie */
+EditLine *el;	/* editline cookie */
+int displayhist;
+static FILE *el_in, *el_out;
+
+STATIC const char *fc_replace(const char *, char *, char *);
+
+#ifdef DEBUG
+extern FILE *tracefile;
+#endif
+
+/*
+ * Set history and editing status.  Called whenever the status may
+ * have changed (figures out what to do).
+ */
+void
+histedit(void)
+{
+	FILE *el_err;
+
+#define editing (Eflag || Vflag)
+
+	if (iflag) {
+		if (!hist) {
+			/*
+			 * turn history on
+			 */
+			INTOFF;
+			hist = history_init();
+			INTON;
+
+			if (hist != NULL)
+				sethistsize(histsizeval());
+			else
+				out2str("sh: can't initialize history\n");
+		}
+		if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
+			/*
+			 * turn editing on
+			 */
+			char *term, *shname;
+
+			INTOFF;
+			if (el_in == NULL)
+				el_in = fdopen(0, "r");
+			if (el_out == NULL)
+				el_out = fdopen(2, "w");
+			if (el_in == NULL || el_out == NULL)
+				goto bad;
+			el_err = el_out;
+#if DEBUG
+			if (tracefile)
+				el_err = tracefile;
+#endif
+			term = lookupvar("TERM");
+			if (term)
+				setenv("TERM", term, 1);
+			else
+				unsetenv("TERM");
+			shname = arg0;
+			if (shname[0] == '-')
+				shname++;
+			el = el_init(shname, el_in, el_out, el_err);
+			if (el != NULL) {
+				if (hist)
+					el_set(el, EL_HIST, history, hist);
+				el_set(el, EL_PROMPT, getprompt);
+				el_set(el, EL_SIGNAL, 1);
+			} else {
+bad:
+				out2str("sh: can't initialize editing\n");
+			}
+			INTON;
+		} else if (!editing && el) {
+			INTOFF;
+			el_end(el);
+			el = NULL;
+			INTON;
+		}
+		if (el) {
+			if (Vflag)
+				el_set(el, EL_EDITOR, "vi");
+			else if (Eflag)
+				el_set(el, EL_EDITOR, "emacs");
+			el_source(el, NULL);
+		}
+	} else {
+		INTOFF;
+		if (el) {	/* no editing if not interactive */
+			el_end(el);
+			el = NULL;
+		}
+		if (hist) {
+			history_end(hist);
+			hist = NULL;
+		}
+		INTON;
+	}
+}
+
+
+void
+sethistsize(const char *hs)
+{
+	int histsize;
+	HistEvent he;
+
+	if (hist != NULL) {
+		if (hs == NULL || *hs == '\0' ||
+		   (histsize = atoi(hs)) < 0)
+			histsize = 100;
+		history(hist, &he, H_SETSIZE, histsize);
+	}
+}
+
+void
+setterm(const char *term)
+{
+	if (el != NULL && term != NULL)
+		if (el_set(el, EL_TERMINAL, term) != 0) {
+			outfmt(out2, "sh: Can't set terminal type %s\n", term);
+			outfmt(out2, "sh: Using dumb terminal settings.\n");
+		}
+}
+
+int
+inputrc(argc, argv)
+	int argc;
+	char **argv;
+{
+	if (argc != 2) {
+		out2str("usage: inputrc file\n");
+		return 1;
+	}
+	if (el != NULL) {
+		if (el_source(el, argv[1])) {
+			out2str("inputrc: failed\n");
+			return 1;
+		} else
+			return 0;
+	} else {
+		out2str("sh: inputrc ignored, not editing\n");
+		return 1;
+	}
+}
+
+/*
+ *  This command is provided since POSIX decided to standardize
+ *  the Korn shell fc command.  Oh well...
+ */
+int
+histcmd(int argc, char **argv)
+{
+	int ch;
+	const char *editor = NULL;
+	HistEvent he;
+	int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
+	int i, retval;
+	const char *firststr, *laststr;
+	int first, last, direction;
+	char *pat = NULL, *repl;	/* ksh "fc old=new" crap */
+	static int active = 0;
+	struct jmploc jmploc;
+	struct jmploc *volatile savehandler;
+	char editfile[MAXPATHLEN + 1];
+	FILE *efp;
+#ifdef __GNUC__
+	/* Avoid longjmp clobbering */
+	(void) &editor;
+	(void) &lflg;
+	(void) &nflg;
+	(void) &rflg;
+	(void) &sflg;
+	(void) &firststr;
+	(void) &laststr;
+	(void) &pat;
+	(void) &repl;
+	(void) &efp;
+	(void) &argc;
+	(void) &argv;
+#endif
+
+	if (hist == NULL)
+		error("history not active");
+
+	if (argc == 1)
+		error("missing history argument");
+
+	optreset = 1; optind = 1; /* initialize getopt */
+	while (not_fcnumber(argv[optind]) &&
+	      (ch = getopt(argc, argv, ":e:lnrs")) != -1)
+		switch ((char)ch) {
+		case 'e':
+			editor = optionarg;
+			break;
+		case 'l':
+			lflg = 1;
+			break;
+		case 'n':
+			nflg = 1;
+			break;
+		case 'r':
+			rflg = 1;
+			break;
+		case 's':
+			sflg = 1;
+			break;
+		case ':':
+			error("option -%c expects argument", optopt);
+			/* NOTREACHED */
+		case '?':
+		default:
+			error("unknown option: -%c", optopt);
+			/* NOTREACHED */
+		}
+	argc -= optind, argv += optind;
+
+	/*
+	 * If executing...
+	 */
+	if (lflg == 0 || editor || sflg) {
+		lflg = 0;	/* ignore */
+		editfile[0] = '\0';
+		/*
+		 * Catch interrupts to reset active counter and
+		 * cleanup temp files.
+		 */
+		if (setjmp(jmploc.loc)) {
+			active = 0;
+			if (*editfile)
+				unlink(editfile);
+			handler = savehandler;
+			longjmp(handler->loc, 1);
+		}
+		savehandler = handler;
+		handler = &jmploc;
+		if (++active > MAXHISTLOOPS) {
+			active = 0;
+			displayhist = 0;
+			error("called recursively too many times");
+		}
+		/*
+		 * Set editor.
+		 */
+		if (sflg == 0) {
+			if (editor == NULL &&
+			    (editor = bltinlookup("FCEDIT", 1)) == NULL &&
+			    (editor = bltinlookup("EDITOR", 1)) == NULL)
+				editor = DEFEDITOR;
+			if (editor[0] == '-' && editor[1] == '\0') {
+				sflg = 1;	/* no edit */
+				editor = NULL;
+			}
+		}
+	}
+
+	/*
+	 * If executing, parse [old=new] now
+	 */
+	if (lflg == 0 && argc > 0 &&
+	     ((repl = strchr(argv[0], '=')) != NULL)) {
+		pat = argv[0];
+		*repl++ = '\0';
+		argc--, argv++;
+	}
+	/*
+	 * determine [first] and [last]
+	 */
+	switch (argc) {
+	case 0:
+		firststr = lflg ? "-16" : "-1";
+		laststr = "-1";
+		break;
+	case 1:
+		firststr = argv[0];
+		laststr = lflg ? "-1" : argv[0];
+		break;
+	case 2:
+		firststr = argv[0];
+		laststr = argv[1];
+		break;
+	default:
+		error("too many args");
+		/* NOTREACHED */
+	}
+	/*
+	 * Turn into event numbers.
+	 */
+	first = str_to_event(firststr, 0);
+	last = str_to_event(laststr, 1);
+
+	if (rflg) {
+		i = last;
+		last = first;
+		first = i;
+	}
+	/*
+	 * XXX - this should not depend on the event numbers
+	 * always increasing.  Add sequence numbers or offset
+	 * to the history element in next (diskbased) release.
+	 */
+	direction = first < last ? H_PREV : H_NEXT;
+
+	/*
+	 * If editing, grab a temp file.
+	 */
+	if (editor) {
+		int fd;
+		INTOFF;		/* easier */
+		snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP);
+		if ((fd = mkstemp(editfile)) < 0)
+			error("can't create temporary file %s", editfile);
+		if ((efp = fdopen(fd, "w")) == NULL) {
+			close(fd);
+			error("can't allocate stdio buffer for temp");
+		}
+	}
+
+	/*
+	 * Loop through selected history events.  If listing or executing,
+	 * do it now.  Otherwise, put into temp file and call the editor
+	 * after.
+	 *
+	 * The history interface needs rethinking, as the following
+	 * convolutions will demonstrate.
+	 */
+	history(hist, &he, H_FIRST);
+	retval = history(hist, &he, H_NEXT_EVENT, first);
+	for (;retval != -1; retval = history(hist, &he, direction)) {
+		if (lflg) {
+			if (!nflg)
+				out1fmt("%5d ", he.num);
+			out1str(he.str);
+		} else {
+			const char *s = pat ?
+			   fc_replace(he.str, pat, repl) : he.str;
+
+			if (sflg) {
+				if (displayhist) {
+					out2str(s);
+				}
+
+				evalstring(strcpy(stalloc(strlen(s) + 1), s), 0);
+				if (displayhist && hist) {
+					/*
+					 *  XXX what about recursive and
+					 *  relative histnums.
+					 */
+					history(hist, &he, H_ENTER, s);
+				}
+			} else
+				fputs(s, efp);
+		}
+		/*
+		 * At end?  (if we were to lose last, we'd sure be
+		 * messed up).
+		 */
+		if (he.num == last)
+			break;
+	}
+	if (editor) {
+		char *editcmd;
+
+		fclose(efp);
+		editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
+		sprintf(editcmd, "%s %s", editor, editfile);
+		evalstring(editcmd, 0);	/* XXX - should use no JC command */
+		INTON;
+		readcmdfile(editfile);	/* XXX - should read back - quick tst */
+		unlink(editfile);
+	}
+
+	if (lflg == 0 && active > 0)
+		--active;
+	if (displayhist)
+		displayhist = 0;
+	return 0;
+}
+
+STATIC const char *
+fc_replace(const char *s, char *p, char *r)
+{
+	char *dest;
+	int plen = strlen(p);
+
+	STARTSTACKSTR(dest);
+	while (*s) {
+		if (*s == *p && strncmp(s, p, plen) == 0) {
+			while (*r)
+				STPUTC(*r++, dest);
+			s += plen;
+			*p = '\0';	/* so no more matches */
+		} else
+			STPUTC(*s++, dest);
+	}
+	STACKSTRNUL(dest);
+	dest = grabstackstr(dest);
+
+	return (dest);
+}
+
+int
+not_fcnumber(char *s)
+{
+	if (s == NULL)
+		return 0;
+        if (*s == '-')
+                s++;
+	return (!is_number(s));
+}
+
+int
+str_to_event(const char *str, int last)
+{
+	HistEvent he;
+	const char *s = str;
+	int relative = 0;
+	int i, retval;
+
+	retval = history(hist, &he, H_FIRST);
+	switch (*s) {
+	case '-':
+		relative = 1;
+		/*FALLTHROUGH*/
+	case '+':
+		s++;
+	}
+	if (is_number(s)) {
+		i = atoi(s);
+		if (relative) {
+			while (retval != -1 && i--) {
+				retval = history(hist, &he, H_NEXT);
+			}
+			if (retval == -1)
+				retval = history(hist, &he, H_LAST);
+		} else {
+			retval = history(hist, &he, H_NEXT_EVENT, i);
+			if (retval == -1) {
+				/*
+				 * the notion of first and last is
+				 * backwards to that of the history package
+				 */
+				retval = history(hist, &he,
+						last ? H_FIRST : H_LAST);
+			}
+		}
+		if (retval == -1)
+			error("history number %s not found (internal error)",
+			       str);
+	} else {
+		/*
+		 * pattern
+		 */
+		retval = history(hist, &he, H_PREV_STR, str);
+		if (retval == -1)
+			error("history pattern not found: %s", str);
+	}
+	return (he.num);
+}
+#else
+int
+histcmd(int argc, char **argv)
+{
+	error("not compiled with history support");
+	/* NOTREACHED */
+}
+int
+inputrc(int argc, char **argv)
+{
+	error("not compiled with history support");
+	/* NOTREACHED */
+}
+#endif
diff --git a/sh/init.c b/sh/init.c
new file mode 100644
index 0000000..55ad172
--- /dev/null
+++ b/sh/init.c
@@ -0,0 +1,1090 @@
+/*
+ * This file was generated by the mkinit program.
+ */
+
+#include "shell.h"
+#include "mystring.h"
+#include "init.h"
+#include "eval.h"
+#include <stdio.h>
+#include "input.h"
+#include "error.h"
+#include <stdlib.h>
+#include "options.h"
+#include "redir.h"
+#include <signal.h>
+#include "trap.h"
+#include "output.h"
+#include "memalloc.h"
+#include "var.h"
+
+
+
+#undef  ATABSIZE
+#define ATABSIZE 39
+#undef  YYBISON
+#define YYBISON 1
+#undef  YYSKELETON_NAME
+#define YYSKELETON_NAME "yacc.c"
+#undef  YYPURE
+#define YYPURE 0
+#undef  YYLSP_NEEDED
+#define YYLSP_NEEDED 0
+#undef  ARITH_NUM
+#define ARITH_NUM 258
+#undef  ARITH_LPAREN
+#define ARITH_LPAREN 259
+#undef  ARITH_RPAREN
+#define ARITH_RPAREN 260
+#undef  ARITH_OR
+#define ARITH_OR 261
+#undef  ARITH_AND
+#define ARITH_AND 262
+#undef  ARITH_BOR
+#define ARITH_BOR 263
+#undef  ARITH_BXOR
+#define ARITH_BXOR 264
+#undef  ARITH_BAND
+#define ARITH_BAND 265
+#undef  ARITH_NE
+#define ARITH_NE 266
+#undef  ARITH_EQ
+#define ARITH_EQ 267
+#undef  ARITH_LE
+#define ARITH_LE 268
+#undef  ARITH_GE
+#define ARITH_GE 269
+#undef  ARITH_GT
+#define ARITH_GT 270
+#undef  ARITH_LT
+#define ARITH_LT 271
+#undef  ARITH_RSHIFT
+#define ARITH_RSHIFT 272
+#undef  ARITH_LSHIFT
+#define ARITH_LSHIFT 273
+#undef  ARITH_SUB
+#define ARITH_SUB 274
+#undef  ARITH_ADD
+#define ARITH_ADD 275
+#undef  ARITH_REM
+#define ARITH_REM 276
+#undef  ARITH_DIV
+#define ARITH_DIV 277
+#undef  ARITH_MUL
+#define ARITH_MUL 278
+#undef  ARITH_BNOT
+#define ARITH_BNOT 279
+#undef  ARITH_NOT
+#define ARITH_NOT 280
+#undef  ARITH_UNARYPLUS
+#define ARITH_UNARYPLUS 281
+#undef  ARITH_UNARYMINUS
+#define ARITH_UNARYMINUS 282
+#undef  YYFINAL
+#define YYFINAL  14
+#undef  YYLAST
+#define YYLAST   170
+#undef  YYNTOKENS
+#define YYNTOKENS  28
+#undef  YYNNTS
+#define YYNNTS  3
+#undef  YYNRULES
+#define YYNRULES  26
+#undef  YYNSTATES
+#define YYNSTATES  52
+#undef  YYUNDEFTOK
+#define YYUNDEFTOK  2
+#undef  YYMAXUTOK
+#define YYMAXUTOK   282
+#undef  YYPACT_NINF
+#define YYPACT_NINF -13
+#undef  YYTABLE_NINF
+#define YYTABLE_NINF -1
+#undef  yyerrok
+#define yyerrok		(yyerrstatus = 0)
+#undef  yyclearin
+#define yyclearin	(yychar = YYEMPTY)
+#undef  YYEMPTY
+#define YYEMPTY		(-2)
+#undef  YYEOF
+#define YYEOF		0
+#undef  YYACCEPT
+#define YYACCEPT	goto yyacceptlab
+#undef  YYABORT
+#define YYABORT		goto yyabortlab
+#undef  YYERROR
+#define YYERROR		goto yyerrorlab
+#undef  YYFAIL
+#define YYFAIL		goto yyerrlab
+#undef  YYTERROR
+#define YYTERROR	1
+#undef  YYERRCODE
+#define YYERRCODE	256
+#undef  YYPOPSTACK
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#undef  YY_INT_ALIGNED
+#define  YY_INT_ALIGNED short int
+#undef  FLEX_SCANNER
+#define FLEX_SCANNER
+#undef  YY_FLEX_MAJOR_VERSION
+#define YY_FLEX_MAJOR_VERSION 2
+#undef  YY_FLEX_MINOR_VERSION
+#define YY_FLEX_MINOR_VERSION 5
+#undef  YY_FLEX_SUBMINOR_VERSION
+#define YY_FLEX_SUBMINOR_VERSION 31
+#undef  FLEX_BETA
+#define FLEX_BETA
+#undef  FLEXINT_H
+#define FLEXINT_H
+#undef  INT8_MIN
+#define INT8_MIN               (-128)
+#undef  INT16_MIN
+#define INT16_MIN              (-32767-1)
+#undef  INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#undef  INT8_MAX
+#define INT8_MAX               (127)
+#undef  INT16_MAX
+#define INT16_MAX              (32767)
+#undef  INT32_MAX
+#define INT32_MAX              (2147483647)
+#undef  UINT8_MAX
+#define UINT8_MAX              (255U)
+#undef  UINT16_MAX
+#define UINT16_MAX             (65535U)
+#undef  UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  yyconst
+#define yyconst const
+#undef  yyconst
+#define yyconst
+#undef  YY_NULL
+#define YY_NULL 0
+#undef  BEGIN
+#define BEGIN (yy_start) = 1 + 2 *
+#undef  YY_START
+#define YY_START (((yy_start) - 1) / 2)
+#undef  YYSTATE
+#define YYSTATE YY_START
+#undef  YY_NEW_FILE
+#define YY_NEW_FILE yyrestart(yyin  )
+#undef  YY_END_OF_BUFFER_CHAR
+#define YY_END_OF_BUFFER_CHAR 0
+#undef  YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#undef  YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+#undef  EOB_ACT_CONTINUE_SCAN
+#define EOB_ACT_CONTINUE_SCAN 0
+#undef  EOB_ACT_END_OF_FILE
+#define EOB_ACT_END_OF_FILE 1
+#undef  EOB_ACT_LAST_MATCH
+#define EOB_ACT_LAST_MATCH 2
+#undef  YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+#undef  YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+#undef  YY_BUFFER_NEW
+#define YY_BUFFER_NEW 0
+#undef  YY_BUFFER_NORMAL
+#define YY_BUFFER_NORMAL 1
+#undef  YY_BUFFER_EOF_PENDING
+#define YY_BUFFER_EOF_PENDING 2
+#undef  YY_CURRENT_BUFFER
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+#undef  YY_CURRENT_BUFFER_LVALUE
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+#undef  YY_FLUSH_BUFFER
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+#undef  yy_new_buffer
+#define yy_new_buffer yy_create_buffer
+#undef  YY_SKIP_YYWRAP
+#define YY_SKIP_YYWRAP
+#undef  yytext_ptr
+#define yytext_ptr yytext
+#undef  YY_DO_BEFORE_ACTION
+#define YY_DO_BEFORE_ACTION \
+#undef  YY_NUM_RULES
+#define YY_NUM_RULES 29
+#undef  YY_END_OF_BUFFER
+#define YY_END_OF_BUFFER 30
+#undef  REJECT
+#define REJECT reject_used_but_not_detected
+#undef  YY_MORE_ADJ
+#define YY_MORE_ADJ 0
+#undef  YY_RESTORE_YY_MORE_OFFSET
+#define YY_RESTORE_YY_MORE_OFFSET
+#undef  YY_NO_UNPUT
+#define YY_NO_UNPUT
+#undef  INITIAL
+#define INITIAL 0
+#undef  YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#undef  YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#undef  ECHO
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#undef  YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#undef  YY_DECL_IS_OURS
+#define YY_DECL_IS_OURS 1
+#undef  YY_DECL
+#define YY_DECL int yylex (void)
+#undef  YY_USER_ACTION
+#define YY_USER_ACTION
+#undef  YY_BREAK
+#define YY_BREAK break;
+#undef  YY_RULE_SETUP
+#define YY_RULE_SETUP \
+#undef  YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#undef  YYTABLES_NAME
+#define YYTABLES_NAME "yytables"
+#undef  MAXPWD
+#define MAXPWD 256
+#undef  signal
+#define signal bsd_signal
+#undef  ALL
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+#undef  EV_EXIT
+#define EV_EXIT 01		/* exit after evaluating tree */
+#undef  EV_TESTED
+#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
+#undef  EV_BACKCMD
+#define EV_BACKCMD 04		/* command executing within back quotes */
+#undef  CMDTABLESIZE
+#define CMDTABLESIZE 31		/* should be prime */
+#undef  ARB
+#define ARB 1			/* actual size determined at run time */
+#undef  NEWARGS
+#define NEWARGS 5
+#undef  EOF_NLEFT
+#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
+#undef  _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#undef  PROFILE
+#define PROFILE 0
+#undef  SIGSSIZE
+#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+#undef  MINSIZE
+#define MINSIZE 504		/* minimum size of a block */
+#undef  DEFINE_OPTIONS
+#define DEFINE_OPTIONS
+#undef  EOFMARKLEN
+#define EOFMARKLEN 79
+#undef  OPENBRACE
+#define OPENBRACE '{'
+#undef  CLOSEBRACE
+#define CLOSEBRACE '}'
+#undef  EMPTY
+#define EMPTY -2		/* marks an unused slot in redirtab */
+#undef  signal
+#define signal bsd_signal
+#undef  sys_signame
+#define sys_signame sys_siglist
+#undef  S_DFL
+#define S_DFL 1			/* default signal handling (SIG_DFL) */
+#undef  S_CATCH
+#define S_CATCH 2		/* signal is caught */
+#undef  S_IGN
+#define S_IGN 3			/* signal is ignored (SIG_IGN) */
+#undef  S_HARD_IGN
+#define S_HARD_IGN 4		/* signal is ignored permenantly */
+#undef  S_RESET
+#define S_RESET 5		/* temporary - to reset a hard ignored sig */
+#undef  OUTBUFSIZ
+#define OUTBUFSIZ BUFSIZ
+#undef  BLOCK_OUT
+#define BLOCK_OUT -2		/* output to a fixed block of memory */
+#undef  MEM_OUT
+#define MEM_OUT -3		/* output to dynamically allocated memory */
+#undef  OUTPUT_ERR
+#define OUTPUT_ERR 01		/* error occurred on output */
+#undef  TEMPSIZE
+#define TEMPSIZE 24
+#undef  HAVE_VASPRINTF
+#define HAVE_VASPRINTF 1
+#undef  VTABSIZE
+#define VTABSIZE 39
+#undef  VTABSIZE
+#define VTABSIZE 517
+#undef  ATABSIZE
+#define ATABSIZE 39
+#undef  YYBISON
+#define YYBISON 1
+#undef  YYSKELETON_NAME
+#define YYSKELETON_NAME "yacc.c"
+#undef  YYPURE
+#define YYPURE 0
+#undef  YYLSP_NEEDED
+#define YYLSP_NEEDED 0
+#undef  ARITH_NUM
+#define ARITH_NUM 258
+#undef  ARITH_LPAREN
+#define ARITH_LPAREN 259
+#undef  ARITH_RPAREN
+#define ARITH_RPAREN 260
+#undef  ARITH_OR
+#define ARITH_OR 261
+#undef  ARITH_AND
+#define ARITH_AND 262
+#undef  ARITH_BOR
+#define ARITH_BOR 263
+#undef  ARITH_BXOR
+#define ARITH_BXOR 264
+#undef  ARITH_BAND
+#define ARITH_BAND 265
+#undef  ARITH_NE
+#define ARITH_NE 266
+#undef  ARITH_EQ
+#define ARITH_EQ 267
+#undef  ARITH_LE
+#define ARITH_LE 268
+#undef  ARITH_GE
+#define ARITH_GE 269
+#undef  ARITH_GT
+#define ARITH_GT 270
+#undef  ARITH_LT
+#define ARITH_LT 271
+#undef  ARITH_RSHIFT
+#define ARITH_RSHIFT 272
+#undef  ARITH_LSHIFT
+#define ARITH_LSHIFT 273
+#undef  ARITH_SUB
+#define ARITH_SUB 274
+#undef  ARITH_ADD
+#define ARITH_ADD 275
+#undef  ARITH_REM
+#define ARITH_REM 276
+#undef  ARITH_DIV
+#define ARITH_DIV 277
+#undef  ARITH_MUL
+#define ARITH_MUL 278
+#undef  ARITH_BNOT
+#define ARITH_BNOT 279
+#undef  ARITH_NOT
+#define ARITH_NOT 280
+#undef  ARITH_UNARYPLUS
+#define ARITH_UNARYPLUS 281
+#undef  ARITH_UNARYMINUS
+#define ARITH_UNARYMINUS 282
+#undef  YYFINAL
+#define YYFINAL  14
+#undef  YYLAST
+#define YYLAST   170
+#undef  YYNTOKENS
+#define YYNTOKENS  28
+#undef  YYNNTS
+#define YYNNTS  3
+#undef  YYNRULES
+#define YYNRULES  26
+#undef  YYNSTATES
+#define YYNSTATES  52
+#undef  YYUNDEFTOK
+#define YYUNDEFTOK  2
+#undef  YYMAXUTOK
+#define YYMAXUTOK   282
+#undef  YYPACT_NINF
+#define YYPACT_NINF -13
+#undef  YYTABLE_NINF
+#define YYTABLE_NINF -1
+#undef  yyerrok
+#define yyerrok		(yyerrstatus = 0)
+#undef  yyclearin
+#define yyclearin	(yychar = YYEMPTY)
+#undef  YYEMPTY
+#define YYEMPTY		(-2)
+#undef  YYEOF
+#define YYEOF		0
+#undef  YYACCEPT
+#define YYACCEPT	goto yyacceptlab
+#undef  YYABORT
+#define YYABORT		goto yyabortlab
+#undef  YYERROR
+#define YYERROR		goto yyerrorlab
+#undef  YYFAIL
+#define YYFAIL		goto yyerrlab
+#undef  YYTERROR
+#define YYTERROR	1
+#undef  YYERRCODE
+#define YYERRCODE	256
+#undef  YYPOPSTACK
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#undef  YY_INT_ALIGNED
+#define  YY_INT_ALIGNED short int
+#undef  FLEX_SCANNER
+#define FLEX_SCANNER
+#undef  YY_FLEX_MAJOR_VERSION
+#define YY_FLEX_MAJOR_VERSION 2
+#undef  YY_FLEX_MINOR_VERSION
+#define YY_FLEX_MINOR_VERSION 5
+#undef  YY_FLEX_SUBMINOR_VERSION
+#define YY_FLEX_SUBMINOR_VERSION 31
+#undef  FLEX_BETA
+#define FLEX_BETA
+#undef  FLEXINT_H
+#define FLEXINT_H
+#undef  INT8_MIN
+#define INT8_MIN               (-128)
+#undef  INT16_MIN
+#define INT16_MIN              (-32767-1)
+#undef  INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#undef  INT8_MAX
+#define INT8_MAX               (127)
+#undef  INT16_MAX
+#define INT16_MAX              (32767)
+#undef  INT32_MAX
+#define INT32_MAX              (2147483647)
+#undef  UINT8_MAX
+#define UINT8_MAX              (255U)
+#undef  UINT16_MAX
+#define UINT16_MAX             (65535U)
+#undef  UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  yyconst
+#define yyconst const
+#undef  yyconst
+#define yyconst
+#undef  YY_NULL
+#define YY_NULL 0
+#undef  BEGIN
+#define BEGIN (yy_start) = 1 + 2 *
+#undef  YY_START
+#define YY_START (((yy_start) - 1) / 2)
+#undef  YYSTATE
+#define YYSTATE YY_START
+#undef  YY_NEW_FILE
+#define YY_NEW_FILE yyrestart(yyin  )
+#undef  YY_END_OF_BUFFER_CHAR
+#define YY_END_OF_BUFFER_CHAR 0
+#undef  YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#undef  YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+#undef  EOB_ACT_CONTINUE_SCAN
+#define EOB_ACT_CONTINUE_SCAN 0
+#undef  EOB_ACT_END_OF_FILE
+#define EOB_ACT_END_OF_FILE 1
+#undef  EOB_ACT_LAST_MATCH
+#define EOB_ACT_LAST_MATCH 2
+#undef  YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+#undef  YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+#undef  YY_BUFFER_NEW
+#define YY_BUFFER_NEW 0
+#undef  YY_BUFFER_NORMAL
+#define YY_BUFFER_NORMAL 1
+#undef  YY_BUFFER_EOF_PENDING
+#define YY_BUFFER_EOF_PENDING 2
+#undef  YY_CURRENT_BUFFER
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+#undef  YY_CURRENT_BUFFER_LVALUE
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+#undef  YY_FLUSH_BUFFER
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+#undef  yy_new_buffer
+#define yy_new_buffer yy_create_buffer
+#undef  YY_SKIP_YYWRAP
+#define YY_SKIP_YYWRAP
+#undef  yytext_ptr
+#define yytext_ptr yytext
+#undef  YY_DO_BEFORE_ACTION
+#define YY_DO_BEFORE_ACTION \
+#undef  YY_NUM_RULES
+#define YY_NUM_RULES 29
+#undef  YY_END_OF_BUFFER
+#define YY_END_OF_BUFFER 30
+#undef  REJECT
+#define REJECT reject_used_but_not_detected
+#undef  YY_MORE_ADJ
+#define YY_MORE_ADJ 0
+#undef  YY_RESTORE_YY_MORE_OFFSET
+#define YY_RESTORE_YY_MORE_OFFSET
+#undef  YY_NO_UNPUT
+#define YY_NO_UNPUT
+#undef  INITIAL
+#define INITIAL 0
+#undef  YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#undef  YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#undef  ECHO
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#undef  YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#undef  YY_DECL_IS_OURS
+#define YY_DECL_IS_OURS 1
+#undef  YY_DECL
+#define YY_DECL int yylex (void)
+#undef  YY_USER_ACTION
+#define YY_USER_ACTION
+#undef  YY_BREAK
+#define YY_BREAK break;
+#undef  YY_RULE_SETUP
+#define YY_RULE_SETUP \
+#undef  YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#undef  YYTABLES_NAME
+#define YYTABLES_NAME "yytables"
+#undef  MAXPWD
+#define MAXPWD 256
+#undef  signal
+#define signal bsd_signal
+#undef  ALL
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+#undef  EV_EXIT
+#define EV_EXIT 01		/* exit after evaluating tree */
+#undef  EV_TESTED
+#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
+#undef  EV_BACKCMD
+#define EV_BACKCMD 04		/* command executing within back quotes */
+#undef  CMDTABLESIZE
+#define CMDTABLESIZE 31		/* should be prime */
+#undef  ARB
+#define ARB 1			/* actual size determined at run time */
+#undef  NEWARGS
+#define NEWARGS 5
+#undef  EOF_NLEFT
+#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
+#undef  _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#undef  PROFILE
+#define PROFILE 0
+#undef  SIGSSIZE
+#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+#undef  MINSIZE
+#define MINSIZE 504		/* minimum size of a block */
+#undef  DEFINE_OPTIONS
+#define DEFINE_OPTIONS
+#undef  EOFMARKLEN
+#define EOFMARKLEN 79
+#undef  OPENBRACE
+#define OPENBRACE '{'
+#undef  CLOSEBRACE
+#define CLOSEBRACE '}'
+#undef  EMPTY
+#define EMPTY -2		/* marks an unused slot in redirtab */
+#undef  signal
+#define signal bsd_signal
+#undef  sys_signame
+#define sys_signame sys_siglist
+#undef  S_DFL
+#define S_DFL 1			/* default signal handling (SIG_DFL) */
+#undef  S_CATCH
+#define S_CATCH 2		/* signal is caught */
+#undef  S_IGN
+#define S_IGN 3			/* signal is ignored (SIG_IGN) */
+#undef  S_HARD_IGN
+#define S_HARD_IGN 4		/* signal is ignored permenantly */
+#undef  S_RESET
+#define S_RESET 5		/* temporary - to reset a hard ignored sig */
+#undef  OUTBUFSIZ
+#define OUTBUFSIZ BUFSIZ
+#undef  BLOCK_OUT
+#define BLOCK_OUT -2		/* output to a fixed block of memory */
+#undef  MEM_OUT
+#define MEM_OUT -3		/* output to dynamically allocated memory */
+#undef  OUTPUT_ERR
+#define OUTPUT_ERR 01		/* error occurred on output */
+#undef  TEMPSIZE
+#define TEMPSIZE 24
+#undef  HAVE_VASPRINTF
+#define HAVE_VASPRINTF 1
+#undef  VTABSIZE
+#define VTABSIZE 39
+#undef  VTABSIZE
+#define VTABSIZE 517
+#undef  main
+#define main echocmd
+#undef  YYBISON
+#define YYBISON 1
+#undef  YYSKELETON_NAME
+#define YYSKELETON_NAME "yacc.c"
+#undef  YYPURE
+#define YYPURE 0
+#undef  YYLSP_NEEDED
+#define YYLSP_NEEDED 0
+#undef  ARITH_NUM
+#define ARITH_NUM 258
+#undef  ARITH_LPAREN
+#define ARITH_LPAREN 259
+#undef  ARITH_RPAREN
+#define ARITH_RPAREN 260
+#undef  ARITH_OR
+#define ARITH_OR 261
+#undef  ARITH_AND
+#define ARITH_AND 262
+#undef  ARITH_BOR
+#define ARITH_BOR 263
+#undef  ARITH_BXOR
+#define ARITH_BXOR 264
+#undef  ARITH_BAND
+#define ARITH_BAND 265
+#undef  ARITH_NE
+#define ARITH_NE 266
+#undef  ARITH_EQ
+#define ARITH_EQ 267
+#undef  ARITH_LE
+#define ARITH_LE 268
+#undef  ARITH_GE
+#define ARITH_GE 269
+#undef  ARITH_GT
+#define ARITH_GT 270
+#undef  ARITH_LT
+#define ARITH_LT 271
+#undef  ARITH_RSHIFT
+#define ARITH_RSHIFT 272
+#undef  ARITH_LSHIFT
+#define ARITH_LSHIFT 273
+#undef  ARITH_SUB
+#define ARITH_SUB 274
+#undef  ARITH_ADD
+#define ARITH_ADD 275
+#undef  ARITH_REM
+#define ARITH_REM 276
+#undef  ARITH_DIV
+#define ARITH_DIV 277
+#undef  ARITH_MUL
+#define ARITH_MUL 278
+#undef  ARITH_BNOT
+#define ARITH_BNOT 279
+#undef  ARITH_NOT
+#define ARITH_NOT 280
+#undef  ARITH_UNARYPLUS
+#define ARITH_UNARYPLUS 281
+#undef  ARITH_UNARYMINUS
+#define ARITH_UNARYMINUS 282
+#undef  YYFINAL
+#define YYFINAL  14
+#undef  YYLAST
+#define YYLAST   170
+#undef  YYNTOKENS
+#define YYNTOKENS  28
+#undef  YYNNTS
+#define YYNNTS  3
+#undef  YYNRULES
+#define YYNRULES  26
+#undef  YYNSTATES
+#define YYNSTATES  52
+#undef  YYUNDEFTOK
+#define YYUNDEFTOK  2
+#undef  YYMAXUTOK
+#define YYMAXUTOK   282
+#undef  YYPACT_NINF
+#define YYPACT_NINF -13
+#undef  YYTABLE_NINF
+#define YYTABLE_NINF -1
+#undef  yyerrok
+#define yyerrok		(yyerrstatus = 0)
+#undef  yyclearin
+#define yyclearin	(yychar = YYEMPTY)
+#undef  YYEMPTY
+#define YYEMPTY		(-2)
+#undef  YYEOF
+#define YYEOF		0
+#undef  YYACCEPT
+#define YYACCEPT	goto yyacceptlab
+#undef  YYABORT
+#define YYABORT		goto yyabortlab
+#undef  YYERROR
+#define YYERROR		goto yyerrorlab
+#undef  YYFAIL
+#define YYFAIL		goto yyerrlab
+#undef  YYTERROR
+#define YYTERROR	1
+#undef  YYERRCODE
+#define YYERRCODE	256
+#undef  YYPOPSTACK
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#undef  YY_INT_ALIGNED
+#define  YY_INT_ALIGNED short int
+#undef  FLEX_SCANNER
+#define FLEX_SCANNER
+#undef  YY_FLEX_MAJOR_VERSION
+#define YY_FLEX_MAJOR_VERSION 2
+#undef  YY_FLEX_MINOR_VERSION
+#define YY_FLEX_MINOR_VERSION 5
+#undef  YY_FLEX_SUBMINOR_VERSION
+#define YY_FLEX_SUBMINOR_VERSION 31
+#undef  FLEX_BETA
+#define FLEX_BETA
+#undef  FLEXINT_H
+#define FLEXINT_H
+#undef  INT8_MIN
+#define INT8_MIN               (-128)
+#undef  INT16_MIN
+#define INT16_MIN              (-32767-1)
+#undef  INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#undef  INT8_MAX
+#define INT8_MAX               (127)
+#undef  INT16_MAX
+#define INT16_MAX              (32767)
+#undef  INT32_MAX
+#define INT32_MAX              (2147483647)
+#undef  UINT8_MAX
+#define UINT8_MAX              (255U)
+#undef  UINT16_MAX
+#define UINT16_MAX             (65535U)
+#undef  UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  YY_USE_CONST
+#define YY_USE_CONST
+#undef  yyconst
+#define yyconst const
+#undef  yyconst
+#define yyconst
+#undef  YY_NULL
+#define YY_NULL 0
+#undef  BEGIN
+#define BEGIN (yy_start) = 1 + 2 *
+#undef  YY_START
+#define YY_START (((yy_start) - 1) / 2)
+#undef  YYSTATE
+#define YYSTATE YY_START
+#undef  YY_NEW_FILE
+#define YY_NEW_FILE yyrestart(yyin  )
+#undef  YY_END_OF_BUFFER_CHAR
+#define YY_END_OF_BUFFER_CHAR 0
+#undef  YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#undef  YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+#undef  EOB_ACT_CONTINUE_SCAN
+#define EOB_ACT_CONTINUE_SCAN 0
+#undef  EOB_ACT_END_OF_FILE
+#define EOB_ACT_END_OF_FILE 1
+#undef  EOB_ACT_LAST_MATCH
+#define EOB_ACT_LAST_MATCH 2
+#undef  YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+#undef  YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+#undef  YY_BUFFER_NEW
+#define YY_BUFFER_NEW 0
+#undef  YY_BUFFER_NORMAL
+#define YY_BUFFER_NORMAL 1
+#undef  YY_BUFFER_EOF_PENDING
+#define YY_BUFFER_EOF_PENDING 2
+#undef  YY_CURRENT_BUFFER
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+#undef  YY_CURRENT_BUFFER_LVALUE
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+#undef  YY_FLUSH_BUFFER
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+#undef  yy_new_buffer
+#define yy_new_buffer yy_create_buffer
+#undef  yytext_ptr
+#define yytext_ptr yytext
+#undef  YY_DO_BEFORE_ACTION
+#define YY_DO_BEFORE_ACTION \
+#undef  YY_NUM_RULES
+#define YY_NUM_RULES 29
+#undef  YY_END_OF_BUFFER
+#define YY_END_OF_BUFFER 30
+#undef  REJECT
+#define REJECT reject_used_but_not_detected
+#undef  YY_MORE_ADJ
+#define YY_MORE_ADJ 0
+#undef  YY_RESTORE_YY_MORE_OFFSET
+#define YY_RESTORE_YY_MORE_OFFSET
+#undef  YY_NO_UNPUT
+#define YY_NO_UNPUT
+#undef  INITIAL
+#define INITIAL 0
+#undef  YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#undef  YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#undef  ECHO
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#undef  YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#undef  YY_DECL_IS_OURS
+#define YY_DECL_IS_OURS 1
+#undef  YY_DECL
+#define YY_DECL int yylex (void)
+#undef  YY_USER_ACTION
+#define YY_USER_ACTION
+#undef  YY_BREAK
+#define YY_BREAK break;
+#undef  YY_RULE_SETUP
+#define YY_RULE_SETUP \
+#undef  YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#undef  YYTABLES_NAME
+#define YYTABLES_NAME "yytables"
+#undef  MAXPWD
+#define MAXPWD 256
+#undef  ALL
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+#undef  EV_EXIT
+#define EV_EXIT 01		/* exit after evaluating tree */
+#undef  EV_TESTED
+#define EV_TESTED 02		/* exit status is checked; ignore -e flag */
+#undef  EV_BACKCMD
+#define EV_BACKCMD 04		/* command executing within back quotes */
+#undef  CMDTABLESIZE
+#define CMDTABLESIZE 31		/* should be prime */
+#undef  ARB
+#define ARB 1			/* actual size determined at run time */
+#undef  NEWARGS
+#define NEWARGS 5
+#undef  EOF_NLEFT
+#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
+#undef  _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#undef  PROFILE
+#define PROFILE 0
+#undef  SIGSSIZE
+#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+#undef  MINSIZE
+#define MINSIZE 504		/* minimum size of a block */
+#undef  DEFINE_OPTIONS
+#define DEFINE_OPTIONS
+#undef  EOFMARKLEN
+#define EOFMARKLEN 79
+#undef  OPENBRACE
+#define OPENBRACE '{'
+#undef  CLOSEBRACE
+#define CLOSEBRACE '}'
+#undef  EMPTY
+#define EMPTY -2		/* marks an unused slot in redirtab */
+#undef  S_DFL
+#define S_DFL 1			/* default signal handling (SIG_DFL) */
+#undef  S_CATCH
+#define S_CATCH 2		/* signal is caught */
+#undef  S_IGN
+#define S_IGN 3			/* signal is ignored (SIG_IGN) */
+#undef  S_HARD_IGN
+#define S_HARD_IGN 4		/* signal is ignored permenantly */
+#undef  S_RESET
+#define S_RESET 5		/* temporary - to reset a hard ignored sig */
+#undef  OUTBUFSIZ
+#define OUTBUFSIZ BUFSIZ
+#undef  BLOCK_OUT
+#define BLOCK_OUT -2		/* output to a fixed block of memory */
+#undef  MEM_OUT
+#define MEM_OUT -3		/* output to dynamically allocated memory */
+#undef  OUTPUT_ERR
+#define OUTPUT_ERR 01		/* error occurred on output */
+#undef  TEMPSIZE
+#define TEMPSIZE 24
+#undef  HAVE_VASPRINTF
+#define HAVE_VASPRINTF 1
+#undef  VTABSIZE
+#define VTABSIZE 39
+#undef  VTABSIZE
+#define VTABSIZE 517
+#undef  main
+#define main echocmd
+
+
+
+extern void rmaliases(void);
+
+extern int loopnest;		/* current loop nesting level */
+
+extern void deletefuncs(void);
+extern void hash_special_builtins(void);
+
+struct strpush {
+	struct strpush *prev;	/* preceding string on stack */
+	char *prevstring;
+	int prevnleft;
+	int prevlleft;
+	struct alias *ap;	/* if push was associated with an alias */
+};
+
+struct parsefile {
+	struct parsefile *prev;	/* preceding file on stack */
+	int linno;		/* current line */
+	int fd;			/* file descriptor (or -1 if string) */
+	int nleft;		/* number of chars left in this line */
+	int lleft;		/* number of chars left in this buffer */
+	char *nextc;		/* next char in buffer */
+	char *buf;		/* input buffer */
+	struct strpush *strpush; /* for pushing strings at this level */
+	struct strpush basestrpush; /* so pushing one is fast */
+};
+
+extern int parselleft;		/* copy of parsefile->lleft */
+extern struct parsefile basepf;	/* top level input file */
+extern char basebuf[BUFSIZ];	/* buffer for top level input file */
+
+extern pid_t backgndpid;	/* pid of last background process */
+extern int jobctl;
+
+extern int tokpushback;		/* last token pushed back */
+extern int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
+
+struct redirtab {
+	struct redirtab *next;
+	short renamed[10];
+};
+
+extern struct redirtab *redirlist;
+
+extern char sigmode[NSIG];	/* current value of signal */
+
+extern char **environ;
+
+
+
+/*
+ * Initialization code.
+ */
+
+void
+init() {
+
+      /* from exec.c: */
+      {
+	      hash_special_builtins();
+      }
+
+      /* from input.c: */
+      {
+	      basepf.nextc = basepf.buf = basebuf;
+      }
+
+      /* from var.c: */
+      {
+	      char **envp;
+
+	      initvar();
+	      for (envp = environ ; *envp ; envp++) {
+		      if (strchr(*envp, '=')) {
+			      setvareq(*envp, VEXPORT|VTEXTFIXED);
+		      }
+	      }
+      }
+}
+
+
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+
+void
+reset() {
+
+      /* from eval.c: */
+      {
+	      evalskip = 0;
+	      loopnest = 0;
+	      funcnest = 0;
+      }
+
+      /* from input.c: */
+      {
+	      if (exception != EXSHELLPROC)
+		      parselleft = parsenleft = 0;	/* clear input buffer */
+	      popallfiles();
+      }
+
+      /* from parser.c: */
+      {
+	      tokpushback = 0;
+	      checkkwd = 0;
+      }
+
+      /* from redir.c: */
+      {
+	      while (redirlist)
+		      popredir();
+      }
+
+      /* from output.c: */
+      {
+	      out1 = &output;
+	      out2 = &errout;
+	      if (memout.buf != NULL) {
+		      ckfree(memout.buf);
+		      memout.buf = NULL;
+	      }
+      }
+}
+
+
+
+/*
+ * This routine is called to initialize the shell to run a shell procedure.
+ */
+
+void
+initshellproc() {
+
+      /* from alias.c: */
+      {
+	      rmaliases();
+      }
+
+      /* from eval.c: */
+      {
+	      exitstatus = 0;
+      }
+
+      /* from exec.c: */
+      {
+	      deletefuncs();
+      }
+
+      /* from input.c: */
+      {
+	      popallfiles();
+      }
+
+      /* from jobs.c: */
+      {
+	      backgndpid = -1;
+#if JOBS
+	      jobctl = 0;
+#endif
+      }
+
+      /* from options.c: */
+      {
+	      int i;
+
+	      for (i = 0; optlist[i].name; i++)
+		      optlist[i].val = 0;
+	      optschanged();
+
+      }
+
+      /* from redir.c: */
+      {
+	      clearredir(0);
+      }
+
+      /* from trap.c: */
+      {
+	      char *sm;
+
+	      clear_traps(0);
+	      for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
+		      if (*sm == S_IGN)
+			      *sm = S_HARD_IGN;
+	      }
+      }
+
+      /* from var.c: */
+      {
+	      shprocvar();
+      }
+}
diff --git a/sh/init.h b/sh/init.h
new file mode 100644
index 0000000..60d924e
--- /dev/null
+++ b/sh/init.h
@@ -0,0 +1,39 @@
+/*	$NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)init.h	8.2 (Berkeley) 5/4/95
+ */
+
+void init(void);
+void reset(void);
+void initshellproc(void);
diff --git a/sh/input.c b/sh/input.c
new file mode 100644
index 0000000..a81fd7b
--- /dev/null
+++ b/sh/input.c
@@ -0,0 +1,531 @@
+/*	$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
+#else
+__RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdio.h>	/* defines BUFSIZ */
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This file implements the input routines used by the parser.
+ */
+
+#include "shell.h"
+#include "redir.h"
+#include "syntax.h"
+#include "input.h"
+#include "output.h"
+#include "options.h"
+#include "memalloc.h"
+#include "error.h"
+#include "alias.h"
+#include "parser.h"
+#include "myhistedit.h"
+
+#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
+
+MKINIT
+struct strpush {
+	struct strpush *prev;	/* preceding string on stack */
+	char *prevstring;
+	int prevnleft;
+	int prevlleft;
+	struct alias *ap;	/* if push was associated with an alias */
+};
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+
+MKINIT
+struct parsefile {
+	struct parsefile *prev;	/* preceding file on stack */
+	int linno;		/* current line */
+	int fd;			/* file descriptor (or -1 if string) */
+	int nleft;		/* number of chars left in this line */
+	int lleft;		/* number of chars left in this buffer */
+	char *nextc;		/* next char in buffer */
+	char *buf;		/* input buffer */
+	struct strpush *strpush; /* for pushing strings at this level */
+	struct strpush basestrpush; /* so pushing one is fast */
+};
+
+
+int plinno = 1;			/* input line number */
+int parsenleft;			/* copy of parsefile->nleft */
+MKINIT int parselleft;		/* copy of parsefile->lleft */
+char *parsenextc;		/* copy of parsefile->nextc */
+MKINIT struct parsefile basepf;	/* top level input file */
+MKINIT char basebuf[BUFSIZ];	/* buffer for top level input file */
+struct parsefile *parsefile = &basepf;	/* current input file */
+int init_editline = 0;		/* editline library initialized? */
+int whichprompt;		/* 1 == PS1, 2 == PS2 */
+
+#if WITH_HISTORY
+EditLine *el;			/* cookie for editline package */
+#endif
+
+STATIC void pushfile(void);
+static int preadfd(void);
+
+#ifdef mkinit
+INCLUDE <stdio.h>
+INCLUDE "input.h"
+INCLUDE "error.h"
+
+INIT {
+	basepf.nextc = basepf.buf = basebuf;
+}
+
+RESET {
+	if (exception != EXSHELLPROC)
+		parselleft = parsenleft = 0;	/* clear input buffer */
+	popallfiles();
+}
+
+SHELLPROC {
+	popallfiles();
+}
+#endif
+
+
+/*
+ * Read a line from the script.
+ */
+
+char *
+pfgets(char *line, int len)
+{
+	char *p = line;
+	int nleft = len;
+	int c;
+
+	while (--nleft > 0) {
+		c = pgetc_macro();
+		if (c == PEOF) {
+			if (p == line)
+				return NULL;
+			break;
+		}
+		*p++ = c;
+		if (c == '\n')
+			break;
+	}
+	*p = '\0';
+	return line;
+}
+
+
+
+/*
+ * Read a character from the script, returning PEOF on end of file.
+ * Nul characters in the input are silently discarded.
+ */
+
+int
+pgetc(void)
+{
+	return pgetc_macro();
+}
+
+
+static int
+preadfd(void)
+{
+	int nr;
+	char *buf =  parsefile->buf;
+	parsenextc = buf;
+
+retry:
+#ifdef WITH_HISTORY
+	if (parsefile->fd == 0 && el) {
+		static const char *rl_cp;
+		static int el_len;
+
+		if (rl_cp == NULL)
+			rl_cp = el_gets(el, &el_len);
+		if (rl_cp == NULL)
+			nr = 0;
+		else {
+			nr = el_len;
+			if (nr > BUFSIZ - 8)
+				nr = BUFSIZ - 8;
+			memcpy(buf, rl_cp, nr);
+			if (nr != el_len) {
+				el_len -= nr;
+				rl_cp += nr;
+			} else
+				rl_cp = 0;
+		}
+
+	} else
+#endif
+		nr = read(parsefile->fd, buf, BUFSIZ - 8);
+
+
+	if (nr <= 0) {
+                if (nr < 0) {
+                        if (errno == EINTR)
+                                goto retry;
+                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
+                                int flags = fcntl(0, F_GETFL, 0);
+                                if (flags >= 0 && flags & O_NONBLOCK) {
+                                        flags &=~ O_NONBLOCK;
+                                        if (fcntl(0, F_SETFL, flags) >= 0) {
+						out2str("sh: turning off NDELAY mode\n");
+                                                goto retry;
+                                        }
+                                }
+                        }
+                }
+                nr = -1;
+	}
+	return nr;
+}
+
+/*
+ * Refill the input buffer and return the next input character:
+ *
+ * 1) If a string was pushed back on the input, pop it;
+ * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
+ *    from a string so we can't refill the buffer, return EOF.
+ * 3) If the is more stuff in this buffer, use it else call read to fill it.
+ * 4) Process input up to the next newline, deleting nul characters.
+ */
+
+int
+preadbuffer(void)
+{
+	char *p, *q;
+	int more;
+	int something;
+	char savec;
+
+	if (parsefile->strpush) {
+		popstring();
+		if (--parsenleft >= 0)
+			return (*parsenextc++);
+	}
+	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+		return PEOF;
+	flushout(&output);
+	flushout(&errout);
+
+again:
+	if (parselleft <= 0) {
+		if ((parselleft = preadfd()) == -1) {
+			parselleft = parsenleft = EOF_NLEFT;
+			return PEOF;
+		}
+	}
+
+	q = p = parsenextc;
+
+	/* delete nul characters */
+	something = 0;
+	for (more = 1; more;) {
+		switch (*p) {
+		case '\0':
+			p++;	/* Skip nul */
+			goto check;
+
+		case '\t':
+		case ' ':
+			break;
+
+		case '\n':
+			parsenleft = q - parsenextc;
+			more = 0; /* Stop processing here */
+			break;
+
+		default:
+			something = 1;
+			break;
+		}
+
+		*q++ = *p++;
+check:
+		if (--parselleft <= 0) {
+			parsenleft = q - parsenextc - 1;
+			if (parsenleft < 0)
+				goto again;
+			*q = '\0';
+			more = 0;
+		}
+	}
+
+	savec = *q;
+	*q = '\0';
+
+#ifdef WITH_HISTORY
+	if (parsefile->fd == 0 && hist && something) {
+		HistEvent he;
+		INTOFF;
+		history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND,
+		    parsenextc);
+		INTON;
+	}
+#endif
+
+	if (vflag) {
+		out2str(parsenextc);
+		flushout(out2);
+	}
+
+	*q = savec;
+
+	return *parsenextc++;
+}
+
+/*
+ * Undo the last call to pgetc.  Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+
+void
+pungetc(void)
+{
+	parsenleft++;
+	parsenextc--;
+}
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+void
+pushstring(char *s, int len, void *ap)
+{
+	struct strpush *sp;
+
+	INTOFF;
+/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
+	if (parsefile->strpush) {
+		sp = ckmalloc(sizeof (struct strpush));
+		sp->prev = parsefile->strpush;
+		parsefile->strpush = sp;
+	} else
+		sp = parsefile->strpush = &(parsefile->basestrpush);
+	sp->prevstring = parsenextc;
+	sp->prevnleft = parsenleft;
+	sp->prevlleft = parselleft;
+	sp->ap = (struct alias *)ap;
+	if (ap)
+		((struct alias *)ap)->flag |= ALIASINUSE;
+	parsenextc = s;
+	parsenleft = len;
+	INTON;
+}
+
+void
+popstring(void)
+{
+	struct strpush *sp = parsefile->strpush;
+
+	INTOFF;
+	parsenextc = sp->prevstring;
+	parsenleft = sp->prevnleft;
+	parselleft = sp->prevlleft;
+/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+	if (sp->ap)
+		sp->ap->flag &= ~ALIASINUSE;
+	parsefile->strpush = sp->prev;
+	if (sp != &(parsefile->basestrpush))
+		ckfree(sp);
+	INTON;
+}
+
+/*
+ * Set the input to take input from a file.  If push is set, push the
+ * old input onto the stack first.
+ */
+
+void
+setinputfile(const char *fname, int push)
+{
+	int fd;
+	int fd2;
+
+	INTOFF;
+	if ((fd = open(fname, O_RDONLY)) < 0)
+		error("Can't open %s", fname);
+	if (fd < 10) {
+		fd2 = copyfd(fd, 10);
+		close(fd);
+		if (fd2 < 0)
+			error("Out of file descriptors");
+		fd = fd2;
+	}
+	setinputfd(fd, push);
+	INTON;
+}
+
+
+/*
+ * Like setinputfile, but takes an open file descriptor.  Call this with
+ * interrupts off.
+ */
+
+void
+setinputfd(int fd, int push)
+{
+	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+	if (push) {
+		pushfile();
+		parsefile->buf = ckmalloc(BUFSIZ);
+	}
+	if (parsefile->fd > 0)
+		close(parsefile->fd);
+	parsefile->fd = fd;
+	if (parsefile->buf == NULL)
+		parsefile->buf = ckmalloc(BUFSIZ);
+	parselleft = parsenleft = 0;
+	plinno = 1;
+}
+
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+
+void
+setinputstring(char *string, int push)
+{
+	INTOFF;
+	if (push)
+		pushfile();
+	parsenextc = string;
+	parselleft = parsenleft = strlen(string);
+	parsefile->buf = NULL;
+	plinno = 1;
+	INTON;
+}
+
+
+
+/*
+ * To handle the "." command, a stack of input files is used.  Pushfile
+ * adds a new entry to the stack and popfile restores the previous level.
+ */
+
+STATIC void
+pushfile(void)
+{
+	struct parsefile *pf;
+
+	parsefile->nleft = parsenleft;
+	parsefile->lleft = parselleft;
+	parsefile->nextc = parsenextc;
+	parsefile->linno = plinno;
+	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
+	pf->prev = parsefile;
+	pf->fd = -1;
+	pf->strpush = NULL;
+	pf->basestrpush.prev = NULL;
+	parsefile = pf;
+}
+
+
+void
+popfile(void)
+{
+	struct parsefile *pf = parsefile;
+
+	INTOFF;
+	if (pf->fd >= 0)
+		close(pf->fd);
+	if (pf->buf)
+		ckfree(pf->buf);
+	while (pf->strpush)
+		popstring();
+	parsefile = pf->prev;
+	ckfree(pf);
+	parsenleft = parsefile->nleft;
+	parselleft = parsefile->lleft;
+	parsenextc = parsefile->nextc;
+	plinno = parsefile->linno;
+	INTON;
+}
+
+
+/*
+ * Return to top level.
+ */
+
+void
+popallfiles(void)
+{
+	while (parsefile != &basepf)
+		popfile();
+}
+
+
+
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ *
+ * Takes one arg, vfork, which tells it to not modify its global vars
+ * as it is still running in the parent.
+ *
+ * This code is (probably) unnecessary as the 'close on exec' flag is
+ * set and should be enough.  In the vfork case it is definitely wrong
+ * to close the fds as another fork() may be done later to feed data
+ * from a 'here' document into a pipe and we don't want to close the
+ * pipe!
+ */
+
+void
+closescript(int vforked)
+{
+	if (vforked)
+		return;
+	popallfiles();
+	if (parsefile->fd > 0) {
+		close(parsefile->fd);
+		parsefile->fd = 0;
+	}
+}
diff --git a/sh/input.h b/sh/input.h
new file mode 100644
index 0000000..a9d3a12
--- /dev/null
+++ b/sh/input.h
@@ -0,0 +1,62 @@
+/*	$NetBSD: input.h,v 1.15 2003/08/07 09:05:33 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)input.h	8.2 (Berkeley) 5/4/95
+ */
+
+/* PEOF (the end of file marker) is defined in syntax.h */
+
+/*
+ * The input line number.  Input.c just defines this variable, and saves
+ * and restores it when files are pushed and popped.  The user of this
+ * package must set its value.
+ */
+extern int plinno;
+extern int parsenleft;		/* number of characters left in input buffer */
+extern char *parsenextc;	/* next character in input buffer */
+extern int init_editline;	/* 0 == not setup, 1 == OK, -1 == failed */
+
+char *pfgets(char *, int);
+int pgetc(void);
+int preadbuffer(void);
+void pungetc(void);
+void pushstring(char *, int, void *);
+void popstring(void);
+void setinputfile(const char *, int);
+void setinputfd(int, int);
+void setinputstring(char *, int);
+void popfile(void);
+void popallfiles(void);
+void closescript(int);
+
+#define pgetc_macro()	(--parsenleft >= 0? *parsenextc++ : preadbuffer())
diff --git a/sh/jobs.c b/sh/jobs.c
new file mode 100644
index 0000000..b9460b0
--- /dev/null
+++ b/sh/jobs.c
@@ -0,0 +1,1487 @@
+/*	$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)jobs.c	8.5 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#define _PATH_DEVNULL "/dev/null"
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef BSD
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#include <sys/wait.h>
+#define killpg(s,i) kill(-(s),i)
+#include <sys/ioctl.h>
+
+#include "shell.h"
+#if JOBS
+#if OLD_TTY_DRIVER
+#include "sgtty.h"
+#else
+#include <termios.h>
+#endif
+#undef CEOF			/* syntax.h redefines this */
+#endif
+#include "redir.h"
+#include "show.h"
+#include "main.h"
+#include "parser.h"
+#include "nodes.h"
+#include "jobs.h"
+#include "options.h"
+#include "trap.h"
+#include "syntax.h"
+#include "input.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+
+// Use of process groups is disabled to allow adb shell children to terminate when the shell dies
+#define USE_PROCESS_GROUPS
+
+
+static struct job *jobtab;		/* array of jobs */
+static int njobs;			/* size of array */
+static int jobs_invalid;		/* set in child */
+MKINIT pid_t backgndpid = -1;	/* pid of last background process */
+#if JOBS
+int initialpgrp;		/* pgrp of shell on invocation */
+static int curjob = -1;		/* current job */
+#endif
+static int ttyfd = -1;
+
+STATIC void restartjob(struct job *);
+STATIC void freejob(struct job *);
+STATIC struct job *getjob(const char *, int);
+STATIC int dowait(int, struct job *);
+STATIC int onsigchild(void);
+STATIC int waitproc(int, struct job *, int *);
+STATIC void cmdtxt(union node *);
+STATIC void cmdlist(union node *, int);
+STATIC void cmdputs(const char *);
+
+#ifdef OLD_TTY_DRIVER
+static pid_t tcgetpgrp(int fd);
+static int tcsetpgrp(int fd, pid_t pgrp);
+
+static pid_t
+tcgetpgrp(int fd)
+{
+	pid_t pgrp;
+	if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1)
+		return -1;
+	else
+		return pgrp;
+}
+
+static int
+tcsetpgrp(int fd, pid_tpgrp)
+{
+	return ioctl(fd, TIOCSPGRP, (char *)&pgrp);
+}
+#endif
+
+/*
+ * Turn job control on and off.
+ *
+ * Note:  This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V.  Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ */
+
+MKINIT int jobctl;
+
+void
+setjobctl(int on)
+{
+#ifdef OLD_TTY_DRIVER
+	int ldisc;
+#endif
+
+	if (on == jobctl || rootshell == 0)
+		return;
+	if (on) {
+#if defined(FIOCLEX) || defined(FD_CLOEXEC)
+		int err;
+		int i;
+		if (ttyfd != -1)
+			close(ttyfd);
+		if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) {
+			for (i = 0; i < 3; i++) {
+				if (isatty(i) && (ttyfd = dup(i)) != -1)
+					break;
+			}
+			if (i == 3)
+				goto out;
+		}
+		/* Move to a high fd */
+		for (i = 10; i > 2; i--) {
+			if ((err = fcntl(ttyfd, F_DUPFD, (1 << i) - 1)) != -1)
+				break;
+		}
+		if (err != -1) {
+			close(ttyfd);
+			ttyfd = err;
+		}
+#ifdef FIOCLEX
+		err = ioctl(ttyfd, FIOCLEX, 0);
+#elif FD_CLOEXEC
+		err = fcntl(ttyfd, F_SETFD,
+		    fcntl(ttyfd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+		if (err == -1) {
+			close(ttyfd);
+			ttyfd = -1;
+			goto out;
+		}
+#else
+		out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control");
+		goto out;
+#endif
+		do { /* while we are in the background */
+			if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) {
+out:
+				out2str("sh: can't access tty; job control turned off\n");
+				mflag = 0;
+				return;
+			}
+			if (initialpgrp == -1)
+				initialpgrp = getpgrp();
+			else if (initialpgrp != getpgrp()) {
+				killpg(0, SIGTTIN);
+				continue;
+			}
+		} while (0);
+
+#ifdef OLD_TTY_DRIVER
+		if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0
+		    || ldisc != NTTYDISC) {
+			out2str("sh: need new tty driver to run job control; job control turned off\n");
+			mflag = 0;
+			return;
+		}
+#endif
+		setsignal(SIGTSTP, 0);
+		setsignal(SIGTTOU, 0);
+		setsignal(SIGTTIN, 0);
+#ifdef USE_PROCESS_GROUPS
+		if (getpgid(0) != rootpid && setpgid(0, rootpid) == -1)
+			error("Cannot set process group (%s) at %d",
+			    strerror(errno), __LINE__);
+		if (tcsetpgrp(ttyfd, rootpid) == -1)
+			error("Cannot set tty process group (%s) at %d",
+			    strerror(errno), __LINE__);
+#endif
+	} else { /* turning job control off */
+#ifdef USE_PROCESS_GROUPS
+		if (getpgid(0) != initialpgrp && setpgid(0, initialpgrp) == -1)
+			error("Cannot set process group (%s) at %d",
+			    strerror(errno), __LINE__);
+		if (tcsetpgrp(ttyfd, initialpgrp) == -1)
+			error("Cannot set tty process group (%s) at %d",
+			    strerror(errno), __LINE__);
+#endif
+		close(ttyfd);
+		ttyfd = -1;
+		setsignal(SIGTSTP, 0);
+		setsignal(SIGTTOU, 0);
+		setsignal(SIGTTIN, 0);
+	}
+	jobctl = on;
+}
+
+
+#ifdef mkinit
+INCLUDE <stdlib.h>
+
+SHELLPROC {
+	backgndpid = -1;
+#if JOBS
+	jobctl = 0;
+#endif
+}
+
+#endif
+
+
+
+#if JOBS
+int
+fgcmd(int argc, char **argv)
+{
+	struct job *jp;
+	int i;
+	int status;
+
+	nextopt("");
+	jp = getjob(*argptr, 0);
+	if (jp->jobctl == 0)
+		error("job not created under job control");
+	out1fmt("%s", jp->ps[0].cmd);
+	for (i = 1; i < jp->nprocs; i++)
+		out1fmt(" | %s", jp->ps[i].cmd );
+	out1c('\n');
+	flushall();
+
+	for (i = 0; i < jp->nprocs; i++)
+	    if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1)
+		    break;
+
+	if (i >= jp->nprocs) {
+		error("Cannot set tty process group (%s) at %d",
+		    strerror(errno), __LINE__);
+	}
+	restartjob(jp);
+	INTOFF;
+	status = waitforjob(jp);
+	INTON;
+	return status;
+}
+
+static void
+set_curjob(struct job *jp, int mode)
+{
+	struct job *jp1, *jp2;
+	int i, ji;
+
+	ji = jp - jobtab;
+
+	/* first remove from list */
+	if (ji == curjob)
+		curjob = jp->prev_job;
+	else {
+		for (i = 0; i < njobs; i++) {
+			if (jobtab[i].prev_job != ji)
+				continue;
+			jobtab[i].prev_job = jp->prev_job;
+			break;
+		}
+	}
+
+	/* Then re-insert in correct position */
+	switch (mode) {
+	case 0:	/* job being deleted */
+		jp->prev_job = -1;
+		break;
+	case 1:	/* newly created job or backgrounded job,
+		   put after all stopped jobs. */
+		if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) {
+			for (jp1 = jobtab + curjob; ; jp1 = jp2) {
+				if (jp1->prev_job == -1)
+					break;
+				jp2 = jobtab + jp1->prev_job;
+				if (jp2->state != JOBSTOPPED)
+					break;
+			}
+			jp->prev_job = jp1->prev_job;
+			jp1->prev_job = ji;
+			break;
+		}
+		/* FALLTHROUGH */
+	case 2:	/* newly stopped job - becomes curjob */
+		jp->prev_job = curjob;
+		curjob = ji;
+		break;
+	}
+}
+
+int
+bgcmd(int argc, char **argv)
+{
+	struct job *jp;
+	int i;
+
+	nextopt("");
+	do {
+		jp = getjob(*argptr, 0);
+		if (jp->jobctl == 0)
+			error("job not created under job control");
+		set_curjob(jp, 1);
+		out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd);
+		for (i = 1; i < jp->nprocs; i++)
+			out1fmt(" | %s", jp->ps[i].cmd );
+		out1c('\n');
+		flushall();
+		restartjob(jp);
+	} while (*argptr && *++argptr);
+	return 0;
+}
+
+
+STATIC void
+restartjob(struct job *jp)
+{
+	struct procstat *ps;
+	int i;
+
+	if (jp->state == JOBDONE)
+		return;
+	INTOFF;
+	for (i = 0; i < jp->nprocs; i++)
+		if (killpg(jp->ps[i].pid, SIGCONT) != -1)
+			break;
+	if (i >= jp->nprocs)
+		error("Cannot continue job (%s)", strerror(errno));
+	for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
+		if (WIFSTOPPED(ps->status)) {
+			ps->status = -1;
+			jp->state = JOBRUNNING;
+		}
+	}
+	INTON;
+}
+#endif
+
+static void
+showjob(struct output *out, struct job *jp, int mode)
+{
+	int procno;
+	int st;
+	struct procstat *ps;
+	int col;
+	char s[64];
+
+#if JOBS
+	if (mode & SHOW_PGID) {
+		/* just output process (group) id of pipeline */
+		outfmt(out, "%ld\n", (long)jp->ps->pid);
+		return;
+	}
+#endif
+
+	procno = jp->nprocs;
+	if (!procno)
+		return;
+
+	if (mode & SHOW_PID)
+		mode |= SHOW_MULTILINE;
+
+	if ((procno > 1 && !(mode & SHOW_MULTILINE))
+	    || (mode & SHOW_SIGNALLED)) {
+		/* See if we have more than one status to report */
+		ps = jp->ps;
+		st = ps->status;
+		do {
+			int st1 = ps->status;
+			if (st1 != st)
+				/* yes - need multi-line output */
+				mode |= SHOW_MULTILINE;
+			if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1))
+				continue;
+			if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f)
+			    && st1 != SIGINT && st1 != SIGPIPE))
+				mode |= SHOW_ISSIG;
+
+		} while (ps++, --procno);
+		procno = jp->nprocs;
+	}
+
+	if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) {
+		if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) {
+			TRACE(("showjob: freeing job %d\n", jp - jobtab + 1));
+			freejob(jp);
+		}
+		return;
+	}
+
+	for (ps = jp->ps; --procno >= 0; ps++) {	/* for each process */
+		if (ps == jp->ps)
+			fmtstr(s, 16, "[%ld] %c ",
+				(long)(jp - jobtab + 1),
+#if JOBS
+				jp == jobtab + curjob ? '+' :
+				curjob != -1 && jp == jobtab +
+					    jobtab[curjob].prev_job ? '-' :
+#endif
+				' ');
+		else
+			fmtstr(s, 16, "      " );
+		col = strlen(s);
+		if (mode & SHOW_PID) {
+			fmtstr(s + col, 16, "%ld ", (long)ps->pid);
+			     col += strlen(s + col);
+		}
+		if (ps->status == -1) {
+			scopy("Running", s + col);
+		} else if (WIFEXITED(ps->status)) {
+			st = WEXITSTATUS(ps->status);
+			if (st)
+				fmtstr(s + col, 16, "Done(%d)", st);
+			else
+				fmtstr(s + col, 16, "Done");
+		} else {
+#if JOBS
+			if (WIFSTOPPED(ps->status)) 
+				st = WSTOPSIG(ps->status);
+			else /* WIFSIGNALED(ps->status) */
+#endif
+				st = WTERMSIG(ps->status);
+			st &= 0x7f;
+			if (st < NSIG && sys_siglist[st])
+				scopyn(sys_siglist[st], s + col, 32);
+			else
+				fmtstr(s + col, 16, "Signal %d", st);
+			if (WCOREDUMP(ps->status)) {
+				col += strlen(s + col);
+				scopyn(" (core dumped)", s + col,  64 - col);
+			}
+		}
+		col += strlen(s + col);
+		outstr(s, out);
+		do {
+			outc(' ', out);
+			col++;
+		} while (col < 30);
+		outstr(ps->cmd, out);
+		if (mode & SHOW_MULTILINE) {
+			if (procno > 0) {
+				outc(' ', out);
+				outc('|', out);
+			}
+		} else {
+			while (--procno >= 0)
+				outfmt(out, " | %s", (++ps)->cmd );
+		}
+		outc('\n', out);
+	}
+	flushout(out);
+	jp->changed = 0;
+	if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE))
+		freejob(jp);
+}
+
+
+int
+jobscmd(int argc, char **argv)
+{
+	int mode, m;
+	int sv = jobs_invalid;
+
+	jobs_invalid = 0;
+	mode = 0;
+	while ((m = nextopt("lp")))
+		if (m == 'l')
+			mode = SHOW_PID;
+		else
+			mode = SHOW_PGID;
+	if (*argptr)
+		do
+			showjob(out1, getjob(*argptr,0), mode);
+		while (*++argptr);
+	else
+		showjobs(out1, mode);
+	jobs_invalid = sv;
+	return 0;
+}
+
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ *
+ * If the shell is interrupted in the process of creating a job, the
+ * result may be a job structure containing zero processes.  Such structures
+ * will be freed here.
+ */
+
+void
+showjobs(struct output *out, int mode)
+{
+	int jobno;
+	struct job *jp;
+	int silent = 0, gotpid;
+
+	TRACE(("showjobs(%x) called\n", mode));
+
+	/* If not even one one job changed, there is nothing to do */
+	gotpid = dowait(0, NULL);
+	while (dowait(0, NULL) > 0)
+		continue;
+#ifdef JOBS
+	/*
+	 * Check if we are not in our foreground group, and if not
+	 * put us in it.
+	 */
+	if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) {
+		if (tcsetpgrp(ttyfd, getpid()) == -1)
+			error("Cannot set tty process group (%s) at %d",
+			    strerror(errno), __LINE__);
+		TRACE(("repaired tty process group\n"));
+		silent = 1;
+	}
+#endif
+	if (jobs_invalid)
+		return;
+
+	for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
+		if (!jp->used)
+			continue;
+		if (jp->nprocs == 0) {
+			freejob(jp);
+			continue;
+		}
+		if ((mode & SHOW_CHANGED) && !jp->changed)
+			continue;
+		if (silent && jp->changed) {
+			jp->changed = 0;
+			continue;
+		}
+		showjob(out, jp, mode);
+	}
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+
+STATIC void
+freejob(struct job *jp)
+{
+	INTOFF;
+	if (jp->ps != &jp->ps0) {
+		ckfree(jp->ps);
+		jp->ps = &jp->ps0;
+	}
+	jp->nprocs = 0;
+	jp->used = 0;
+#if JOBS
+	set_curjob(jp, 0);
+#endif
+	INTON;
+}
+
+
+
+int
+waitcmd(int argc, char **argv)
+{
+	struct job *job;
+	int status, retval = 127;
+	struct job *jp;
+
+	nextopt("");
+
+	if (!*argptr) {
+		/* wait for all jobs */
+		jp = jobtab;
+		if (jobs_invalid)
+			return 0;
+		for (;;) {
+			if (jp >= jobtab + njobs) {
+				/* no running procs */
+				return 0;
+			}
+			if (!jp->used || jp->state != JOBRUNNING) {
+				jp++;
+				continue;
+			}
+			if (dowait(1, (struct job *)NULL) == -1)
+			       return 128 + SIGINT;
+			jp = jobtab;
+		}
+	}
+
+	for (; *argptr; argptr++) {
+		job = getjob(*argptr, 1);
+		if (!job) {
+			retval = 127;
+			continue;
+		}
+		/* loop until process terminated or stopped */
+		while (job->state == JOBRUNNING) {
+			if (dowait(1, (struct job *)NULL) == -1)
+			       return 128 + SIGINT;
+		}
+		status = job->ps[job->nprocs].status;
+		if (WIFEXITED(status))
+			retval = WEXITSTATUS(status);
+#if JOBS
+		else if (WIFSTOPPED(status))
+			retval = WSTOPSIG(status) + 128;
+#endif
+		else {
+			/* XXX: limits number of signals */
+			retval = WTERMSIG(status) + 128;
+		}
+		if (!iflag)
+			freejob(job);
+	}
+	return retval;
+}
+
+
+
+int
+jobidcmd(int argc, char **argv)
+{
+	struct job *jp;
+	int i;
+
+	nextopt("");
+	jp = getjob(*argptr, 0);
+	for (i = 0 ; i < jp->nprocs ; ) {
+		out1fmt("%ld", (long)jp->ps[i].pid);
+		out1c(++i < jp->nprocs ? ' ' : '\n');
+	}
+	return 0;
+}
+
+int
+getjobpgrp(const char *name)
+{
+	struct job *jp;
+
+	jp = getjob(name, 1);
+	if (jp == 0)
+		return 0;
+	return -jp->ps[0].pid;
+}
+
+/*
+ * Convert a job name to a job structure.
+ */
+
+STATIC struct job *
+getjob(const char *name, int noerror)
+{
+	int jobno = -1;
+	struct job *jp;
+	int pid;
+	int i;
+	const char *err_msg = "No such job: %s";
+		
+	if (name == NULL) {
+#if JOBS
+		jobno = curjob;
+#endif
+		err_msg = "No current job";
+	} else if (name[0] == '%') {
+		if (is_number(name + 1)) {
+			jobno = number(name + 1) - 1;
+		} else if (!name[2]) {
+			switch (name[1]) {
+#if JOBS
+			case 0:
+			case '+':
+			case '%':
+				jobno = curjob;
+				err_msg = "No current job";
+				break;
+			case '-':
+				jobno = curjob;
+				if (jobno != -1)
+					jobno = jobtab[jobno].prev_job;
+				err_msg = "No previous job";
+				break;
+#endif
+			default:
+				goto check_pattern;
+			}
+		} else {
+			struct job *found;
+    check_pattern:
+			found = NULL;
+			for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+				if (!jp->used || jp->nprocs <= 0)
+					continue;
+				if ((name[1] == '?'
+					&& strstr(jp->ps[0].cmd, name + 2))
+				    || prefix(name + 1, jp->ps[0].cmd)) {
+					if (found) {
+						err_msg = "%s: ambiguous";
+						found = 0;
+						break;
+					}
+					found = jp;
+				}
+			}
+			if (found)
+				return found;
+		}
+
+	} else if (is_number(name)) {
+		pid = number(name);
+		for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+			if (jp->used && jp->nprocs > 0
+			 && jp->ps[jp->nprocs - 1].pid == pid)
+				return jp;
+		}
+	}
+
+	if (!jobs_invalid && jobno >= 0 && jobno < njobs) {
+		jp = jobtab + jobno;
+		if (jp->used)
+			return jp;
+	}
+	if (!noerror)
+		error(err_msg, name);
+	return 0;
+}
+
+
+
+/*
+ * Return a new job structure,
+ */
+
+struct job *
+makejob(union node *node, int nprocs)
+{
+	int i;
+	struct job *jp;
+
+	if (jobs_invalid) {
+		for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) {
+			if (jp->used)
+				freejob(jp);
+		}
+		jobs_invalid = 0;
+	}
+
+	for (i = njobs, jp = jobtab ; ; jp++) {
+		if (--i < 0) {
+			INTOFF;
+			if (njobs == 0) {
+				jobtab = ckmalloc(4 * sizeof jobtab[0]);
+			} else {
+				jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
+				memcpy(jp, jobtab, njobs * sizeof jp[0]);
+				/* Relocate `ps' pointers */
+				for (i = 0; i < njobs; i++)
+					if (jp[i].ps == &jobtab[i].ps0)
+						jp[i].ps = &jp[i].ps0;
+				ckfree(jobtab);
+				jobtab = jp;
+			}
+			jp = jobtab + njobs;
+			for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
+			INTON;
+			break;
+		}
+		if (jp->used == 0)
+			break;
+	}
+	INTOFF;
+	jp->state = JOBRUNNING;
+	jp->used = 1;
+	jp->changed = 0;
+	jp->nprocs = 0;
+#if JOBS
+	jp->jobctl = jobctl;
+	set_curjob(jp, 1);
+#endif
+	if (nprocs > 1) {
+		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+	} else {
+		jp->ps = &jp->ps0;
+	}
+	INTON;
+	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+	    jp - jobtab + 1));
+	return jp;
+}
+
+
+/*
+ * Fork off a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *	FORK_FG - Fork off a foreground process.
+ *	FORK_BG - Fork off a background process.
+ *	FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *		     process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ */
+
+int
+forkshell(struct job *jp, union node *n, int mode)
+{
+	int pid;
+
+	TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode));
+	switch ((pid = fork())) {
+	case -1:
+		TRACE(("Fork failed, errno=%d\n", errno));
+		INTON;
+		error("Cannot fork");
+		break;
+	case 0:
+		forkchild(jp, n, mode, 0);
+		return 0;
+	default:
+		return forkparent(jp, n, mode, pid);
+	}
+}
+
+int
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+	int pgrp;
+
+	if (rootshell && mode != FORK_NOJOB && mflag) {
+		if (jp == NULL || jp->nprocs == 0)
+			pgrp = pid;
+		else
+			pgrp = jp->ps[0].pid;
+#ifdef USE_PROCESS_GROUPS
+		/* This can fail because we are doing it in the child also */
+		(void)setpgid(pid, pgrp);
+#endif
+	}
+	if (mode == FORK_BG)
+		backgndpid = pid;		/* set $! */
+	if (jp) {
+		struct procstat *ps = &jp->ps[jp->nprocs++];
+		ps->pid = pid;
+		ps->status = -1;
+		ps->cmd[0] = 0;
+		if (/* iflag && rootshell && */ n)
+			commandtext(ps, n);
+	}
+	TRACE(("In parent shell:  child = %d\n", pid));
+	return pid;
+}
+
+void
+forkchild(struct job *jp, union node *n, int mode, int vforked)
+{
+	int wasroot;
+	int pgrp;
+	const char *devnull = _PATH_DEVNULL;
+	const char *nullerr = "Can't open %s";
+
+	wasroot = rootshell;
+	TRACE(("Child shell %d\n", getpid()));
+	if (!vforked)
+		rootshell = 0;
+
+	closescript(vforked);
+	clear_traps(vforked);
+#if JOBS
+	if (!vforked)
+		jobctl = 0;		/* do job control only in root shell */
+	if (wasroot && mode != FORK_NOJOB && mflag) {
+		if (jp == NULL || jp->nprocs == 0)
+			pgrp = getpid();
+		else
+			pgrp = jp->ps[0].pid;
+#ifdef USE_PROCESS_GROUPS
+		/* This can fail because we are doing it in the parent also */
+		(void)setpgid(0, pgrp);
+		if (mode == FORK_FG) {
+			if (tcsetpgrp(ttyfd, pgrp) == -1)
+				error("Cannot set tty process group (%s) at %d",
+				    strerror(errno), __LINE__);
+		}
+#endif
+		setsignal(SIGTSTP, vforked);
+		setsignal(SIGTTOU, vforked);
+	} else if (mode == FORK_BG) {
+		ignoresig(SIGINT, vforked);
+		ignoresig(SIGQUIT, vforked);
+		if ((jp == NULL || jp->nprocs == 0) &&
+		    ! fd0_redirected_p ()) {
+			close(0);
+			if (open(devnull, O_RDONLY) != 0)
+				error(nullerr, devnull);
+		}
+	}
+#else
+	if (mode == FORK_BG) {
+		ignoresig(SIGINT, vforked);
+		ignoresig(SIGQUIT, vforked);
+		if ((jp == NULL || jp->nprocs == 0) &&
+		    ! fd0_redirected_p ()) {
+			close(0);
+			if (open(devnull, O_RDONLY) != 0)
+				error(nullerr, devnull);
+		}
+	}
+#endif
+	if (wasroot && iflag) {
+		setsignal(SIGINT, vforked);
+		setsignal(SIGQUIT, vforked);
+		setsignal(SIGTERM, vforked);
+	}
+
+	if (!vforked)
+		jobs_invalid = 1;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an inter-
+ * active user may be hard to kill.  With job control turned off, an
+ * interactive user may place an interactive program inside a loop.  If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * forground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ */
+
+int
+waitforjob(struct job *jp)
+{
+#if JOBS
+	int mypgrp = getpgrp();
+#endif
+	int status;
+	int st;
+
+	INTOFF;
+	TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
+	while (jp->state == JOBRUNNING) {
+		dowait(1, jp);
+	}
+#if JOBS
+	if (jp->jobctl) {
+		if (tcsetpgrp(ttyfd, mypgrp) == -1)
+			error("Cannot set tty process group (%s) at %d",
+			    strerror(errno), __LINE__);
+	}
+	if (jp->state == JOBSTOPPED && curjob != jp - jobtab)
+		set_curjob(jp, 2);
+#endif
+	status = jp->ps[jp->nprocs - 1].status;
+	/* convert to 8 bits */
+	if (WIFEXITED(status))
+		st = WEXITSTATUS(status);
+#if JOBS
+	else if (WIFSTOPPED(status))
+		st = WSTOPSIG(status) + 128;
+#endif
+	else
+		st = WTERMSIG(status) + 128;
+	TRACE(("waitforjob: job %d, nproc %d, status %x, st %x\n",
+		jp - jobtab + 1, jp->nprocs, status, st ));
+#if JOBS
+	if (jp->jobctl) {
+		/*
+		 * This is truly gross.
+		 * If we're doing job control, then we did a TIOCSPGRP which
+		 * caused us (the shell) to no longer be in the controlling
+		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
+		 * intuit from the subprocess exit status whether a SIGINT
+		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
+		 */
+		if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
+			raise(SIGINT);
+	}
+#endif
+	if (! JOBS || jp->state == JOBDONE)
+		freejob(jp);
+	INTON;
+	return st;
+}
+
+
+
+/*
+ * Wait for a process to terminate.
+ */
+
+STATIC int
+dowait(int block, struct job *job)
+{
+	int pid;
+	int status;
+	struct procstat *sp;
+	struct job *jp;
+	struct job *thisjob;
+	int done;
+	int stopped;
+	extern volatile char gotsig[];
+
+	TRACE(("dowait(%d) called\n", block));
+	do {
+		pid = waitproc(block, job, &status);
+		TRACE(("wait returns pid %d, status %d\n", pid, status));
+	} while (pid == -1 && errno == EINTR && gotsig[SIGINT - 1] == 0);
+	if (pid <= 0)
+		return pid;
+	INTOFF;
+	thisjob = NULL;
+	for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
+		if (jp->used) {
+			done = 1;
+			stopped = 1;
+			for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
+				if (sp->pid == -1)
+					continue;
+				if (sp->pid == pid) {
+					TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jp - jobtab + 1, pid, sp->status, status));
+					sp->status = status;
+					thisjob = jp;
+				}
+				if (sp->status == -1)
+					stopped = 0;
+				else if (WIFSTOPPED(sp->status))
+					done = 0;
+			}
+			if (stopped) {		/* stopped or done */
+				int state = done ? JOBDONE : JOBSTOPPED;
+				if (jp->state != state) {
+					TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
+					jp->state = state;
+#if JOBS
+					if (done)
+						set_curjob(jp, 0);
+#endif
+				}
+			}
+		}
+	}
+
+	if (thisjob && thisjob->state != JOBRUNNING) {
+		int mode = 0;
+		if (!rootshell || !iflag)
+			mode = SHOW_SIGNALLED;
+		if (job == thisjob)
+			mode = SHOW_SIGNALLED | SHOW_NO_FREE;
+		if (mode)
+			showjob(out2, thisjob, mode);
+		else {
+			TRACE(("Not printing status, rootshell=%d, job=%p\n",
+				rootshell, job));
+			thisjob->changed = 1;
+		}
+	}
+
+	INTON;
+	return pid;
+}
+
+
+
+/*
+ * Do a wait system call.  If job control is compiled in, we accept
+ * stopped processes.  If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call.  It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies.  The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process.  Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD.  What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler.  The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called.  If there are any
+ * children to be waited for, it will be.
+ *
+ * If neither SYSV nor BSD is defined, we don't implement nonblocking
+ * waits at all.  In this case, the user will not be informed when
+ * a background process until the next time she runs a real program
+ * (as opposed to running a builtin command or just typing return),
+ * and the jobs command may give out of date information.
+ */
+
+#ifdef SYSV
+STATIC int gotsigchild;
+
+STATIC int onsigchild() {
+	gotsigchild = 1;
+}
+#endif
+
+
+STATIC int
+waitproc(int block, struct job *jp, int *status)
+{
+#ifdef BSD
+	int flags = 0;
+
+#if JOBS
+	if (jp != NULL && jp->jobctl)
+		flags |= WUNTRACED;
+#endif
+	if (block == 0)
+		flags |= WNOHANG;
+	return wait3(status, flags, (struct rusage *)NULL);
+#else
+#ifdef SYSV
+	int (*save)();
+
+	if (block == 0) {
+		gotsigchild = 0;
+		save = signal(SIGCLD, onsigchild);
+		signal(SIGCLD, save);
+		if (gotsigchild == 0)
+			return 0;
+	}
+	return wait(status);
+#else
+	if (block == 0)
+		return 0;
+	return wait(status);
+#endif
+#endif
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+int job_warning = 0;
+int
+stoppedjobs(void)
+{
+	int jobno;
+	struct job *jp;
+
+	if (job_warning || jobs_invalid)
+		return (0);
+	for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
+		if (jp->used == 0)
+			continue;
+		if (jp->state == JOBSTOPPED) {
+			out2str("You have stopped jobs.\n");
+			job_warning = 2;
+			return (1);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
+ */
+
+STATIC char *cmdnextc;
+STATIC int cmdnleft;
+
+void
+commandtext(struct procstat *ps, union node *n)
+{
+	int len;
+
+	cmdnextc = ps->cmd;
+	if (iflag || mflag || sizeof ps->cmd < 100)
+		len = sizeof(ps->cmd);
+	else
+		len = sizeof(ps->cmd) / 10;
+	cmdnleft = len;
+	cmdtxt(n);
+	if (cmdnleft <= 0) {
+		char *p = ps->cmd + len - 4;
+		p[0] = '.';
+		p[1] = '.';
+		p[2] = '.';
+		p[3] = 0;
+	} else
+		*cmdnextc = '\0';
+	TRACE(("commandtext: ps->cmd %x, end %x, left %d\n\t\"%s\"\n",
+		ps->cmd, cmdnextc, cmdnleft, ps->cmd));
+}
+
+
+STATIC void
+cmdtxt(union node *n)
+{
+	union node *np;
+	struct nodelist *lp;
+	const char *p;
+	int i;
+	char s[2];
+
+	if (n == NULL || cmdnleft <= 0)
+		return;
+	switch (n->type) {
+	case NSEMI:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs("; ");
+		cmdtxt(n->nbinary.ch2);
+		break;
+	case NAND:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs(" && ");
+		cmdtxt(n->nbinary.ch2);
+		break;
+	case NOR:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs(" || ");
+		cmdtxt(n->nbinary.ch2);
+		break;
+	case NPIPE:
+		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+			cmdtxt(lp->n);
+			if (lp->next)
+				cmdputs(" | ");
+		}
+		break;
+	case NSUBSHELL:
+		cmdputs("(");
+		cmdtxt(n->nredir.n);
+		cmdputs(")");
+		break;
+	case NREDIR:
+	case NBACKGND:
+		cmdtxt(n->nredir.n);
+		break;
+	case NIF:
+		cmdputs("if ");
+		cmdtxt(n->nif.test);
+		cmdputs("; then ");
+		cmdtxt(n->nif.ifpart);
+		if (n->nif.elsepart) {
+			cmdputs("; else ");
+			cmdtxt(n->nif.elsepart);
+		}
+		cmdputs("; fi");
+		break;
+	case NWHILE:
+		cmdputs("while ");
+		goto until;
+	case NUNTIL:
+		cmdputs("until ");
+until:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs("; do ");
+		cmdtxt(n->nbinary.ch2);
+		cmdputs("; done");
+		break;
+	case NFOR:
+		cmdputs("for ");
+		cmdputs(n->nfor.var);
+		cmdputs(" in ");
+		cmdlist(n->nfor.args, 1);
+		cmdputs("; do ");
+		cmdtxt(n->nfor.body);
+		cmdputs("; done");
+		break;
+	case NCASE:
+		cmdputs("case ");
+		cmdputs(n->ncase.expr->narg.text);
+		cmdputs(" in ");
+		for (np = n->ncase.cases; np; np = np->nclist.next) {
+			cmdtxt(np->nclist.pattern);
+			cmdputs(") ");
+			cmdtxt(np->nclist.body);
+			cmdputs(";; ");
+		}
+		cmdputs("esac");
+		break;
+	case NDEFUN:
+		cmdputs(n->narg.text);
+		cmdputs("() { ... }");
+		break;
+	case NCMD:
+		cmdlist(n->ncmd.args, 1);
+		cmdlist(n->ncmd.redirect, 0);
+		break;
+	case NARG:
+		cmdputs(n->narg.text);
+		break;
+	case NTO:
+		p = ">";  i = 1;  goto redir;
+	case NCLOBBER:
+		p = ">|";  i = 1;  goto redir;
+	case NAPPEND:
+		p = ">>";  i = 1;  goto redir;
+	case NTOFD:
+		p = ">&";  i = 1;  goto redir;
+	case NFROM:
+		p = "<";  i = 0;  goto redir;
+	case NFROMFD:
+		p = "<&";  i = 0;  goto redir;
+	case NFROMTO:
+		p = "<>";  i = 0;  goto redir;
+redir:
+		if (n->nfile.fd != i) {
+			s[0] = n->nfile.fd + '0';
+			s[1] = '\0';
+			cmdputs(s);
+		}
+		cmdputs(p);
+		if (n->type == NTOFD || n->type == NFROMFD) {
+			s[0] = n->ndup.dupfd + '0';
+			s[1] = '\0';
+			cmdputs(s);
+		} else {
+			cmdtxt(n->nfile.fname);
+		}
+		break;
+	case NHERE:
+	case NXHERE:
+		cmdputs("<<...");
+		break;
+	default:
+		cmdputs("???");
+		break;
+	}
+}
+
+STATIC void
+cmdlist(union node *np, int sep)
+{
+	for (; np; np = np->narg.next) {
+		if (!sep)
+			cmdputs(" ");
+		cmdtxt(np);
+		if (sep && np->narg.next)
+			cmdputs(" ");
+	}
+}
+
+
+STATIC void
+cmdputs(const char *s)
+{
+	const char *p, *str = 0;
+	char c, cc[2] = " ";
+	char *nextc;
+	int nleft;
+	int subtype = 0;
+	int quoted = 0;
+	static char vstype[16][4] = { "", "}", "-", "+", "?", "=",
+					"#", "##", "%", "%%" };
+
+	p = s;
+	nextc = cmdnextc;
+	nleft = cmdnleft;
+	while (nleft > 0 && (c = *p++) != 0) {
+		switch (c) {
+		case CTLESC:
+			c = *p++;
+			break;
+		case CTLVAR:
+			subtype = *p++;
+			if ((subtype & VSTYPE) == VSLENGTH)
+				str = "${#";
+			else
+				str = "${";
+			if (!(subtype & VSQUOTE) != !(quoted & 1)) {
+				quoted ^= 1;
+				c = '"';
+			} else
+				c = *str++;
+			break;
+		case CTLENDVAR:
+			if (quoted & 1) {
+				c = '"';
+				str = "}";
+			} else
+				c = '}';
+			quoted >>= 1;
+			subtype = 0;
+			break;
+		case CTLBACKQ:
+			c = '$';
+			str = "(...)";
+			break;
+		case CTLBACKQ+CTLQUOTE:
+			c = '"';
+			str = "$(...)\"";
+			break;
+		case CTLARI:
+			c = '$';
+			str = "((";
+			break;
+		case CTLENDARI:
+			c = ')';
+			str = ")";
+			break;
+		case CTLQUOTEMARK:
+			quoted ^= 1;
+			c = '"';
+			break;
+		case '=':
+			if (subtype == 0)
+				break;
+			str = vstype[subtype & VSTYPE];
+			if (subtype & VSNUL)
+				c = ':';
+			else
+				c = *str++;
+			if (c != '}')
+				quoted <<= 1;
+			break;
+		case '\'':
+		case '\\':
+		case '"':
+		case '$':
+			/* These can only happen inside quotes */
+			cc[0] = c;
+			str = cc;
+			c = '\\';
+			break;
+		default:
+			break;
+		}
+		do {
+			*nextc++ = c;
+		} while (--nleft > 0 && str && (c = *str++));
+		str = 0;
+	}
+	if ((quoted & 1) && nleft) {
+		*nextc++ = '"';
+		nleft--;
+	}
+	cmdnleft = nleft;
+	cmdnextc = nextc;
+}
diff --git a/sh/jobs.h b/sh/jobs.h
new file mode 100644
index 0000000..47e76c2
--- /dev/null
+++ b/sh/jobs.h
@@ -0,0 +1,106 @@
+/*	$NetBSD: jobs.h,v 1.19 2003/11/27 21:16:14 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)jobs.h	8.2 (Berkeley) 5/4/95
+ */
+
+#include "output.h"
+
+/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
+#define FORK_FG 0
+#define FORK_BG 1
+#define FORK_NOJOB 2
+
+/* mode flags for showjob(s) */
+#define	SHOW_PGID	0x01	/* only show pgid - for jobs -p */
+#define	SHOW_MULTILINE	0x02	/* one line per process */
+#define	SHOW_PID	0x04	/* include process pid */
+#define	SHOW_CHANGED	0x08	/* only jobs whose state has changed */
+#define	SHOW_SIGNALLED	0x10	/* only if stopped/exited on signal */
+#define	SHOW_ISSIG	0x20	/* job was signalled */
+#define	SHOW_NO_FREE	0x40	/* do not free job */
+
+
+/*
+ * A job structure contains information about a job.  A job is either a
+ * single process or a set of processes contained in a pipeline.  In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+#define	MAXCMDTEXT	200
+
+struct procstat {
+	pid_t	pid;		/* process id */
+ 	int	status;		/* last process status from wait() */
+ 	char	cmd[MAXCMDTEXT];/* text of command being run */
+};
+
+struct job {
+	struct procstat ps0;	/* status of process */
+	struct procstat *ps;	/* status or processes when more than one */
+	int	nprocs;		/* number of processes */
+	pid_t	pgrp;		/* process group of this job */
+	char	state;
+#define	JOBRUNNING	0	/* at least one proc running */
+#define	JOBSTOPPED	1	/* all procs are stopped */
+#define	JOBDONE		2	/* all procs are completed */
+	char	used;		/* true if this entry is in used */
+	char	changed;	/* true if status has changed */
+#if JOBS
+	char 	jobctl;		/* job running under job control */
+	int	prev_job;	/* previous job index */
+#endif
+};
+
+extern pid_t backgndpid;	/* pid of last background process */
+extern int job_warning;		/* user was warned about stopped jobs */
+
+void setjobctl(int);
+int fgcmd(int, char **);
+int bgcmd(int, char **);
+int jobscmd(int, char **);
+void showjobs(struct output *, int);
+int waitcmd(int, char **);
+int jobidcmd(int, char **);
+struct job *makejob(union node *, int);
+int forkshell(struct job *, union node *, int);
+void forkchild(struct job *, union node *, int, int);
+int forkparent(struct job *, union node *, int, pid_t);
+int waitforjob(struct job *);
+int stoppedjobs(void);
+void commandtext(struct procstat *, union node *);
+int getjobpgrp(const char *);
+
+#if ! JOBS
+#define setjobctl(on)	/* do nothing */
+#endif
diff --git a/sh/machdep.h b/sh/machdep.h
new file mode 100644
index 0000000..14e803b
--- /dev/null
+++ b/sh/machdep.h
@@ -0,0 +1,47 @@
+/*	$NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)machdep.h	8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * Most machines require the value returned from malloc to be aligned
+ * in some way.  The following macro will get this right on many machines.
+ */
+
+#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
+/*
+ * It appears that grabstackstr() will barf with such alignments
+ * because stalloc() will return a string allocated in a new stackblock.
+ */
+#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
diff --git a/sh/main.c b/sh/main.c
new file mode 100644
index 0000000..43b154f
--- /dev/null
+++ b/sh/main.c
@@ -0,0 +1,394 @@
+/*	$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993\n\
+	The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c	8.7 (Berkeley) 7/19/95";
+#else
+__RCSID("$NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $");
+#endif
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+#include "shell.h"
+#include "main.h"
+#include "options.h"
+#include "output.h"
+#include "parser.h"
+#include "nodes.h"
+#include "expand.h"
+#include "eval.h"
+#include "jobs.h"
+#include "input.h"
+#include "trap.h"
+#include "var.h"
+#include "show.h"
+#include "memalloc.h"
+#include "error.h"
+#include "init.h"
+#include "mystring.h"
+#include "exec.h"
+#include "cd.h"
+
+#define PROFILE 0
+
+int rootpid;
+int rootshell;
+STATIC union node *curcmd;
+STATIC union node *prevcmd;
+#if PROFILE
+short profile_buf[16384];
+extern int etext();
+#endif
+
+STATIC void read_profile(const char *);
+STATIC char *find_dot_file(char *);
+int main(int, char **);
+
+/*
+ * Main routine.  We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands.  The setjmp call sets up the location to jump to when an
+ * exception occurs.  When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+
+int
+main(int argc, char **argv)
+{
+	struct jmploc jmploc;
+	struct stackmark smark;
+	volatile int state;
+	char *shinit;
+
+#if PROFILE
+	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
+#endif
+	state = 0;
+	if (setjmp(jmploc.loc)) {
+		/*
+		 * When a shell procedure is executed, we raise the
+		 * exception EXSHELLPROC to clean up before executing
+		 * the shell procedure.
+		 */
+		switch (exception) {
+		case EXSHELLPROC:
+			rootpid = getpid();
+			rootshell = 1;
+			minusc = NULL;
+			state = 3;
+			break;
+
+		case EXEXEC:
+			exitstatus = exerrno;
+			break;
+
+		case EXERROR:
+			exitstatus = 2;
+			break;
+
+		default:
+			break;
+		}
+
+		if (exception != EXSHELLPROC) {
+			if (state == 0 || iflag == 0 || ! rootshell)
+				exitshell(exitstatus);
+		}
+		reset();
+		if (exception == EXINT
+#if ATTY
+		 && (! attyset() || equal(termval(), "emacs"))
+#endif
+		 ) {
+			out2c('\n');
+			flushout(&errout);
+		}
+		popstackmark(&smark);
+		FORCEINTON;				/* enable interrupts */
+		if (state == 1)
+			goto state1;
+		else if (state == 2)
+			goto state2;
+		else if (state == 3)
+			goto state3;
+		else
+			goto state4;
+	}
+	handler = &jmploc;
+#ifdef DEBUG
+#if DEBUG == 2
+	debug = 1;
+#endif
+	opentrace();
+	trputs("Shell args:  ");  trargs(argv);
+#endif
+	rootpid = getpid();
+	rootshell = 1;
+	init();
+	setstackmark(&smark);
+	procargs(argc, argv);
+	if (argv[0] && argv[0][0] == '-') {
+		state = 1;
+		read_profile("/etc/profile");
+state1:
+		state = 2;
+		read_profile(".profile");
+	}
+state2:
+	state = 3;
+	if (getuid() == geteuid() && getgid() == getegid()) {
+		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
+			state = 3;
+			read_profile(shinit);
+		}
+	}
+state3:
+	state = 4;
+	if (sflag == 0 || minusc) {
+		static int sigs[] =  {
+		    SIGINT, SIGQUIT, SIGHUP, 
+#ifdef SIGTSTP
+		    SIGTSTP,
+#endif
+		    SIGPIPE
+		};
+#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+		int i;
+
+		for (i = 0; i < SIGSSIZE; i++)
+		    setsignal(sigs[i], 0);
+	}
+
+	if (minusc)
+		evalstring(minusc, 0);
+
+	if (sflag || minusc == NULL) {
+state4:	/* XXX ??? - why isn't this before the "if" statement */
+		cmdloop(1);
+	}
+#if PROFILE
+	monitor(0);
+#endif
+	exitshell(exitstatus);
+	/* NOTREACHED */
+}
+
+
+/*
+ * Read and execute commands.  "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
+ */
+
+void
+cmdloop(int top)
+{
+	union node *n;
+	struct stackmark smark;
+	int inter;
+	int numeof = 0;
+
+	TRACE(("cmdloop(%d) called\n", top));
+	setstackmark(&smark);
+	for (;;) {
+		if (pendingsigs)
+			dotrap();
+		inter = 0;
+		if (iflag && top) {
+			inter = 1;
+			showjobs(out2, SHOW_CHANGED);
+			flushout(&errout);
+		}
+		n = parsecmd(inter);
+		/* showtree(n); DEBUG */
+		if (n == NEOF) {
+			if (!top || numeof >= 50)
+				break;
+			if (!stoppedjobs()) {
+				if (!Iflag)
+					break;
+				out2str("\nUse \"exit\" to leave shell.\n");
+			}
+			numeof++;
+		} else if (n != NULL && nflag == 0) {
+			job_warning = (job_warning == 2) ? 1 : 0;
+			numeof = 0;
+			evaltree(n, 0);
+		}
+		popstackmark(&smark);
+		setstackmark(&smark);
+		if (evalskip == SKIPFILE) {
+			evalskip = 0;
+			break;
+		}
+	}
+	popstackmark(&smark);
+}
+
+
+
+/*
+ * Read /etc/profile or .profile.  Return on error.
+ */
+
+STATIC void
+read_profile(const char *name)
+{
+	int fd;
+	int xflag_set = 0;
+	int vflag_set = 0;
+
+	INTOFF;
+	if ((fd = open(name, O_RDONLY)) >= 0)
+		setinputfd(fd, 1);
+	INTON;
+	if (fd < 0)
+		return;
+	/* -q turns off -x and -v just when executing init files */
+	if (qflag)  {
+	    if (xflag)
+		    xflag = 0, xflag_set = 1;
+	    if (vflag)
+		    vflag = 0, vflag_set = 1;
+	}
+	cmdloop(0);
+	if (qflag)  {
+	    if (xflag_set)
+		    xflag = 1;
+	    if (vflag_set)
+		    vflag = 1;
+	}
+	popfile();
+}
+
+
+
+/*
+ * Read a file containing shell functions.
+ */
+
+void
+readcmdfile(char *name)
+{
+	int fd;
+
+	INTOFF;
+	if ((fd = open(name, O_RDONLY)) >= 0)
+		setinputfd(fd, 1);
+	else
+		error("Can't open %s", name);
+	INTON;
+	cmdloop(0);
+	popfile();
+}
+
+
+
+/*
+ * Take commands from a file.  To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+
+
+STATIC char *
+find_dot_file(char *basename)
+{
+	char *fullname;
+	const char *path = pathval();
+	struct stat statb;
+
+	/* don't try this for absolute or relative paths */
+	if (strchr(basename, '/'))
+		return basename;
+
+	while ((fullname = padvance(&path, basename)) != NULL) {
+		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+			/*
+			 * Don't bother freeing here, since it will
+			 * be freed by the caller.
+			 */
+			return fullname;
+		}
+		stunalloc(fullname);
+	}
+
+	/* not found in the PATH */
+	error("%s: not found", basename);
+	/* NOTREACHED */
+}
+
+int
+dotcmd(int argc, char **argv)
+{
+	exitstatus = 0;
+
+	if (argc >= 2) {		/* That's what SVR2 does */
+		char *fullname;
+		struct stackmark smark;
+
+		setstackmark(&smark);
+		fullname = find_dot_file(argv[1]);
+		setinputfile(fullname, 1);
+		commandname = fullname;
+		cmdloop(0);
+		popfile();
+		popstackmark(&smark);
+	}
+	return exitstatus;
+}
+
+
+int
+exitcmd(int argc, char **argv)
+{
+	if (stoppedjobs())
+		return 0;
+	if (argc > 1)
+		exitstatus = number(argv[1]);
+	exitshell(exitstatus);
+	/* NOTREACHED */
+}
diff --git a/sh/main.h b/sh/main.h
new file mode 100644
index 0000000..d198e2d
--- /dev/null
+++ b/sh/main.h
@@ -0,0 +1,43 @@
+/*	$NetBSD: main.h,v 1.10 2003/08/07 09:05:34 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)main.h	8.2 (Berkeley) 5/4/95
+ */
+
+extern int rootpid;	/* pid of main shell */
+extern int rootshell;	/* true if we aren't a child of the main shell */
+
+void readcmdfile(char *);
+void cmdloop(int);
+int dotcmd(int, char **);
+int exitcmd(int, char **);
diff --git a/sh/memalloc.c b/sh/memalloc.c
new file mode 100644
index 0000000..07c14db
--- /dev/null
+++ b/sh/memalloc.c
@@ -0,0 +1,307 @@
+/*	$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)memalloc.c	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "shell.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "machdep.h"
+#include "mystring.h"
+
+/*
+ * Like malloc, but returns an error when out of space.
+ */
+
+pointer
+ckmalloc(int nbytes)
+{
+	pointer p;
+
+	p = malloc(nbytes);
+	if (p == NULL)
+		error("Out of space");
+	return p;
+}
+
+
+/*
+ * Same for realloc.
+ */
+
+pointer
+ckrealloc(pointer p, int nbytes)
+{
+	p = realloc(p, nbytes);
+	if (p == NULL)
+		error("Out of space");
+	return p;
+}
+
+
+/*
+ * Make a copy of a string in safe storage.
+ */
+
+char *
+savestr(const char *s)
+{
+	char *p;
+
+	p = ckmalloc(strlen(s) + 1);
+	scopy(s, p);
+	return p;
+}
+
+
+/*
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
+ */
+
+#define MINSIZE 504		/* minimum size of a block */
+
+struct stack_block {
+	struct stack_block *prev;
+	char space[MINSIZE];
+};
+
+struct stack_block stackbase;
+struct stack_block *stackp = &stackbase;
+struct stackmark *markp;
+char *stacknxt = stackbase.space;
+int stacknleft = MINSIZE;
+int sstrnleft;
+int herefd = -1;
+
+pointer
+stalloc(int nbytes)
+{
+	char *p;
+
+	nbytes = SHELL_ALIGN(nbytes);
+	if (nbytes > stacknleft) {
+		int blocksize;
+		struct stack_block *sp;
+
+		blocksize = nbytes;
+		if (blocksize < MINSIZE)
+			blocksize = MINSIZE;
+		INTOFF;
+		sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
+		sp->prev = stackp;
+		stacknxt = sp->space;
+		stacknleft = blocksize;
+		stackp = sp;
+		INTON;
+	}
+	p = stacknxt;
+	stacknxt += nbytes;
+	stacknleft -= nbytes;
+	return p;
+}
+
+
+void
+stunalloc(pointer p)
+{
+	if (p == NULL) {		/*DEBUG */
+		write(2, "stunalloc\n", 10);
+		abort();
+	}
+	stacknleft += stacknxt - (char *)p;
+	stacknxt = p;
+}
+
+
+
+void
+setstackmark(struct stackmark *mark)
+{
+	mark->stackp = stackp;
+	mark->stacknxt = stacknxt;
+	mark->stacknleft = stacknleft;
+	mark->marknext = markp;
+	markp = mark;
+}
+
+
+void
+popstackmark(struct stackmark *mark)
+{
+	struct stack_block *sp;
+
+	INTOFF;
+	markp = mark->marknext;
+	while (stackp != mark->stackp) {
+		sp = stackp;
+		stackp = sp->prev;
+		ckfree(sp);
+	}
+	stacknxt = mark->stacknxt;
+	stacknleft = mark->stacknleft;
+	INTON;
+}
+
+
+/*
+ * When the parser reads in a string, it wants to stick the string on the
+ * stack and only adjust the stack pointer when it knows how big the
+ * string is.  Stackblock (defined in stack.h) returns a pointer to a block
+ * of space on top of the stack and stackblocklen returns the length of
+ * this block.  Growstackblock will grow this space by at least one byte,
+ * possibly moving it (like realloc).  Grabstackblock actually allocates the
+ * part of the block that has been used.
+ */
+
+void
+growstackblock(void)
+{
+	int newlen = SHELL_ALIGN(stacknleft * 2 + 100);
+
+	if (stacknxt == stackp->space && stackp != &stackbase) {
+		struct stack_block *oldstackp;
+		struct stackmark *xmark;
+		struct stack_block *sp;
+
+		INTOFF;
+		oldstackp = stackp;
+		sp = stackp;
+		stackp = sp->prev;
+		sp = ckrealloc((pointer)sp,
+		    sizeof(struct stack_block) - MINSIZE + newlen);
+		sp->prev = stackp;
+		stackp = sp;
+		stacknxt = sp->space;
+		stacknleft = newlen;
+
+		/*
+		 * Stack marks pointing to the start of the old block
+		 * must be relocated to point to the new block 
+		 */
+		xmark = markp;
+		while (xmark != NULL && xmark->stackp == oldstackp) {
+			xmark->stackp = stackp;
+			xmark->stacknxt = stacknxt;
+			xmark->stacknleft = stacknleft;
+			xmark = xmark->marknext;
+		}
+		INTON;
+	} else {
+		char *oldspace = stacknxt;
+		int oldlen = stacknleft;
+		char *p = stalloc(newlen);
+
+		(void)memcpy(p, oldspace, oldlen);
+		stacknxt = p;			/* free the space */
+		stacknleft += newlen;		/* we just allocated */
+	}
+}
+
+void
+grabstackblock(int len)
+{
+	len = SHELL_ALIGN(len);
+	stacknxt += len;
+	stacknleft -= len;
+}
+
+/*
+ * The following routines are somewhat easier to use than the above.
+ * The user declares a variable of type STACKSTR, which may be declared
+ * to be a register.  The macro STARTSTACKSTR initializes things.  Then
+ * the user uses the macro STPUTC to add characters to the string.  In
+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
+ * grown as necessary.  When the user is done, she can just leave the
+ * string there and refer to it using stackblock().  Or she can allocate
+ * the space for it using grabstackstr().  If it is necessary to allow
+ * someone else to use the stack temporarily and then continue to grow
+ * the string, the user should use grabstack to allocate the space, and
+ * then call ungrabstr(p) to return to the previous mode of operation.
+ *
+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
+ * is space for at least one character.
+ */
+
+char *
+growstackstr(void)
+{
+	int len = stackblocksize();
+	if (herefd >= 0 && len >= 1024) {
+		xwrite(herefd, stackblock(), len);
+		sstrnleft = len - 1;
+		return stackblock();
+	}
+	growstackblock();
+	sstrnleft = stackblocksize() - len - 1;
+	return stackblock() + len;
+}
+
+/*
+ * Called from CHECKSTRSPACE.
+ */
+
+char *
+makestrspace(void)
+{
+	int len = stackblocksize() - sstrnleft;
+	growstackblock();
+	sstrnleft = stackblocksize() - len;
+	return stackblock() + len;
+}
+
+void
+ungrabstackstr(char *s, char *p)
+{
+	stacknleft += stacknxt - s;
+	stacknxt = s;
+	sstrnleft = stacknleft - (p - s);
+
+}
diff --git a/sh/memalloc.h b/sh/memalloc.h
new file mode 100644
index 0000000..e793880
--- /dev/null
+++ b/sh/memalloc.h
@@ -0,0 +1,77 @@
+/*	$NetBSD: memalloc.h,v 1.14 2003/08/07 09:05:34 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)memalloc.h	8.2 (Berkeley) 5/4/95
+ */
+
+struct stackmark {
+	struct stack_block *stackp;
+	char *stacknxt;
+	int stacknleft;
+	struct stackmark *marknext;
+};
+
+
+extern char *stacknxt;
+extern int stacknleft;
+extern int sstrnleft;
+extern int herefd;
+
+pointer ckmalloc(int);
+pointer ckrealloc(pointer, int);
+char *savestr(const char *);
+pointer stalloc(int);
+void stunalloc(pointer);
+void setstackmark(struct stackmark *);
+void popstackmark(struct stackmark *);
+void growstackblock(void);
+void grabstackblock(int);
+char *growstackstr(void);
+char *makestrspace(void);
+void ungrabstackstr(char *, char *);
+
+
+
+#define stackblock() stacknxt
+#define stackblocksize() stacknleft
+#define STARTSTACKSTR(p)	p = stackblock(), sstrnleft = stackblocksize()
+#define STPUTC(c, p)	(--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
+#define CHECKSTRSPACE(n, p)	{ if (sstrnleft < n) p = makestrspace(); }
+#define USTPUTC(c, p)	(--sstrnleft, *p++ = (c))
+#define STACKSTRNUL(p)	(sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p)	(++sstrnleft, --p)
+#define STTOPC(p)	p[-1]
+#define STADJUST(amount, p)	(p += (amount), sstrnleft -= (amount))
+#define grabstackstr(p)	stalloc(stackblocksize() - sstrnleft)
+
+#define ckfree(p)	free((pointer)(p))
diff --git a/sh/miscbltin.c b/sh/miscbltin.c
new file mode 100644
index 0000000..1a8e252
--- /dev/null
+++ b/sh/miscbltin.c
@@ -0,0 +1,447 @@
+/*	$NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)miscbltin.c	8.4 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: miscbltin.c,v 1.34.2.1 2005/04/07 11:34:20 tron Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Miscelaneous builtins.
+ */
+
+#include <sys/types.h>		/* quad_t */
+#include <sys/param.h>		/* BSD4_4 */
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "shell.h"
+#include "options.h"
+#include "var.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "miscbltin.h"
+#include "mystring.h"
+
+#undef rflag
+
+
+
+/*
+ * The read builtin.
+ * Backslahes escape the next char unless -r is specified.
+ *
+ * This uses unbuffered input, which may be avoidable in some cases.
+ *
+ * Note that if IFS=' :' then read x y should work so that:
+ * 'a b'	x='a', y='b'
+ * ' a b '	x='a', y='b'
+ * ':b'		x='',  y='b'
+ * ':'		x='',  y=''
+ * '::'		x='',  y=''
+ * ': :'	x='',  y=''
+ * ':::'	x='',  y='::'
+ * ':b c:'	x='',  y='b c:'
+ */
+
+int
+readcmd(int argc, char **argv)
+{
+	char **ap;
+	char c;
+	int rflag;
+	char *prompt;
+	const char *ifs;
+	char *p;
+	int startword;
+	int status;
+	int i;
+	int is_ifs;
+	int saveall = 0;
+
+	rflag = 0;
+	prompt = NULL;
+	while ((i = nextopt("p:r")) != '\0') {
+		if (i == 'p')
+			prompt = optionarg;
+		else
+			rflag = 1;
+	}
+
+	if (prompt && isatty(0)) {
+		out2str(prompt);
+		flushall();
+	}
+
+	if (*(ap = argptr) == NULL)
+		error("arg count");
+
+	if ((ifs = bltinlookup("IFS", 1)) == NULL)
+		ifs = " \t\n";
+
+	status = 0;
+	startword = 2;
+	STARTSTACKSTR(p);
+	for (;;) {
+		if (read(0, &c, 1) != 1) {
+			status = 1;
+			break;
+		}
+		if (c == '\0')
+			continue;
+		if (c == '\\' && !rflag) {
+			if (read(0, &c, 1) != 1) {
+				status = 1;
+				break;
+			}
+			if (c != '\n')
+				STPUTC(c, p);
+			continue;
+		}
+		if (c == '\n')
+			break;
+		if (strchr(ifs, c))
+			is_ifs = strchr(" \t\n", c) ? 1 : 2;
+		else
+			is_ifs = 0;
+
+		if (startword != 0) {
+			if (is_ifs == 1) {
+				/* Ignore leading IFS whitespace */
+				if (saveall)
+					STPUTC(c, p);
+				continue;
+			}
+			if (is_ifs == 2 && startword == 1) {
+				/* Only one non-whitespace IFS per word */
+				startword = 2;
+				if (saveall)
+					STPUTC(c, p);
+				continue;
+			}
+		}
+
+		if (is_ifs == 0) {
+			/* append this character to the current variable */
+			startword = 0;
+			if (saveall)
+				/* Not just a spare terminator */
+				saveall++;
+			STPUTC(c, p);
+			continue;
+		}
+
+		/* end of variable... */
+		startword = is_ifs;
+
+		if (ap[1] == NULL) {
+			/* Last variable needs all IFS chars */
+			saveall++;
+			STPUTC(c, p);
+			continue;
+		}
+
+		STACKSTRNUL(p);
+		setvar(*ap, stackblock(), 0);
+		ap++;
+		STARTSTACKSTR(p);
+	}
+	STACKSTRNUL(p);
+
+	/* Remove trailing IFS chars */
+	for (; stackblock() <= --p; *p = 0) {
+		if (!strchr(ifs, *p))
+			break;
+		if (strchr(" \t\n", *p))
+			/* Always remove whitespace */
+			continue;
+		if (saveall > 1)
+			/* Don't remove non-whitespace unless it was naked */
+			break;
+	}
+	setvar(*ap, stackblock(), 0);
+
+	/* Set any remaining args to "" */
+	while (*++ap != NULL)
+		setvar(*ap, nullstr, 0);
+	return status;
+}
+
+
+
+int
+umaskcmd(int argc, char **argv)
+{
+	char *ap;
+	int mask;
+	int i;
+	int symbolic_mode = 0;
+
+	while ((i = nextopt("S")) != '\0') {
+		symbolic_mode = 1;
+	}
+
+	INTOFF;
+	mask = umask(0);
+	umask(mask);
+	INTON;
+
+	if ((ap = *argptr) == NULL) {
+		if (symbolic_mode) {
+			char u[4], g[4], o[4];
+
+			i = 0;
+			if ((mask & S_IRUSR) == 0)
+				u[i++] = 'r';
+			if ((mask & S_IWUSR) == 0)
+				u[i++] = 'w';
+			if ((mask & S_IXUSR) == 0)
+				u[i++] = 'x';
+			u[i] = '\0';
+
+			i = 0;
+			if ((mask & S_IRGRP) == 0)
+				g[i++] = 'r';
+			if ((mask & S_IWGRP) == 0)
+				g[i++] = 'w';
+			if ((mask & S_IXGRP) == 0)
+				g[i++] = 'x';
+			g[i] = '\0';
+
+			i = 0;
+			if ((mask & S_IROTH) == 0)
+				o[i++] = 'r';
+			if ((mask & S_IWOTH) == 0)
+				o[i++] = 'w';
+			if ((mask & S_IXOTH) == 0)
+				o[i++] = 'x';
+			o[i] = '\0';
+
+			out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+		} else {
+			out1fmt("%.4o\n", mask);
+		}
+	} else {
+		if (isdigit((unsigned char)*ap)) {
+			mask = 0;
+			do {
+				if (*ap >= '8' || *ap < '0')
+					error("Illegal number: %s", argv[1]);
+				mask = (mask << 3) + (*ap - '0');
+			} while (*++ap != '\0');
+			umask(mask);
+		} else
+			error("Illegal mode: %s", ap);
+	}
+	return 0;
+}
+
+typedef unsigned long rlim_t;
+
+#if 1
+/*
+ * ulimit builtin
+ *
+ * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
+ * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
+ * ash by J.T. Conklin.
+ *
+ * Public domain.
+ */
+
+struct limits {
+	const char *name;
+	int	cmd;
+	int	factor;	/* multiply by to get rlim_{cur,max} values */
+	char	option;
+};
+
+static const struct limits limits[] = {
+#ifdef RLIMIT_CPU
+	{ "time(seconds)",		RLIMIT_CPU,	   1, 't' },
+#endif
+#ifdef RLIMIT_FSIZE
+	{ "file(blocks)",		RLIMIT_FSIZE,	 512, 'f' },
+#endif
+#ifdef RLIMIT_DATA
+	{ "data(kbytes)",		RLIMIT_DATA,	1024, 'd' },
+#endif
+#ifdef RLIMIT_STACK
+	{ "stack(kbytes)",		RLIMIT_STACK,	1024, 's' },
+#endif
+#ifdef  RLIMIT_CORE
+	{ "coredump(blocks)",		RLIMIT_CORE,	 512, 'c' },
+#endif
+#ifdef RLIMIT_RSS
+	{ "memory(kbytes)",		RLIMIT_RSS,	1024, 'm' },
+#endif
+#ifdef RLIMIT_MEMLOCK
+	{ "locked memory(kbytes)",	RLIMIT_MEMLOCK, 1024, 'l' },
+#endif
+#ifdef RLIMIT_NPROC
+	{ "process(processes)",		RLIMIT_NPROC,      1, 'p' },
+#endif
+#ifdef RLIMIT_NOFILE
+	{ "nofiles(descriptors)",	RLIMIT_NOFILE,     1, 'n' },
+#endif
+#ifdef RLIMIT_VMEM
+	{ "vmemory(kbytes)",		RLIMIT_VMEM,	1024, 'v' },
+#endif
+#ifdef RLIMIT_SWAP
+	{ "swap(kbytes)",		RLIMIT_SWAP,	1024, 'w' },
+#endif
+#ifdef RLIMIT_SBSIZE
+	{ "sbsize(bytes)",		RLIMIT_SBSIZE,	   1, 'b' },
+#endif
+	{ (char *) 0,			0,		   0,  '\0' }
+};
+
+int
+ulimitcmd(int argc, char **argv)
+{
+	int	c;
+	rlim_t val = 0;
+	enum { SOFT = 0x1, HARD = 0x2 }
+			how = SOFT | HARD;
+	const struct limits	*l;
+	int		set, all = 0;
+	int		optc, what;
+	struct rlimit	limit;
+
+	what = 'f';
+	while ((optc = nextopt("HSabtfdsmcnpl")) != '\0')
+		switch (optc) {
+		case 'H':
+			how = HARD;
+			break;
+		case 'S':
+			how = SOFT;
+			break;
+		case 'a':
+			all = 1;
+			break;
+		default:
+			what = optc;
+		}
+
+	for (l = limits; l->name && l->option != what; l++)
+		;
+	if (!l->name)
+		error("internal error (%c)", what);
+
+	set = *argptr ? 1 : 0;
+	if (set) {
+		char *p = *argptr;
+
+		if (all || argptr[1])
+			error("too many arguments");
+		if (strcmp(p, "unlimited") == 0)
+			val = RLIM_INFINITY;
+		else {
+			val = (rlim_t) 0;
+
+			while ((c = *p++) >= '0' && c <= '9')
+			{
+				val = (val * 10) + (long)(c - '0');
+				if ((long)val < 0)
+					break;
+			}
+			if (c)
+				error("bad number");
+			val *= l->factor;
+		}
+	}
+	if (all) {
+		for (l = limits; l->name; l++) {
+			getrlimit(l->cmd, &limit);
+			if (how & SOFT)
+				val = limit.rlim_cur;
+			else if (how & HARD)
+				val = limit.rlim_max;
+
+			out1fmt("%-20s ", l->name);
+			if (val == RLIM_INFINITY)
+				out1fmt("unlimited\n");
+			else
+			{
+				val /= l->factor;
+#ifdef BSD4_4
+				out1fmt("%lld\n", (long long) val);
+#else
+				out1fmt("%ld\n", (long) val);
+#endif
+			}
+		}
+		return 0;
+	}
+
+	getrlimit(l->cmd, &limit);
+	if (set) {
+		if (how & HARD)
+			limit.rlim_max = val;
+		if (how & SOFT)
+			limit.rlim_cur = val;
+		if (setrlimit(l->cmd, &limit) < 0)
+			error("error setting limit (%s)", strerror(errno));
+	} else {
+		if (how & SOFT)
+			val = limit.rlim_cur;
+		else if (how & HARD)
+			val = limit.rlim_max;
+
+		if (val == RLIM_INFINITY)
+			out1fmt("unlimited\n");
+		else
+		{
+			val /= l->factor;
+#ifdef BSD4_4
+			out1fmt("%lld\n", (long long) val);
+#else
+			out1fmt("%ld\n", (long) val);
+#endif
+		}
+	}
+	return 0;
+}
+#endif
diff --git a/sh/miscbltin.h b/sh/miscbltin.h
new file mode 100644
index 0000000..4c12c82
--- /dev/null
+++ b/sh/miscbltin.h
@@ -0,0 +1,31 @@
+/*	$NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $	*/
+
+/*
+ * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+int readcmd(int, char **);
+int umaskcmd(int, char **);
+int ulimitcmd(int, char **);
diff --git a/sh/mkbuiltins b/sh/mkbuiltins
new file mode 100644
index 0000000..5b19269
--- /dev/null
+++ b/sh/mkbuiltins
@@ -0,0 +1,136 @@
+#!/bin/sh -
+#	$NetBSD: mkbuiltins,v 1.21 2004/06/06 07:03:11 christos Exp $
+#
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)mkbuiltins	8.2 (Berkeley) 5/4/95
+
+havehist=1
+if [ "X$1" = "X-h" ]; then
+	havehist=0
+	shift
+fi
+
+shell=$1
+builtins=$2
+objdir=$3
+
+havejobs=0
+if grep '^#define JOBS[	 ]*1' ${shell} > /dev/null
+then
+	havejobs=1
+fi
+
+exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h
+
+echo '/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#include "shell.h"
+#include "builtins.h"
+
+const struct builtincmd builtincmd[] = {
+' >&3
+
+echo '/*
+ * This file was generated by the mkbuiltins program.
+ */
+
+#include <sys/cdefs.h>
+
+struct builtincmd {
+      const char *name;
+      int (*builtin)(int, char **);
+};
+
+extern const struct builtincmd builtincmd[];
+extern const struct builtincmd splbltincmd[];
+
+' >&4
+
+specials=
+
+while read line
+do
+	set -- $line
+	[ -z "$1" ] && continue
+	case "$1" in
+	\#if*|\#def*|\#end*)
+		echo $line >&3
+		echo $line >&4
+		continue
+		;;
+	esac
+	l1="${line###}"
+	[ "$l1" != "$line" ] && continue
+
+
+	func=$1
+	shift
+	[ x"$1" = x'-j' ] && {
+		[ $havejobs = 0 ] && continue
+		shift
+	}
+	[ x"$1" = x'-h' ] && {
+		[ $havehist = 0 ] && continue
+		shift
+	}
+	echo 'int '"$func"'(int, char **);' >&4
+	while
+		[ $# != 0 -a "$1" != '#' ]
+	do
+		[ "$1" = '-s' ] && {
+			specials="$specials $2 $func"
+			shift 2
+			continue;
+		}
+		[ "$1" = '-u' ] && shift
+		echo '	{ "'$1'",	'"$func"' },' >&3
+		shift
+	done
+done
+
+echo '	{ 0, 0 },' >&3
+echo '};' >&3
+echo >&3
+echo 'const struct builtincmd splbltincmd[] = {' >&3
+
+set -- $specials
+while
+	[ $# != 0 ]
+do
+	echo '	{ "'$1'",	'"$2"' },' >&3
+	shift 2
+done
+
+echo '	{ 0, 0 },' >&3
+echo "};" >&3
diff --git a/sh/mkinit.sh b/sh/mkinit.sh
new file mode 100644
index 0000000..cae27dd
--- /dev/null
+++ b/sh/mkinit.sh
@@ -0,0 +1,197 @@
+#! /bin/sh
+#	$NetBSD: mkinit.sh,v 1.2 2004/06/15 23:09:54 dsl Exp $
+
+# Copyright (c) 2003 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by David Laight.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of The NetBSD Foundation nor the names of its
+#    contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+srcs="$*"
+
+nl='
+'
+openparen='('
+backslash='\'
+
+includes=' "shell.h" "mystring.h" "init.h" '
+defines=
+decles=
+event_init=
+event_reset=
+event_shellproc=
+
+for src in $srcs; do
+	exec <$src
+	decnl="$nl"
+	while IFS=; read -r line; do
+		[ "$line" = x ]
+		case "$line " in
+		INIT["{ 	"]* ) event=init;;
+		RESET["{ 	"]* ) event=reset;;
+		SHELLPROC["{ 	"]* ) event=shellproc;;
+		INCLUDE[\ \	]* )
+			IFS=' 	'
+			set -- $line
+			# ignore duplicates
+			[ "${includes}" != "${includes%* $2 }" ] && continue
+			includes="$includes$2 "
+			continue
+			;;
+		MKINIT\  )
+			# struct declaration
+			decles="$decles$nl"
+			while
+				read -r line
+				decles="${decles}${line}${nl}"
+				[ "$line" != "};" ]
+			do
+				:
+			done
+			decnl="$nl"
+			continue
+			;;
+		MKINIT["{ 	"]* )
+			# strip initialiser
+			def=${line#MKINIT}
+			comment="${def#*;}"
+			def="${def%;$comment}"
+			def="${def%%=*}"
+			def="${def% }"
+			decles="${decles}${decnl}extern${def};${comment}${nl}"
+			decnl=
+			continue
+			;;
+		\#define[\ \	]* )
+			IFS=' 	'
+			set -- $line
+			# Ignore those with arguments
+			[ "$2" = "${2##*$openparen}" ] || continue
+			# and multiline definitions
+			[ "$line" = "${line%$backslash}" ] || continue
+			defines="${defines}#undef  $2${nl}${line}${nl}"
+			continue
+			;;
+		* ) continue;;
+		esac
+		# code for events
+		ev="${nl}      /* from $src: */${nl}      {${nl}"
+		while
+			read -r line
+			[ "$line" != "}" ]
+		do
+			# The C program indented by an extra 6 chars using
+			# tabs then spaces. I need to compare the output :-(
+			indent=6
+			while
+				l=${line#	}
+				[ "$l" != "$line" ]
+			do
+				indent=$(($indent + 8))
+				line="$l"
+			done
+			while
+				l=${line# }
+				[ "$l" != "$line" ]
+			do
+				indent=$(($indent + 1))
+				line="$l"
+			done
+			[ -z "$line" -o "$line" != "${line###}" ] && indent=0
+			while
+				[ $indent -ge 8 ]
+			do
+				ev="$ev	"
+				indent="$(($indent - 8))"
+			done
+			while
+				[ $indent -gt 0 ]
+			do
+				ev="$ev "
+				indent="$(($indent - 1))"
+			done
+			ev="${ev}${line}${nl}"
+		done
+		ev="${ev}      }${nl}"
+		eval event_$event=\"\$event_$event\$ev\"
+	done
+done
+
+exec >init.c.tmp
+
+echo "/*"
+echo " * This file was generated by the mkinit program."
+echo " */"
+echo
+
+IFS=' '
+for f in $includes; do
+	echo "#include $f"
+done
+
+echo
+echo
+echo
+echo "$defines"
+echo
+echo "$decles"
+echo
+echo
+echo "/*"
+echo " * Initialization code."
+echo " */"
+echo
+echo "void"
+echo "init() {"
+echo "${event_init%$nl}"
+echo "}"
+echo
+echo
+echo
+echo "/*"
+echo " * This routine is called when an error or an interrupt occurs in an"
+echo " * interactive shell and control is returned to the main command loop."
+echo " */"
+echo
+echo "void"
+echo "reset() {"
+echo "${event_reset%$nl}"
+echo "}"
+echo
+echo
+echo
+echo "/*"
+echo " * This routine is called to initialize the shell to run a shell procedure."
+echo " */"
+echo
+echo "void"
+echo "initshellproc() {"
+echo "${event_shellproc%$nl}"
+echo "}"
+
+exec >&-
+mv init.c.tmp init.c
diff --git a/sh/mknodes.sh b/sh/mknodes.sh
new file mode 100644
index 0000000..54d2e3d
--- /dev/null
+++ b/sh/mknodes.sh
@@ -0,0 +1,217 @@
+#! /bin/sh
+#	$NetBSD: mknodes.sh,v 1.1 2004/01/16 23:24:38 dsl Exp $
+
+# Copyright (c) 2003 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by David Laight.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of The NetBSD Foundation nor the names of its
+#    contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+nodetypes=$1
+nodes_pat=$2
+objdir="$3"
+
+exec <$nodetypes
+exec >$objdir/nodes.h.tmp
+
+echo "/*"
+echo " * This file was generated by mknodes.sh"
+echo " */"
+echo
+
+tagno=0
+while IFS=; read -r line; do
+	line="${line%%#*}"
+	IFS=' 	'
+	set -- $line
+	IFS=
+	[ -z "$2" ] && continue
+	case "$line" in
+	[" 	"]* )
+		IFS=' '
+		[ $field = 0 ] && struct_list="$struct_list $struct"
+		eval field_${struct}_$field=\"\$*\"
+		eval numfld_$struct=\$field
+		field=$(($field + 1))
+		;;
+	* )
+		define=$1
+		struct=$2
+		echo "#define $define $tagno"
+		tagno=$(($tagno + 1))
+		eval define_$struct=\"\$define_$struct \$define\"
+		struct_define="$struct_define $struct"
+		field=0
+		;;
+	esac
+done
+
+echo
+
+IFS=' '
+for struct in $struct_list; do
+	echo
+	echo
+	echo "struct $struct {"
+	field=0
+	while
+		eval line=\"\$field_${struct}_$field\"
+		field=$(($field + 1))
+		[ -n "$line" ]
+	do
+		IFS=' '
+		set -- $line
+		name=$1
+		case $2 in
+		nodeptr ) type="union node *";;
+		nodelist ) type="struct nodelist *";;
+		string ) type="char *";;
+		int ) type="int ";;
+		* ) name=; shift 2; type="$*";;
+		esac
+		echo "      $type$name;"
+	done
+	echo "};"
+done
+
+echo
+echo
+echo "union node {"
+echo "      int type;"
+for struct in $struct_list; do
+	echo "      struct $struct $struct;"
+done
+echo "};"
+echo
+echo
+echo "struct nodelist {"
+echo "	struct nodelist *next;"
+echo "	union node *n;"
+echo "};"
+echo
+echo
+echo "union node *copyfunc(union node *);"
+echo "void freefunc(union node *);"
+
+mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1
+
+exec <$nodes_pat
+exec >$objdir/nodes.c.tmp
+
+echo "/*"
+echo " * This file was generated by mknodes.sh"
+echo " */"
+echo
+
+while IFS=; read -r line; do
+	IFS=' 	'
+	set -- $line
+	IFS=
+	case "$1" in
+	'%SIZES' )
+		echo "static const short nodesize[$tagno] = {"
+		IFS=' '
+		for struct in $struct_define; do
+			echo "      SHELL_ALIGN(sizeof (struct $struct)),"
+		done
+		echo "};"
+		;;
+	'%CALCSIZE' )
+		echo "      if (n == NULL)"
+		echo "	    return;"
+		echo "      funcblocksize += nodesize[n->type];"
+		echo "      switch (n->type) {"
+		IFS=' '
+		for struct in $struct_list; do
+			eval defines=\"\$define_$struct\"
+			for define in $defines; do
+				echo "      case $define:"
+			done
+			eval field=\$numfld_$struct
+			while
+				[ $field != 0 ]
+			do
+				eval line=\"\$field_${struct}_$field\"
+				field=$(($field - 1))
+				IFS=' '
+				set -- $line
+				name=$1
+				cl=")"
+				case $2 in
+				nodeptr ) fn=calcsize;;
+				nodelist ) fn=sizenodelist;;
+				string ) fn="funcstringsize += strlen"
+					cl=") + 1";;
+				* ) continue;;
+				esac
+				echo "	    ${fn}(n->$struct.$name${cl};"
+			done
+			echo "	    break;"
+		done
+		echo "      };"
+		;;
+	'%COPY' )
+		echo "      if (n == NULL)"
+		echo "	    return NULL;"
+		echo "      new = funcblock;"
+		echo "      funcblock = (char *) funcblock + nodesize[n->type];"
+		echo "      switch (n->type) {"
+		IFS=' '
+		for struct in $struct_list; do
+			eval defines=\"\$define_$struct\"
+			for define in $defines; do
+				echo "      case $define:"
+			done
+			eval field=\$numfld_$struct
+			while
+				[ $field != 0 ]
+			do
+				eval line=\"\$field_${struct}_$field\"
+				field=$(($field - 1))
+				IFS=' '
+				set -- $line
+				name=$1
+				case $2 in
+				nodeptr ) fn="copynode(";;
+				nodelist ) fn="copynodelist(";;
+				string ) fn="nodesavestr(";;
+				int ) fn=;;
+				* ) continue;;
+				esac
+				f="$struct.$name"
+				echo "	    new->$f = ${fn}n->$f${fn:+)};"
+			done
+			echo "	    break;"
+		done
+		echo "      };"
+		echo "      new->type = n->type;"
+		;;
+	* ) echo "$line";;
+	esac
+done
+
+mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1
diff --git a/sh/mktokens b/sh/mktokens
new file mode 100644
index 0000000..25f2e6e
--- /dev/null
+++ b/sh/mktokens
@@ -0,0 +1,92 @@
+#!/bin/sh -
+#	$NetBSD: mktokens,v 1.10 2003/08/22 11:22:23 agc Exp $
+#
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)mktokens	8.1 (Berkeley) 5/31/93
+
+# The following is a list of tokens.  The second column is nonzero if the
+# token marks the end of a list.  The third column is the name to print in
+# error messages.
+
+cat > /tmp/ka$$ <<\!
+TEOF	1	end of file
+TNL	0	newline
+TSEMI	0	";"
+TBACKGND 0	"&"
+TAND	0	"&&"
+TOR	0	"||"
+TPIPE	0	"|"
+TLP	0	"("
+TRP	1	")"
+TENDCASE 1	";;"
+TENDBQUOTE 1	"`"
+TREDIR	0	redirection
+TWORD	0	word
+TIF	0	"if"
+TTHEN	1	"then"
+TELSE	1	"else"
+TELIF	1	"elif"
+TFI	1	"fi"
+TWHILE	0	"while"
+TUNTIL	0	"until"
+TFOR	0	"for"
+TDO	1	"do"
+TDONE	1	"done"
+TBEGIN	0	"{"
+TEND	1	"}"
+TCASE	0	"case"
+TESAC	1	"esac"
+TNOT	0	"!"
+!
+nl=`wc -l /tmp/ka$$`
+exec > token.h
+awk '{print "#define " $1 " " NR-1}' /tmp/ka$$
+echo '
+/* Array indicating which tokens mark the end of a list */
+const char tokendlist[] = {'
+awk '{print "\t" $2 ","}' /tmp/ka$$
+echo '};
+
+const char *const tokname[] = {'
+sed -e 's/"/\\"/g' \
+    -e 's/[^	 ]*[	 ][	 ]*[^	 ]*[	 ][	 ]*\(.*\)/	"\1",/' \
+    /tmp/ka$$
+echo '};
+'
+sed 's/"//g' /tmp/ka$$ | awk '
+/TIF/{print "#define KWDOFFSET " NR-1; print ""; 
+      print "const char *const parsekwd[] = {"}
+/TIF/,/neverfound/{print "	\"" $3 "\","}'
+echo '	0
+};'
+
+rm /tmp/ka$$
diff --git a/sh/myhistedit.h b/sh/myhistedit.h
new file mode 100644
index 0000000..603a27b
--- /dev/null
+++ b/sh/myhistedit.h
@@ -0,0 +1,49 @@
+/*	$NetBSD: myhistedit.h,v 1.10 2003/08/07 09:05:35 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)myhistedit.h	8.2 (Berkeley) 5/4/95
+ */
+
+#ifdef WITH_HISTORY
+#include <histedit.h>
+
+extern History *hist;
+extern EditLine *el;
+extern int displayhist;
+
+void histedit(void);
+void sethistsize(const char *);
+void setterm(const char *);
+int histcmd(int, char **);
+int inputrc(int, char **);
+int not_fcnumber(char *);
+int str_to_event(const char *, int);
+#endif
+
diff --git a/sh/mystring.c b/sh/mystring.c
new file mode 100644
index 0000000..aecf83e
--- /dev/null
+++ b/sh/mystring.c
@@ -0,0 +1,133 @@
+/*	$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mystring.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * String functions.
+ *
+ *	equal(s1, s2)		Return true if strings are equal.
+ *	scopy(from, to)		Copy a string.
+ *	scopyn(from, to, n)	Like scopy, but checks for overflow.
+ *	number(s)		Convert a string of digits to an integer.
+ *	is_number(s)		Return true if s is a string of digits.
+ */
+
+#include <stdlib.h>
+#include "shell.h"
+#include "syntax.h"
+#include "error.h"
+#include "mystring.h"
+
+
+char nullstr[1];		/* zero length string */
+
+/*
+ * equal - #defined in mystring.h
+ */
+
+/*
+ * scopy - #defined in mystring.h
+ */
+
+
+/*
+ * scopyn - copy a string from "from" to "to", truncating the string
+ *		if necessary.  "To" is always nul terminated, even if
+ *		truncation is performed.  "Size" is the size of "to".
+ */
+
+void
+scopyn(const char *from, char *to, int size)
+{
+
+	while (--size > 0) {
+		if ((*to++ = *from++) == '\0')
+			return;
+	}
+	*to = '\0';
+}
+
+
+/*
+ * prefix -- see if pfx is a prefix of string.
+ */
+
+int
+prefix(const char *pfx, const char *string)
+{
+	while (*pfx) {
+		if (*pfx++ != *string++)
+			return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+
+int
+number(const char *s)
+{
+
+	if (! is_number(s))
+		error("Illegal number: %s", s);
+	return atoi(s);
+}
+
+
+
+/*
+ * Check for a valid number.  This should be elsewhere.
+ */
+
+int
+is_number(const char *p)
+{
+	do {
+		if (! is_digit(*p))
+			return 0;
+	} while (*++p != '\0');
+	return 1;
+}
diff --git a/sh/mystring.h b/sh/mystring.h
new file mode 100644
index 0000000..08a73e9
--- /dev/null
+++ b/sh/mystring.h
@@ -0,0 +1,45 @@
+/*	$NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)mystring.h	8.2 (Berkeley) 5/4/95
+ */
+
+#include <string.h>
+
+void scopyn(const char *, char *, int);
+int prefix(const char *, const char *);
+int number(const char *);
+int is_number(const char *);
+
+#define equal(s1, s2)	(strcmp(s1, s2) == 0)
+#define scopy(s1, s2)	((void)strcpy(s2, s1))
diff --git a/sh/nodes.c b/sh/nodes.c
new file mode 100644
index 0000000..8a2c718
--- /dev/null
+++ b/sh/nodes.c
@@ -0,0 +1,347 @@
+/*
+ * This file was generated by mknodes.sh
+ */
+
+/*	$NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)nodes.c.pat	8.2 (Berkeley) 5/4/95
+ */
+
+#include <stdlib.h>
+/*
+ * Routine for dealing with parsed shell commands.
+ */
+
+#include "shell.h"
+#include "nodes.h"
+#include "memalloc.h"
+#include "machdep.h"
+#include "mystring.h"
+
+
+int     funcblocksize;		/* size of structures in function */
+int     funcstringsize;		/* size of strings in node */
+pointer funcblock;		/* block to allocate function from */
+char   *funcstring;		/* block to allocate strings from */
+
+static const short nodesize[26] = {
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct ncmd)),
+      SHELL_ALIGN(sizeof (struct npipe)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nredir)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nif)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nbinary)),
+      SHELL_ALIGN(sizeof (struct nfor)),
+      SHELL_ALIGN(sizeof (struct ncase)),
+      SHELL_ALIGN(sizeof (struct nclist)),
+      SHELL_ALIGN(sizeof (struct narg)),
+      SHELL_ALIGN(sizeof (struct narg)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct nfile)),
+      SHELL_ALIGN(sizeof (struct ndup)),
+      SHELL_ALIGN(sizeof (struct ndup)),
+      SHELL_ALIGN(sizeof (struct nhere)),
+      SHELL_ALIGN(sizeof (struct nhere)),
+      SHELL_ALIGN(sizeof (struct nnot)),
+};
+
+
+STATIC void calcsize(union node *);
+STATIC void sizenodelist(struct nodelist *);
+STATIC union node *copynode(union node *);
+STATIC struct nodelist *copynodelist(struct nodelist *);
+STATIC char *nodesavestr(char *);
+
+
+
+/*
+ * Make a copy of a parse tree.
+ */
+
+union node *
+copyfunc(n)
+	union node *n;
+{
+	if (n == NULL)
+		return NULL;
+	funcblocksize = 0;
+	funcstringsize = 0;
+	calcsize(n);
+	funcblock = ckmalloc(funcblocksize + funcstringsize);
+	funcstring = (char *) funcblock + funcblocksize;
+	return copynode(n);
+}
+
+
+
+STATIC void
+calcsize(n)
+	union node *n;
+{
+      if (n == NULL)
+	    return;
+      funcblocksize += nodesize[n->type];
+      switch (n->type) {
+      case NSEMI:
+      case NAND:
+      case NOR:
+      case NWHILE:
+      case NUNTIL:
+	    calcsize(n->nbinary.ch2);
+	    calcsize(n->nbinary.ch1);
+	    break;
+      case NCMD:
+	    calcsize(n->ncmd.redirect);
+	    calcsize(n->ncmd.args);
+	    break;
+      case NPIPE:
+	    sizenodelist(n->npipe.cmdlist);
+	    break;
+      case NREDIR:
+      case NBACKGND:
+      case NSUBSHELL:
+	    calcsize(n->nredir.redirect);
+	    calcsize(n->nredir.n);
+	    break;
+      case NIF:
+	    calcsize(n->nif.elsepart);
+	    calcsize(n->nif.ifpart);
+	    calcsize(n->nif.test);
+	    break;
+      case NFOR:
+	    funcstringsize += strlen(n->nfor.var) + 1;
+	    calcsize(n->nfor.body);
+	    calcsize(n->nfor.args);
+	    break;
+      case NCASE:
+	    calcsize(n->ncase.cases);
+	    calcsize(n->ncase.expr);
+	    break;
+      case NCLIST:
+	    calcsize(n->nclist.body);
+	    calcsize(n->nclist.pattern);
+	    calcsize(n->nclist.next);
+	    break;
+      case NDEFUN:
+      case NARG:
+	    sizenodelist(n->narg.backquote);
+	    funcstringsize += strlen(n->narg.text) + 1;
+	    calcsize(n->narg.next);
+	    break;
+      case NTO:
+      case NCLOBBER:
+      case NFROM:
+      case NFROMTO:
+      case NAPPEND:
+	    calcsize(n->nfile.fname);
+	    calcsize(n->nfile.next);
+	    break;
+      case NTOFD:
+      case NFROMFD:
+	    calcsize(n->ndup.vname);
+	    calcsize(n->ndup.next);
+	    break;
+      case NHERE:
+      case NXHERE:
+	    calcsize(n->nhere.doc);
+	    calcsize(n->nhere.next);
+	    break;
+      case NNOT:
+	    calcsize(n->nnot.com);
+	    break;
+      };
+}
+
+
+
+STATIC void
+sizenodelist(lp)
+	struct nodelist *lp;
+{
+	while (lp) {
+		funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
+		calcsize(lp->n);
+		lp = lp->next;
+	}
+}
+
+
+
+STATIC union node *
+copynode(n)
+	union node *n;
+{
+	union node *new;
+
+      if (n == NULL)
+	    return NULL;
+      new = funcblock;
+      funcblock = (char *) funcblock + nodesize[n->type];
+      switch (n->type) {
+      case NSEMI:
+      case NAND:
+      case NOR:
+      case NWHILE:
+      case NUNTIL:
+	    new->nbinary.ch2 = copynode(n->nbinary.ch2);
+	    new->nbinary.ch1 = copynode(n->nbinary.ch1);
+	    break;
+      case NCMD:
+	    new->ncmd.redirect = copynode(n->ncmd.redirect);
+	    new->ncmd.args = copynode(n->ncmd.args);
+	    new->ncmd.backgnd = n->ncmd.backgnd;
+	    break;
+      case NPIPE:
+	    new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+	    new->npipe.backgnd = n->npipe.backgnd;
+	    break;
+      case NREDIR:
+      case NBACKGND:
+      case NSUBSHELL:
+	    new->nredir.redirect = copynode(n->nredir.redirect);
+	    new->nredir.n = copynode(n->nredir.n);
+	    break;
+      case NIF:
+	    new->nif.elsepart = copynode(n->nif.elsepart);
+	    new->nif.ifpart = copynode(n->nif.ifpart);
+	    new->nif.test = copynode(n->nif.test);
+	    break;
+      case NFOR:
+	    new->nfor.var = nodesavestr(n->nfor.var);
+	    new->nfor.body = copynode(n->nfor.body);
+	    new->nfor.args = copynode(n->nfor.args);
+	    break;
+      case NCASE:
+	    new->ncase.cases = copynode(n->ncase.cases);
+	    new->ncase.expr = copynode(n->ncase.expr);
+	    break;
+      case NCLIST:
+	    new->nclist.body = copynode(n->nclist.body);
+	    new->nclist.pattern = copynode(n->nclist.pattern);
+	    new->nclist.next = copynode(n->nclist.next);
+	    break;
+      case NDEFUN:
+      case NARG:
+	    new->narg.backquote = copynodelist(n->narg.backquote);
+	    new->narg.text = nodesavestr(n->narg.text);
+	    new->narg.next = copynode(n->narg.next);
+	    break;
+      case NTO:
+      case NCLOBBER:
+      case NFROM:
+      case NFROMTO:
+      case NAPPEND:
+	    new->nfile.fname = copynode(n->nfile.fname);
+	    new->nfile.fd = n->nfile.fd;
+	    new->nfile.next = copynode(n->nfile.next);
+	    break;
+      case NTOFD:
+      case NFROMFD:
+	    new->ndup.vname = copynode(n->ndup.vname);
+	    new->ndup.dupfd = n->ndup.dupfd;
+	    new->ndup.fd = n->ndup.fd;
+	    new->ndup.next = copynode(n->ndup.next);
+	    break;
+      case NHERE:
+      case NXHERE:
+	    new->nhere.doc = copynode(n->nhere.doc);
+	    new->nhere.fd = n->nhere.fd;
+	    new->nhere.next = copynode(n->nhere.next);
+	    break;
+      case NNOT:
+	    new->nnot.com = copynode(n->nnot.com);
+	    break;
+      };
+      new->type = n->type;
+	return new;
+}
+
+
+STATIC struct nodelist *
+copynodelist(lp)
+	struct nodelist *lp;
+{
+	struct nodelist *start;
+	struct nodelist **lpp;
+
+	lpp = &start;
+	while (lp) {
+		*lpp = funcblock;
+		funcblock = (char *) funcblock +
+		    SHELL_ALIGN(sizeof(struct nodelist));
+		(*lpp)->n = copynode(lp->n);
+		lp = lp->next;
+		lpp = &(*lpp)->next;
+	}
+	*lpp = NULL;
+	return start;
+}
+
+
+
+STATIC char *
+nodesavestr(s)
+	char   *s;
+{
+	register char *p = s;
+	register char *q = funcstring;
+	char   *rtn = funcstring;
+
+	while ((*q++ = *p++) != 0)
+		continue;
+	funcstring = q;
+	return rtn;
+}
+
+
+
+/*
+ * Free a parse tree.
+ */
+
+void
+freefunc(n)
+	union node *n;
+{
+	if (n)
+		ckfree(n);
+}
diff --git a/sh/nodes.c.pat b/sh/nodes.c.pat
new file mode 100644
index 0000000..e619a01
--- /dev/null
+++ b/sh/nodes.c.pat
@@ -0,0 +1,166 @@
+/*	$NetBSD: nodes.c.pat,v 1.12 2004/06/15 22:57:27 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)nodes.c.pat	8.2 (Berkeley) 5/4/95
+ */
+
+#include <stdlib.h>
+/*
+ * Routine for dealing with parsed shell commands.
+ */
+
+#include "shell.h"
+#include "nodes.h"
+#include "memalloc.h"
+#include "machdep.h"
+#include "mystring.h"
+
+
+int     funcblocksize;		/* size of structures in function */
+int     funcstringsize;		/* size of strings in node */
+pointer funcblock;		/* block to allocate function from */
+char   *funcstring;		/* block to allocate strings from */
+
+%SIZES
+
+
+STATIC void calcsize(union node *);
+STATIC void sizenodelist(struct nodelist *);
+STATIC union node *copynode(union node *);
+STATIC struct nodelist *copynodelist(struct nodelist *);
+STATIC char *nodesavestr(char *);
+
+
+
+/*
+ * Make a copy of a parse tree.
+ */
+
+union node *
+copyfunc(n)
+	union node *n;
+{
+	if (n == NULL)
+		return NULL;
+	funcblocksize = 0;
+	funcstringsize = 0;
+	calcsize(n);
+	funcblock = ckmalloc(funcblocksize + funcstringsize);
+	funcstring = (char *) funcblock + funcblocksize;
+	return copynode(n);
+}
+
+
+
+STATIC void
+calcsize(n)
+	union node *n;
+{
+	%CALCSIZE
+}
+
+
+
+STATIC void
+sizenodelist(lp)
+	struct nodelist *lp;
+{
+	while (lp) {
+		funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
+		calcsize(lp->n);
+		lp = lp->next;
+	}
+}
+
+
+
+STATIC union node *
+copynode(n)
+	union node *n;
+{
+	union node *new;
+
+	%COPY
+	return new;
+}
+
+
+STATIC struct nodelist *
+copynodelist(lp)
+	struct nodelist *lp;
+{
+	struct nodelist *start;
+	struct nodelist **lpp;
+
+	lpp = &start;
+	while (lp) {
+		*lpp = funcblock;
+		funcblock = (char *) funcblock +
+		    SHELL_ALIGN(sizeof(struct nodelist));
+		(*lpp)->n = copynode(lp->n);
+		lp = lp->next;
+		lpp = &(*lpp)->next;
+	}
+	*lpp = NULL;
+	return start;
+}
+
+
+
+STATIC char *
+nodesavestr(s)
+	char   *s;
+{
+	register char *p = s;
+	register char *q = funcstring;
+	char   *rtn = funcstring;
+
+	while ((*q++ = *p++) != 0)
+		continue;
+	funcstring = q;
+	return rtn;
+}
+
+
+
+/*
+ * Free a parse tree.
+ */
+
+void
+freefunc(n)
+	union node *n;
+{
+	if (n)
+		ckfree(n);
+}
diff --git a/sh/nodes.h b/sh/nodes.h
new file mode 100644
index 0000000..aa750ed
--- /dev/null
+++ b/sh/nodes.h
@@ -0,0 +1,159 @@
+/*
+ * This file was generated by mknodes.sh
+ */
+
+#define NSEMI 0
+#define NCMD 1
+#define NPIPE 2
+#define NREDIR 3
+#define NBACKGND 4
+#define NSUBSHELL 5
+#define NAND 6
+#define NOR 7
+#define NIF 8
+#define NWHILE 9
+#define NUNTIL 10
+#define NFOR 11
+#define NCASE 12
+#define NCLIST 13
+#define NDEFUN 14
+#define NARG 15
+#define NTO 16
+#define NCLOBBER 17
+#define NFROM 18
+#define NFROMTO 19
+#define NAPPEND 20
+#define NTOFD 21
+#define NFROMFD 22
+#define NHERE 23
+#define NXHERE 24
+#define NNOT 25
+
+
+
+struct nbinary {
+      int type;
+      union node *ch1;
+      union node *ch2;
+};
+
+
+struct ncmd {
+      int type;
+      int backgnd;
+      union node *args;
+      union node *redirect;
+};
+
+
+struct npipe {
+      int type;
+      int backgnd;
+      struct nodelist *cmdlist;
+};
+
+
+struct nredir {
+      int type;
+      union node *n;
+      union node *redirect;
+};
+
+
+struct nif {
+      int type;
+      union node *test;
+      union node *ifpart;
+      union node *elsepart;
+};
+
+
+struct nfor {
+      int type;
+      union node *args;
+      union node *body;
+      char *var;
+};
+
+
+struct ncase {
+      int type;
+      union node *expr;
+      union node *cases;
+};
+
+
+struct nclist {
+      int type;
+      union node *next;
+      union node *pattern;
+      union node *body;
+};
+
+
+struct narg {
+      int type;
+      union node *next;
+      char *text;
+      struct nodelist *backquote;
+};
+
+
+struct nfile {
+      int type;
+      union node *next;
+      int fd;
+      union node *fname;
+      char *expfname;
+};
+
+
+struct ndup {
+      int type;
+      union node *next;
+      int fd;
+      int dupfd;
+      union node *vname;
+};
+
+
+struct nhere {
+      int type;
+      union node *next;
+      int fd;
+      union node *doc;
+};
+
+
+struct nnot {
+      int type;
+      union node *com;
+};
+
+
+union node {
+      int type;
+      struct nbinary nbinary;
+      struct ncmd ncmd;
+      struct npipe npipe;
+      struct nredir nredir;
+      struct nif nif;
+      struct nfor nfor;
+      struct ncase ncase;
+      struct nclist nclist;
+      struct narg narg;
+      struct nfile nfile;
+      struct ndup ndup;
+      struct nhere nhere;
+      struct nnot nnot;
+};
+
+
+struct nodelist {
+	struct nodelist *next;
+	union node *n;
+};
+
+
+union node *copyfunc(union node *);
+void freefunc(union node *);
diff --git a/sh/nodetypes b/sh/nodetypes
new file mode 100644
index 0000000..4adebc0
--- /dev/null
+++ b/sh/nodetypes
@@ -0,0 +1,143 @@
+#	$NetBSD: nodetypes,v 1.12 2003/08/22 11:22:23 agc Exp $
+# Copyright (c) 1991, 1993
+#	The Regents of the University of California.  All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Kenneth Almquist.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+#    may be used to endorse or promote products derived from this software
+#    without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#	@(#)nodetypes	8.2 (Berkeley) 5/4/95
+
+# This file describes the nodes used in parse trees.  Unindented lines
+# contain a node type followed by a structure tag.  Subsequent indented
+# lines specify the fields of the structure.  Several node types can share
+# the same structure, in which case the fields of the structure should be
+# specified only once.
+#
+# A field of a structure is described by the name of the field followed
+# by a type.  The currently implemented types are:
+#	nodeptr - a pointer to a node
+#	nodelist - a pointer to a list of nodes
+#	string - a pointer to a nul terminated string
+#	int - an integer
+#	other - any type that can be copied by assignment
+#	temp - a field that doesn't have to be copied when the node is copied
+# The last two types should be followed by the text of a C declaration for
+# the field.
+
+NSEMI nbinary			# two commands separated by a semicolon
+	type	  int
+	ch1	  nodeptr		# the first child
+	ch2	  nodeptr		# the second child
+
+NCMD ncmd			# a simple command
+	type	  int
+	backgnd	  int			# set to run command in background
+	args	  nodeptr		# the arguments
+	redirect  nodeptr		# list of file redirections
+
+NPIPE npipe			# a pipeline
+	type	  int
+	backgnd	  int			# set to run pipeline in background
+	cmdlist	  nodelist		# the commands in the pipeline
+
+NREDIR nredir			# redirection (of a complex command)
+	type	  int
+	n	  nodeptr		# the command
+	redirect  nodeptr		# list of file redirections
+
+NBACKGND nredir			# run command in background
+NSUBSHELL nredir		# run command in a subshell
+
+NAND nbinary			# the && operator
+NOR nbinary			# the || operator
+
+NIF nif				# the if statement.  Elif clauses are handled
+	type	  int		    # using multiple if nodes.
+	test	  nodeptr		# if test
+	ifpart	  nodeptr		# then ifpart
+	elsepart  nodeptr		# else elsepart
+
+NWHILE nbinary			# the while statement.  First child is the test
+NUNTIL nbinary			# the until statement
+
+NFOR nfor			# the for statement
+	type	  int
+	args	  nodeptr		# for var in args
+	body	  nodeptr		# do body; done
+	var	  string		# the for variable
+
+NCASE ncase			# a case statement
+	type	  int
+	expr	  nodeptr		# the word to switch on
+	cases	  nodeptr		# the list of cases (NCLIST nodes)
+
+NCLIST nclist			# a case
+	type	  int
+	next	  nodeptr		# the next case in list
+	pattern	  nodeptr		# list of patterns for this case
+	body	  nodeptr		# code to execute for this case
+
+
+NDEFUN narg			# define a function.  The "next" field contains
+				# the body of the function.
+
+NARG narg			# represents a word
+	type	  int
+	next	  nodeptr		# next word in list
+	text	  string		# the text of the word
+	backquote nodelist		# list of commands in back quotes
+
+NTO nfile			# fd> fname
+NCLOBBER nfile			# fd>| fname
+NFROM nfile			# fd< fname
+NFROMTO nfile			# fd<> fname
+NAPPEND nfile			# fd>> fname
+	type	  int
+	next	  nodeptr		# next redirection in list
+	fd	  int			# file descriptor being redirected
+	fname	  nodeptr		# file name, in a NARG node
+	expfname  temp	char *expfname	# actual file name
+
+NTOFD ndup			# fd<&dupfd
+NFROMFD ndup			# fd>&dupfd
+	type	  int
+	next	  nodeptr		# next redirection in list
+	fd	  int			# file descriptor being redirected
+	dupfd	  int			# file descriptor to duplicate
+	vname	  nodeptr		# file name if fd>&$var
+
+
+NHERE nhere			# fd<<\!
+NXHERE nhere			# fd<<!
+	type	  int
+	next	  nodeptr		# next redirection in list
+	fd	  int			# file descriptor being redirected
+	doc	  nodeptr		# input to command (NARG node)
+
+NNOT nnot			# ! command  (actually pipeline)
+	type	int
+	com	nodeptr
diff --git a/sh/options.c b/sh/options.c
new file mode 100644
index 0000000..bc833c7
--- /dev/null
+++ b/sh/options.c
@@ -0,0 +1,530 @@
+/*	$NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "shell.h"
+#define DEFINE_OPTIONS
+#include "options.h"
+#undef DEFINE_OPTIONS
+#include "nodes.h"	/* for other header files */
+#include "eval.h"
+#include "jobs.h"
+#include "input.h"
+#include "output.h"
+#include "trap.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+#include "show.h"
+
+char *arg0;			/* value of $0 */
+struct shparam shellparam;	/* current positional parameters */
+char **argptr;			/* argument list for builtin commands */
+char *optionarg;		/* set by nextopt (like getopt) */
+char *optptr;			/* used by nextopt */
+
+char *minusc;			/* argument to -c option */
+
+
+STATIC void options(int);
+STATIC void minus_o(char *, int);
+STATIC void setoption(int, int);
+STATIC int getopts(char *, char *, char **, char ***, char **);
+
+
+/*
+ * Process the shell command line arguments.
+ */
+
+void
+procargs(int argc, char **argv)
+{
+	int i;
+
+	argptr = argv;
+	if (argc > 0)
+		argptr++;
+	for (i = 0; i < NOPTS; i++)
+		optlist[i].val = 2;
+	options(1);
+	if (*argptr == NULL && minusc == NULL)
+		sflag = 1;
+	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+		iflag = 1;
+	if (mflag == 2)
+		mflag = iflag;
+	for (i = 0; i < NOPTS; i++)
+		if (optlist[i].val == 2)
+			optlist[i].val = 0;
+#if DEBUG == 2
+	debug = 1;
+#endif
+	arg0 = argv[0];
+	if (sflag == 0 && minusc == NULL) {
+		commandname = argv[0];
+		arg0 = *argptr++;
+		setinputfile(arg0, 0);
+		commandname = arg0;
+	}
+	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+	if (minusc != NULL) {
+		if (argptr == NULL || *argptr == NULL)
+			error("Bad -c option");
+		minusc = *argptr++;
+		if (*argptr != 0)
+			arg0 = *argptr++;
+	}
+
+	shellparam.p = argptr;
+	shellparam.reset = 1;
+	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+	while (*argptr) {
+		shellparam.nparam++;
+		argptr++;
+	}
+	optschanged();
+}
+
+
+void
+optschanged(void)
+{
+	setinteractive(iflag);
+#ifdef WITH_HISTORY
+	histedit();
+#endif
+	setjobctl(mflag);
+}
+
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
+
+STATIC void
+options(int cmdline)
+{
+	static char empty[] = "";
+	char *p;
+	int val;
+	int c;
+
+	if (cmdline)
+		minusc = NULL;
+	while ((p = *argptr) != NULL) {
+		argptr++;
+		if ((c = *p++) == '-') {
+			val = 1;
+                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
+                                if (!cmdline) {
+                                        /* "-" means turn off -x and -v */
+                                        if (p[0] == '\0')
+                                                xflag = vflag = 0;
+                                        /* "--" means reset params */
+                                        else if (*argptr == NULL)
+						setparam(argptr);
+                                }
+				break;	  /* "-" or  "--" terminates options */
+			}
+		} else if (c == '+') {
+			val = 0;
+		} else {
+			argptr--;
+			break;
+		}
+		while ((c = *p++) != '\0') {
+			if (c == 'c' && cmdline) {
+				/* command is after shell args*/
+				minusc = empty;
+			} else if (c == 'o') {
+				minus_o(*argptr, val);
+				if (*argptr)
+					argptr++;
+			} else {
+				setoption(c, val);
+			}
+		}
+	}
+}
+
+static void
+set_opt_val(int i, int val)
+{
+	int j;
+	int flag;
+
+	if (val && (flag = optlist[i].opt_set)) {
+		/* some options (eg vi/emacs) are mutually exclusive */
+		for (j = 0; j < NOPTS; j++)
+		    if (optlist[j].opt_set == flag)
+			optlist[j].val = 0;
+	}
+	optlist[i].val = val;
+#ifdef DEBUG
+	if (&optlist[i].val == &debug)
+		opentrace();
+#endif
+}
+
+STATIC void
+minus_o(char *name, int val)
+{
+	int i;
+
+	if (name == NULL) {
+		out1str("Current option settings\n");
+		for (i = 0; i < NOPTS; i++)
+			out1fmt("%-16s%s\n", optlist[i].name,
+				optlist[i].val ? "on" : "off");
+	} else {
+		for (i = 0; i < NOPTS; i++)
+			if (equal(name, optlist[i].name)) {
+				set_opt_val(i, val);
+				return;
+			}
+		error("Illegal option -o %s", name);
+	}
+}
+
+
+STATIC void
+setoption(int flag, int val)
+{
+	int i;
+
+	for (i = 0; i < NOPTS; i++)
+		if (optlist[i].letter == flag) {
+			set_opt_val( i, val );
+			return;
+		}
+	error("Illegal option -%c", flag);
+	/* NOTREACHED */
+}
+
+
+
+#ifdef mkinit
+INCLUDE "options.h"
+
+SHELLPROC {
+	int i;
+
+	for (i = 0; optlist[i].name; i++)
+		optlist[i].val = 0;
+	optschanged();
+
+}
+#endif
+
+
+/*
+ * Set the shell parameters.
+ */
+
+void
+setparam(char **argv)
+{
+	char **newparam;
+	char **ap;
+	int nparam;
+
+	for (nparam = 0 ; argv[nparam] ; nparam++);
+	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
+	while (*argv) {
+		*ap++ = savestr(*argv++);
+	}
+	*ap = NULL;
+	freeparam(&shellparam);
+	shellparam.malloc = 1;
+	shellparam.nparam = nparam;
+	shellparam.p = newparam;
+	shellparam.optnext = NULL;
+}
+
+
+/*
+ * Free the list of positional parameters.
+ */
+
+void
+freeparam(volatile struct shparam *param)
+{
+	char **ap;
+
+	if (param->malloc) {
+		for (ap = param->p ; *ap ; ap++)
+			ckfree(*ap);
+		ckfree(param->p);
+	}
+}
+
+
+
+/*
+ * The shift builtin command.
+ */
+
+int
+shiftcmd(int argc, char **argv)
+{
+	int n;
+	char **ap1, **ap2;
+
+	n = 1;
+	if (argc > 1)
+		n = number(argv[1]);
+	if (n > shellparam.nparam)
+		error("can't shift that many");
+	INTOFF;
+	shellparam.nparam -= n;
+	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
+		if (shellparam.malloc)
+			ckfree(*ap1);
+	}
+	ap2 = shellparam.p;
+	while ((*ap2++ = *ap1++) != NULL);
+	shellparam.optnext = NULL;
+	INTON;
+	return 0;
+}
+
+
+
+/*
+ * The set command builtin.
+ */
+
+int
+setcmd(int argc, char **argv)
+{
+	if (argc == 1)
+		return showvars(0, 0, 1);
+	INTOFF;
+	options(0);
+	optschanged();
+	if (*argptr != NULL) {
+		setparam(argptr);
+	}
+	INTON;
+	return 0;
+}
+
+
+void
+getoptsreset(value)
+	const char *value;
+{
+	if (number(value) == 1) {
+		shellparam.optnext = NULL;
+		shellparam.reset = 1;
+	}
+}
+
+/*
+ * The getopts builtin.  Shellparam.optnext points to the next argument
+ * to be processed.  Shellparam.optptr points to the next character to
+ * be processed in the current argument.  If shellparam.optnext is NULL,
+ * then it's the first time getopts has been called.
+ */
+
+int
+getoptscmd(int argc, char **argv)
+{
+	char **optbase;
+
+	if (argc < 3)
+		error("usage: getopts optstring var [arg]");
+	else if (argc == 3)
+		optbase = shellparam.p;
+	else
+		optbase = &argv[3];
+
+	if (shellparam.reset == 1) {
+		shellparam.optnext = optbase;
+		shellparam.optptr = NULL;
+		shellparam.reset = 0;
+	}
+
+	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
+		       &shellparam.optptr);
+}
+
+STATIC int
+getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
+{
+	char *p, *q;
+	char c = '?';
+	int done = 0;
+	int ind = 0;
+	int err = 0;
+	char s[12];
+
+	if ((p = *optpptr) == NULL || *p == '\0') {
+		/* Current word is done, advance */
+		if (*optnext == NULL)
+			return 1;
+		p = **optnext;
+		if (p == NULL || *p != '-' || *++p == '\0') {
+atend:
+			ind = *optnext - optfirst + 1;
+			*optnext = NULL;
+			p = NULL;
+			done = 1;
+			goto out;
+		}
+		(*optnext)++;
+		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
+			goto atend;
+	}
+
+	c = *p++;
+	for (q = optstr; *q != c; ) {
+		if (*q == '\0') {
+			if (optstr[0] == ':') {
+				s[0] = c;
+				s[1] = '\0';
+				err |= setvarsafe("OPTARG", s, 0);
+			} else {
+				outfmt(&errout, "Illegal option -%c\n", c);
+				(void) unsetvar("OPTARG", 0);
+			}
+			c = '?';
+			goto bad;
+		}
+		if (*++q == ':')
+			q++;
+	}
+
+	if (*++q == ':') {
+		if (*p == '\0' && (p = **optnext) == NULL) {
+			if (optstr[0] == ':') {
+				s[0] = c;
+				s[1] = '\0';
+				err |= setvarsafe("OPTARG", s, 0);
+				c = ':';
+			} else {
+				outfmt(&errout, "No arg for -%c option\n", c);
+				(void) unsetvar("OPTARG", 0);
+				c = '?';
+			}
+			goto bad;
+		}
+
+		if (p == **optnext)
+			(*optnext)++;
+		err |= setvarsafe("OPTARG", p, 0);
+		p = NULL;
+	} else
+		err |= setvarsafe("OPTARG", "", 0);
+	ind = *optnext - optfirst + 1;
+	goto out;
+
+bad:
+	ind = 1;
+	*optnext = NULL;
+	p = NULL;
+out:
+	*optpptr = p;
+	fmtstr(s, sizeof(s), "%d", ind);
+	err |= setvarsafe("OPTIND", s, VNOFUNC);
+	s[0] = c;
+	s[1] = '\0';
+	err |= setvarsafe(optvar, s, 0);
+	if (err) {
+		*optnext = NULL;
+		*optpptr = NULL;
+		flushall();
+		exraise(EXERROR);
+	}
+	return done;
+}
+
+/*
+ * XXX - should get rid of.  have all builtins use getopt(3).  the
+ * library getopt must have the BSD extension static variable "optreset"
+ * otherwise it can't be used within the shell safely.
+ *
+ * Standard option processing (a la getopt) for builtin routines.  The
+ * only argument that is passed to nextopt is the option string; the
+ * other arguments are unnecessary.  It return the character, or '\0' on
+ * end of input.
+ */
+
+int
+nextopt(const char *optstring)
+{
+	char *p;
+	const char *q;
+	char c;
+
+	if ((p = optptr) == NULL || *p == '\0') {
+		p = *argptr;
+		if (p == NULL || *p != '-' || *++p == '\0')
+			return '\0';
+		argptr++;
+		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
+			return '\0';
+	}
+	c = *p++;
+	for (q = optstring ; *q != c ; ) {
+		if (*q == '\0')
+			error("Illegal option -%c", c);
+		if (*++q == ':')
+			q++;
+	}
+	if (*++q == ':') {
+		if (*p == '\0' && (p = *argptr++) == NULL)
+			error("No arg for -%c option", c);
+		optionarg = p;
+		p = NULL;
+	}
+	optptr = p;
+	return c;
+}
diff --git a/sh/options.h b/sh/options.h
new file mode 100644
index 0000000..4cc7dbe
--- /dev/null
+++ b/sh/options.h
@@ -0,0 +1,131 @@
+/*	$NetBSD: options.h,v 1.17 2003/08/07 09:05:36 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)options.h	8.2 (Berkeley) 5/4/95
+ */
+
+struct shparam {
+	int nparam;		/* # of positional parameters (without $0) */
+	unsigned char malloc;	/* if parameter list dynamically allocated */
+	unsigned char reset;	/* if getopts has been reset */
+	char **p;		/* parameter list */
+	char **optnext;		/* next parameter to be processed by getopts */
+	char *optptr;		/* used by getopts */
+};
+
+
+struct optent {
+	const char *name;		/* for set -o <name> */
+	const char letter;		/* set [+/-]<letter> and $- */
+	const char opt_set;		/* mutually exclusive option set */
+	char val;			/* value of <letter>flag */
+};
+
+/* Those marked [U] are required by posix, but have no effect! */
+
+#ifdef DEFINE_OPTIONS
+#define DEF_OPTS(name, letter, opt_set) {name, letter, opt_set, 0},
+struct optent optlist[] = {
+#else
+#define DEF_OPTS(name, letter, opt_set)
+#endif
+#define DEF_OPT(name,letter) DEF_OPTS(name, letter, 0)
+
+DEF_OPT( "errexit",	'e' )	/* exit on error */
+#define eflag optlist[0].val
+DEF_OPT( "noglob",	'f' )	/* no pathname expansion */
+#define fflag optlist[1].val
+DEF_OPT( "ignoreeof",	'I' )	/* do not exit on EOF */
+#define Iflag optlist[2].val
+DEF_OPT( "interactive",'i' )	/* interactive shell */
+#define iflag optlist[3].val
+DEF_OPT( "monitor",	'm' )	/* job control */
+#define mflag optlist[4].val
+DEF_OPT( "noexec",	'n' )	/* [U] do not exec commands */
+#define nflag optlist[5].val
+DEF_OPT( "stdin",	's' )	/* read from stdin */
+#define sflag optlist[6].val
+DEF_OPT( "xtrace",	'x' )	/* trace after expansion */
+#define xflag optlist[7].val
+DEF_OPT( "verbose",	'v' )	/* trace read input */
+#define vflag optlist[8].val
+DEF_OPTS( "vi",		'V', 'V' )	/* vi style editing */
+#define Vflag optlist[9].val
+DEF_OPTS( "emacs",	'E', 'V' )	/* emacs style editing */
+#define	Eflag optlist[10].val
+DEF_OPT( "noclobber",	'C' )	/* do not overwrite files with > */
+#define	Cflag optlist[11].val
+DEF_OPT( "allexport",	'a' )	/* export all variables */
+#define	aflag optlist[12].val
+DEF_OPT( "notify",	'b' )	/* [U] report completion of background jobs */
+#define	bflag optlist[13].val
+DEF_OPT( "nounset",	'u' )	/* error expansion of unset variables */
+#define	uflag optlist[14].val
+DEF_OPT( "quietprofile", 'q' )
+#define	qflag optlist[15].val
+DEF_OPT( "nolog",	0 )	/* [U] no functon defs in command history */
+#define	nolog optlist[16].val
+DEF_OPT( "cdprint",	0 )	/* always print result of cd */
+#define	cdprint optlist[17].val
+#ifdef DEBUG
+DEF_OPT( "debug",	0 )	/* enable debug prints */
+#define	debug optlist[18].val
+#endif
+
+#ifdef DEFINE_OPTIONS
+	{ 0, 0, 0, 0 },
+};
+#define NOPTS (sizeof optlist / sizeof optlist[0] - 1)
+int sizeof_optlist = sizeof optlist;
+#else
+extern struct optent optlist[];
+extern int sizeof_optlist;
+#endif
+
+
+extern char *minusc;		/* argument to -c option */
+extern char *arg0;		/* $0 */
+extern struct shparam shellparam;  /* $@ */
+extern char **argptr;		/* argument list for builtin commands */
+extern char *optionarg;		/* set by nextopt */
+extern char *optptr;		/* used by nextopt */
+
+void procargs(int, char **);
+void optschanged(void);
+void setparam(char **);
+void freeparam(volatile struct shparam *);
+int shiftcmd(int, char **);
+int setcmd(int, char **);
+int getoptscmd(int, char **);
+int nextopt(const char *);
+void getoptsreset(const char *);
diff --git a/sh/output.c b/sh/output.c
new file mode 100644
index 0000000..b0e669e
--- /dev/null
+++ b/sh/output.c
@@ -0,0 +1,516 @@
+/*	$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)output.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Shell output routines.  We use our own output routines because:
+ *	When a builtin command is interrupted we have to discard
+ *		any pending output.
+ *	When a builtin command appears in back quotes, we want to
+ *		save the output of the command in a region obtained
+ *		via malloc, rather than doing a fork and reading the
+ *		output of the command via a pipe.
+ *	Our output routines may be smaller than the stdio routines.
+ */
+
+#include <sys/types.h>		/* quad_t */
+#include <sys/param.h>		/* BSD4_4 */
+#include <sys/ioctl.h>
+
+#include <stdio.h>	/* defines BUFSIZ */
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "shell.h"
+#include "syntax.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+
+
+#define OUTBUFSIZ BUFSIZ
+#define BLOCK_OUT -2		/* output to a fixed block of memory */
+#define MEM_OUT -3		/* output to dynamically allocated memory */
+#define OUTPUT_ERR 01		/* error occurred on output */
+
+
+struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
+struct output errout = {NULL, 0, NULL, 100, 2, 0};
+struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
+struct output *out1 = &output;
+struct output *out2 = &errout;
+
+
+
+#ifdef mkinit
+
+INCLUDE "output.h"
+INCLUDE "memalloc.h"
+
+RESET {
+	out1 = &output;
+	out2 = &errout;
+	if (memout.buf != NULL) {
+		ckfree(memout.buf);
+		memout.buf = NULL;
+	}
+}
+
+#endif
+
+
+#ifdef notdef	/* no longer used */
+/*
+ * Set up an output file to write to memory rather than a file.
+ */
+
+void
+open_mem(char *block, int length, struct output *file)
+{
+	file->nextc = block;
+	file->nleft = --length;
+	file->fd = BLOCK_OUT;
+	file->flags = 0;
+}
+#endif
+
+
+void
+out1str(const char *p)
+{
+	outstr(p, out1);
+}
+
+
+void
+out2str(const char *p)
+{
+	outstr(p, out2);
+}
+
+
+void
+outstr(const char *p, struct output *file)
+{
+	while (*p)
+		outc(*p++, file);
+	if (file == out2)
+		flushout(file);
+}
+
+
+char out_junk[16];
+
+
+void
+emptyoutbuf(struct output *dest)
+{
+	int offset;
+
+	if (dest->fd == BLOCK_OUT) {
+		dest->nextc = out_junk;
+		dest->nleft = sizeof out_junk;
+		dest->flags |= OUTPUT_ERR;
+	} else if (dest->buf == NULL) {
+		INTOFF;
+		dest->buf = ckmalloc(dest->bufsize);
+		dest->nextc = dest->buf;
+		dest->nleft = dest->bufsize;
+		INTON;
+	} else if (dest->fd == MEM_OUT) {
+		offset = dest->bufsize;
+		INTOFF;
+		dest->bufsize <<= 1;
+		dest->buf = ckrealloc(dest->buf, dest->bufsize);
+		dest->nleft = dest->bufsize - offset;
+		dest->nextc = dest->buf + offset;
+		INTON;
+	} else {
+		flushout(dest);
+	}
+	dest->nleft--;
+}
+
+
+void
+flushall(void)
+{
+	flushout(&output);
+	flushout(&errout);
+}
+
+
+void
+flushout(struct output *dest)
+{
+
+	if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
+		return;
+	if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
+		dest->flags |= OUTPUT_ERR;
+	dest->nextc = dest->buf;
+	dest->nleft = dest->bufsize;
+}
+
+
+void
+freestdout(void)
+{
+	INTOFF;
+	if (output.buf) {
+		ckfree(output.buf);
+		output.buf = NULL;
+		output.nleft = 0;
+	}
+	INTON;
+}
+
+
+void
+outfmt(struct output *file, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	doformat(file, fmt, ap);
+	va_end(ap);
+}
+
+
+void
+out1fmt(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	doformat(out1, fmt, ap);
+	va_end(ap);
+}
+
+void
+dprintf(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	doformat(out2, fmt, ap);
+	va_end(ap);
+	flushout(out2);
+}
+
+void
+fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+{
+	va_list ap;
+	struct output strout;
+
+	va_start(ap, fmt);
+	strout.nextc = outbuf;
+	strout.nleft = length;
+	strout.fd = BLOCK_OUT;
+	strout.flags = 0;
+	doformat(&strout, fmt, ap);
+	outc('\0', &strout);
+	if (strout.flags & OUTPUT_ERR)
+		outbuf[length - 1] = '\0';
+	va_end(ap);
+}
+
+/*
+ * Formatted output.  This routine handles a subset of the printf formats:
+ * - Formats supported: d, u, o, p, X, s, and c.
+ * - The x format is also accepted but is treated like X.
+ * - The l, ll and q modifiers are accepted.
+ * - The - and # flags are accepted; # only works with the o format.
+ * - Width and precision may be specified with any format except c.
+ * - An * may be given for the width or precision.
+ * - The obsolete practice of preceding the width with a zero to get
+ *   zero padding is not supported; use the precision field.
+ * - A % may be printed by writing %% in the format string.
+ */
+
+#define TEMPSIZE 24
+
+#ifdef BSD4_4
+#define HAVE_VASPRINTF 1
+#endif
+
+void
+doformat(struct output *dest, const char *f, va_list ap)
+{
+#if	HAVE_VASPRINTF
+	char *s;
+
+	vasprintf(&s, f, ap);
+	outstr(s, dest);
+	free(s);     
+#else	/* !HAVE_VASPRINTF */
+	static const char digit[] = "0123456789ABCDEF";
+	char c;
+	char temp[TEMPSIZE];
+	int flushleft;
+	int sharp;
+	int width;
+	int prec;
+	int islong;
+	int isquad;
+	char *p;
+	int sign;
+#ifdef BSD4_4
+	quad_t l;
+	u_quad_t num;
+#else
+	long l;
+	u_long num;
+#endif
+	unsigned base;
+	int len;
+	int size;
+	int pad;
+
+	while ((c = *f++) != '\0') {
+		if (c != '%') {
+			outc(c, dest);
+			continue;
+		}
+		flushleft = 0;
+		sharp = 0;
+		width = 0;
+		prec = -1;
+		islong = 0;
+		isquad = 0;
+		for (;;) {
+			if (*f == '-')
+				flushleft++;
+			else if (*f == '#')
+				sharp++;
+			else
+				break;
+			f++;
+		}
+		if (*f == '*') {
+			width = va_arg(ap, int);
+			f++;
+		} else {
+			while (is_digit(*f)) {
+				width = 10 * width + digit_val(*f++);
+			}
+		}
+		if (*f == '.') {
+			if (*++f == '*') {
+				prec = va_arg(ap, int);
+				f++;
+			} else {
+				prec = 0;
+				while (is_digit(*f)) {
+					prec = 10 * prec + digit_val(*f++);
+				}
+			}
+		}
+		if (*f == 'l') {
+			f++;
+			if (*f == 'l') {
+				isquad++;
+				f++;
+			} else
+				islong++;
+		} else if (*f == 'q') {
+			isquad++;
+			f++;
+		}
+		switch (*f) {
+		case 'd':
+#ifdef BSD4_4
+			if (isquad)
+				l = va_arg(ap, quad_t);
+			else
+#endif
+			if (islong)
+				l = va_arg(ap, long);
+			else
+				l = va_arg(ap, int);
+			sign = 0;
+			num = l;
+			if (l < 0) {
+				num = -l;
+				sign = 1;
+			}
+			base = 10;
+			goto number;
+		case 'u':
+			base = 10;
+			goto uns_number;
+		case 'o':
+			base = 8;
+			goto uns_number;
+		case 'p':
+			outc('0', dest);
+			outc('x', dest);
+			/*FALLTHROUGH*/
+		case 'x':
+			/* we don't implement 'x'; treat like 'X' */
+		case 'X':
+			base = 16;
+uns_number:	  /* an unsigned number */
+			sign = 0;
+#ifdef BSD4_4
+			if (isquad)
+				num = va_arg(ap, u_quad_t);
+			else
+#endif
+			if (islong)
+				num = va_arg(ap, unsigned long);
+			else
+				num = va_arg(ap, unsigned int);
+number:		  /* process a number */
+			p = temp + TEMPSIZE - 1;
+			*p = '\0';
+			while (num) {
+				*--p = digit[num % base];
+				num /= base;
+			}
+			len = (temp + TEMPSIZE - 1) - p;
+			if (prec < 0)
+				prec = 1;
+			if (sharp && *f == 'o' && prec <= len)
+				prec = len + 1;
+			pad = 0;
+			if (width) {
+				size = len;
+				if (size < prec)
+					size = prec;
+				size += sign;
+				pad = width - size;
+				if (flushleft == 0) {
+					while (--pad >= 0)
+						outc(' ', dest);
+				}
+			}
+			if (sign)
+				outc('-', dest);
+			prec -= len;
+			while (--prec >= 0)
+				outc('0', dest);
+			while (*p)
+				outc(*p++, dest);
+			while (--pad >= 0)
+				outc(' ', dest);
+			break;
+		case 's':
+			p = va_arg(ap, char *);
+			pad = 0;
+			if (width) {
+				len = strlen(p);
+				if (prec >= 0 && len > prec)
+					len = prec;
+				pad = width - len;
+				if (flushleft == 0) {
+					while (--pad >= 0)
+						outc(' ', dest);
+				}
+			}
+			prec++;
+			while (--prec != 0 && *p)
+				outc(*p++, dest);
+			while (--pad >= 0)
+				outc(' ', dest);
+			break;
+		case 'c':
+			c = va_arg(ap, int);
+			outc(c, dest);
+			break;
+		default:
+			outc(*f, dest);
+			break;
+		}
+		f++;
+	}
+#endif	/* !HAVE_VASPRINTF */
+}
+
+
+
+/*
+ * Version of write which resumes after a signal is caught.
+ */
+
+int
+xwrite(int fd, char *buf, int nbytes)
+{
+	int ntry;
+	int i;
+	int n;
+
+	n = nbytes;
+	ntry = 0;
+	for (;;) {
+		i = write(fd, buf, n);
+		if (i > 0) {
+			if ((n -= i) <= 0)
+				return nbytes;
+			buf += i;
+			ntry = 0;
+		} else if (i == 0) {
+			if (++ntry > 10)
+				return nbytes - n;
+		} else if (errno != EINTR) {
+			return -1;
+		}
+	}
+}
+
+
+/*
+ * Version of ioctl that retries after a signal is caught.
+ * XXX unused function
+ */
+
+int
+xioctl(int fd, unsigned long request, char *arg)
+{
+	int i;
+
+	while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
+	return i;
+}
diff --git a/sh/output.h b/sh/output.h
new file mode 100644
index 0000000..9a199a0
--- /dev/null
+++ b/sh/output.h
@@ -0,0 +1,81 @@
+/*	$NetBSD: output.h,v 1.17 2003/08/07 09:05:36 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)output.h	8.2 (Berkeley) 5/4/95
+ */
+
+#ifndef OUTPUT_INCL
+
+#include <stdarg.h>
+
+struct output {
+	char *nextc;
+	int nleft;
+	char *buf;
+	int bufsize;
+	short fd;
+	short flags;
+};
+
+extern struct output output;
+extern struct output errout;
+extern struct output memout;
+extern struct output *out1;
+extern struct output *out2;
+
+void open_mem(char *, int, struct output *);
+void out1str(const char *);
+void out2str(const char *);
+void outstr(const char *, struct output *);
+void emptyoutbuf(struct output *);
+void flushall(void);
+void flushout(struct output *);
+void freestdout(void);
+void outfmt(struct output *, const char *, ...)
+    __attribute__((__format__(__printf__,2,3)));
+void out1fmt(const char *, ...)
+    __attribute__((__format__(__printf__,1,2)));
+void dprintf(const char *, ...)
+    __attribute__((__format__(__printf__,1,2)));
+void fmtstr(char *, size_t, const char *, ...)
+    __attribute__((__format__(__printf__,3,4)));
+void doformat(struct output *, const char *, va_list);
+int xwrite(int, char *, int);
+int xioctl(int, unsigned long, char *);
+
+#define outc(c, file)	(--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c)))
+#define out1c(c)	outc(c, out1);
+#define out2c(c)	outc(c, out2);
+
+#define OUTPUT_INCL
+#endif
diff --git a/sh/parser.c b/sh/parser.c
new file mode 100644
index 0000000..67de58e
--- /dev/null
+++ b/sh/parser.c
@@ -0,0 +1,1651 @@
+/*	$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
+#else
+__RCSID("$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+
+#include "shell.h"
+#include "parser.h"
+#include "nodes.h"
+#include "expand.h"	/* defines rmescapes() */
+#include "eval.h"	/* defines commandname */
+#include "redir.h"	/* defines copyfd() */
+#include "syntax.h"
+#include "options.h"
+#include "input.h"
+#include "output.h"
+#include "var.h"
+#include "error.h"
+#include "memalloc.h"
+#include "mystring.h"
+#include "alias.h"
+#include "show.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+
+/*
+ * Shell command parser.
+ */
+
+#define EOFMARKLEN 79
+
+/* values returned by readtoken */
+#include "token.h"
+
+#define OPENBRACE '{'
+#define CLOSEBRACE '}'
+
+
+struct heredoc {
+	struct heredoc *next;	/* next here document in list */
+	union node *here;		/* redirection node */
+	char *eofmark;		/* string indicating end of input */
+	int striptabs;		/* if set, strip leading tabs */
+};
+
+
+
+static int noalias = 0;		/* when set, don't handle aliases */
+struct heredoc *heredoclist;	/* list of here documents to read */
+int parsebackquote;		/* nonzero if we are inside backquotes */
+int doprompt;			/* if set, prompt the user */
+int needprompt;			/* true if interactive and at start of line */
+int lasttoken;			/* last token read */
+MKINIT int tokpushback;		/* last token pushed back */
+char *wordtext;			/* text of last word returned by readtoken */
+MKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
+struct nodelist *backquotelist;
+union node *redirnode;
+struct heredoc *heredoc;
+int quoteflag;			/* set if (part of) last token was quoted */
+int startlinno;			/* line # where last token started */
+
+
+STATIC union node *list(int);
+STATIC union node *andor(void);
+STATIC union node *pipeline(void);
+STATIC union node *command(void);
+STATIC union node *simplecmd(union node **, union node *);
+STATIC union node *makename(void);
+STATIC void parsefname(void);
+STATIC void parseheredoc(void);
+STATIC int peektoken(void);
+STATIC int readtoken(void);
+STATIC int xxreadtoken(void);
+STATIC int readtoken1(int, char const *, char *, int);
+STATIC int noexpand(char *);
+STATIC void synexpect(int) __attribute__((__noreturn__));
+STATIC void synerror(const char *) __attribute__((__noreturn__));
+STATIC void setprompt(int);
+
+
+/*
+ * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
+ * valid parse tree indicating a blank line.)
+ */
+
+union node *
+parsecmd(int interact)
+{
+	int t;
+
+	tokpushback = 0;
+	doprompt = interact;
+	if (doprompt)
+		setprompt(1);
+	else
+		setprompt(0);
+	needprompt = 0;
+	t = readtoken();
+	if (t == TEOF)
+		return NEOF;
+	if (t == TNL)
+		return NULL;
+	tokpushback++;
+	return list(1);
+}
+
+
+STATIC union node *
+list(int nlflag)
+{
+	union node *n1, *n2, *n3;
+	int tok;
+
+	checkkwd = 2;
+	if (nlflag == 0 && tokendlist[peektoken()])
+		return NULL;
+	n1 = NULL;
+	for (;;) {
+		n2 = andor();
+		tok = readtoken();
+		if (tok == TBACKGND) {
+			if (n2->type == NCMD || n2->type == NPIPE) {
+				n2->ncmd.backgnd = 1;
+			} else if (n2->type == NREDIR) {
+				n2->type = NBACKGND;
+			} else {
+				n3 = (union node *)stalloc(sizeof (struct nredir));
+				n3->type = NBACKGND;
+				n3->nredir.n = n2;
+				n3->nredir.redirect = NULL;
+				n2 = n3;
+			}
+		}
+		if (n1 == NULL) {
+			n1 = n2;
+		}
+		else {
+			n3 = (union node *)stalloc(sizeof (struct nbinary));
+			n3->type = NSEMI;
+			n3->nbinary.ch1 = n1;
+			n3->nbinary.ch2 = n2;
+			n1 = n3;
+		}
+		switch (tok) {
+		case TBACKGND:
+		case TSEMI:
+			tok = readtoken();
+			/* fall through */
+		case TNL:
+			if (tok == TNL) {
+				parseheredoc();
+				if (nlflag)
+					return n1;
+			} else {
+				tokpushback++;
+			}
+			checkkwd = 2;
+			if (tokendlist[peektoken()])
+				return n1;
+			break;
+		case TEOF:
+			if (heredoclist)
+				parseheredoc();
+			else
+				pungetc();		/* push back EOF on input */
+			return n1;
+		default:
+			if (nlflag)
+				synexpect(-1);
+			tokpushback++;
+			return n1;
+		}
+	}
+}
+
+
+
+STATIC union node *
+andor(void)
+{
+	union node *n1, *n2, *n3;
+	int t;
+
+	n1 = pipeline();
+	for (;;) {
+		if ((t = readtoken()) == TAND) {
+			t = NAND;
+		} else if (t == TOR) {
+			t = NOR;
+		} else {
+			tokpushback++;
+			return n1;
+		}
+		n2 = pipeline();
+		n3 = (union node *)stalloc(sizeof (struct nbinary));
+		n3->type = t;
+		n3->nbinary.ch1 = n1;
+		n3->nbinary.ch2 = n2;
+		n1 = n3;
+	}
+}
+
+
+
+STATIC union node *
+pipeline(void)
+{
+	union node *n1, *n2, *pipenode;
+	struct nodelist *lp, *prev;
+	int negate;
+
+	negate = 0;
+	TRACE(("pipeline: entered\n"));
+	while (readtoken() == TNOT)
+		negate = !negate;
+	tokpushback++;
+	n1 = command();
+	if (readtoken() == TPIPE) {
+		pipenode = (union node *)stalloc(sizeof (struct npipe));
+		pipenode->type = NPIPE;
+		pipenode->npipe.backgnd = 0;
+		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+		pipenode->npipe.cmdlist = lp;
+		lp->n = n1;
+		do {
+			prev = lp;
+			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+			lp->n = command();
+			prev->next = lp;
+		} while (readtoken() == TPIPE);
+		lp->next = NULL;
+		n1 = pipenode;
+	}
+	tokpushback++;
+	if (negate) {
+		n2 = (union node *)stalloc(sizeof (struct nnot));
+		n2->type = NNOT;
+		n2->nnot.com = n1;
+		return n2;
+	} else
+		return n1;
+}
+
+
+
+STATIC union node *
+command(void)
+{
+	union node *n1, *n2;
+	union node *ap, **app;
+	union node *cp, **cpp;
+	union node *redir, **rpp;
+	int t, negate = 0;
+
+	checkkwd = 2;
+	redir = NULL;
+	n1 = NULL;
+	rpp = &redir;
+
+	/* Check for redirection which may precede command */
+	while (readtoken() == TREDIR) {
+		*rpp = n2 = redirnode;
+		rpp = &n2->nfile.next;
+		parsefname();
+	}
+	tokpushback++;
+
+	while (readtoken() == TNOT) {
+		TRACE(("command: TNOT recognized\n"));
+		negate = !negate;
+	}
+	tokpushback++;
+
+	switch (readtoken()) {
+	case TIF:
+		n1 = (union node *)stalloc(sizeof (struct nif));
+		n1->type = NIF;
+		n1->nif.test = list(0);
+		if (readtoken() != TTHEN)
+			synexpect(TTHEN);
+		n1->nif.ifpart = list(0);
+		n2 = n1;
+		while (readtoken() == TELIF) {
+			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
+			n2 = n2->nif.elsepart;
+			n2->type = NIF;
+			n2->nif.test = list(0);
+			if (readtoken() != TTHEN)
+				synexpect(TTHEN);
+			n2->nif.ifpart = list(0);
+		}
+		if (lasttoken == TELSE)
+			n2->nif.elsepart = list(0);
+		else {
+			n2->nif.elsepart = NULL;
+			tokpushback++;
+		}
+		if (readtoken() != TFI)
+			synexpect(TFI);
+		checkkwd = 1;
+		break;
+	case TWHILE:
+	case TUNTIL: {
+		int got;
+		n1 = (union node *)stalloc(sizeof (struct nbinary));
+		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
+		n1->nbinary.ch1 = list(0);
+		if ((got=readtoken()) != TDO) {
+TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
+			synexpect(TDO);
+		}
+		n1->nbinary.ch2 = list(0);
+		if (readtoken() != TDONE)
+			synexpect(TDONE);
+		checkkwd = 1;
+		break;
+	}
+	case TFOR:
+		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
+			synerror("Bad for loop variable");
+		n1 = (union node *)stalloc(sizeof (struct nfor));
+		n1->type = NFOR;
+		n1->nfor.var = wordtext;
+		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
+			app = &ap;
+			while (readtoken() == TWORD) {
+				n2 = (union node *)stalloc(sizeof (struct narg));
+				n2->type = NARG;
+				n2->narg.text = wordtext;
+				n2->narg.backquote = backquotelist;
+				*app = n2;
+				app = &n2->narg.next;
+			}
+			*app = NULL;
+			n1->nfor.args = ap;
+			if (lasttoken != TNL && lasttoken != TSEMI)
+				synexpect(-1);
+		} else {
+			static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
+								   '@', '=', '\0'};
+			n2 = (union node *)stalloc(sizeof (struct narg));
+			n2->type = NARG;
+			n2->narg.text = argvars;
+			n2->narg.backquote = NULL;
+			n2->narg.next = NULL;
+			n1->nfor.args = n2;
+			/*
+			 * Newline or semicolon here is optional (but note
+			 * that the original Bourne shell only allowed NL).
+			 */
+			if (lasttoken != TNL && lasttoken != TSEMI)
+				tokpushback++;
+		}
+		checkkwd = 2;
+		if ((t = readtoken()) == TDO)
+			t = TDONE;
+		else if (t == TBEGIN)
+			t = TEND;
+		else
+			synexpect(-1);
+		n1->nfor.body = list(0);
+		if (readtoken() != t)
+			synexpect(t);
+		checkkwd = 1;
+		break;
+	case TCASE:
+		n1 = (union node *)stalloc(sizeof (struct ncase));
+		n1->type = NCASE;
+		if (readtoken() != TWORD)
+			synexpect(TWORD);
+		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
+		n2->type = NARG;
+		n2->narg.text = wordtext;
+		n2->narg.backquote = backquotelist;
+		n2->narg.next = NULL;
+		while (readtoken() == TNL);
+		if (lasttoken != TWORD || ! equal(wordtext, "in"))
+			synerror("expecting \"in\"");
+		cpp = &n1->ncase.cases;
+		noalias = 1;
+		checkkwd = 2, readtoken();
+		do {
+			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
+			cp->type = NCLIST;
+			app = &cp->nclist.pattern;
+			for (;;) {
+				*app = ap = (union node *)stalloc(sizeof (struct narg));
+				ap->type = NARG;
+				ap->narg.text = wordtext;
+				ap->narg.backquote = backquotelist;
+				if (checkkwd = 2, readtoken() != TPIPE)
+					break;
+				app = &ap->narg.next;
+				readtoken();
+			}
+			ap->narg.next = NULL;
+			noalias = 0;
+			if (lasttoken != TRP) {
+				synexpect(TRP);
+			}
+			cp->nclist.body = list(0);
+
+			checkkwd = 2;
+			if ((t = readtoken()) != TESAC) {
+				if (t != TENDCASE) {
+					noalias = 0;
+					synexpect(TENDCASE);
+				} else {
+					noalias = 1;
+					checkkwd = 2;
+					readtoken();
+				}
+			}
+			cpp = &cp->nclist.next;
+		} while(lasttoken != TESAC);
+		noalias = 0;
+		*cpp = NULL;
+		checkkwd = 1;
+		break;
+	case TLP:
+		n1 = (union node *)stalloc(sizeof (struct nredir));
+		n1->type = NSUBSHELL;
+		n1->nredir.n = list(0);
+		n1->nredir.redirect = NULL;
+		if (readtoken() != TRP)
+			synexpect(TRP);
+		checkkwd = 1;
+		break;
+	case TBEGIN:
+		n1 = list(0);
+		if (readtoken() != TEND)
+			synexpect(TEND);
+		checkkwd = 1;
+		break;
+	/* Handle an empty command like other simple commands.  */
+	case TSEMI:
+		/*
+		 * An empty command before a ; doesn't make much sense, and
+		 * should certainly be disallowed in the case of `if ;'.
+		 */
+		if (!redir)
+			synexpect(-1);
+	case TAND:
+	case TOR:
+	case TNL:
+	case TEOF:
+	case TWORD:
+	case TRP:
+		tokpushback++;
+		n1 = simplecmd(rpp, redir);
+		goto checkneg;
+	default:
+		synexpect(-1);
+		/* NOTREACHED */
+	}
+
+	/* Now check for redirection which may follow command */
+	while (readtoken() == TREDIR) {
+		*rpp = n2 = redirnode;
+		rpp = &n2->nfile.next;
+		parsefname();
+	}
+	tokpushback++;
+	*rpp = NULL;
+	if (redir) {
+		if (n1->type != NSUBSHELL) {
+			n2 = (union node *)stalloc(sizeof (struct nredir));
+			n2->type = NREDIR;
+			n2->nredir.n = n1;
+			n1 = n2;
+		}
+		n1->nredir.redirect = redir;
+	}
+
+checkneg:
+	if (negate) {
+		n2 = (union node *)stalloc(sizeof (struct nnot));
+		n2->type = NNOT;
+		n2->nnot.com = n1;
+		return n2;
+	}
+	else
+		return n1;
+}
+
+
+STATIC union node *
+simplecmd(union node **rpp, union node *redir)
+{
+	union node *args, **app;
+	union node **orig_rpp = rpp;
+	union node *n = NULL, *n2;
+	int negate = 0;
+
+	/* If we don't have any redirections already, then we must reset */
+	/* rpp to be the address of the local redir variable.  */
+	if (redir == 0)
+		rpp = &redir;
+
+	args = NULL;
+	app = &args;
+	/*
+	 * We save the incoming value, because we need this for shell
+	 * functions.  There can not be a redirect or an argument between
+	 * the function name and the open parenthesis.
+	 */
+	orig_rpp = rpp;
+
+	while (readtoken() == TNOT) {
+		TRACE(("command: TNOT recognized\n"));
+		negate = !negate;
+	}
+	tokpushback++;
+
+	for (;;) {
+		if (readtoken() == TWORD) {
+			n = (union node *)stalloc(sizeof (struct narg));
+			n->type = NARG;
+			n->narg.text = wordtext;
+			n->narg.backquote = backquotelist;
+			*app = n;
+			app = &n->narg.next;
+		} else if (lasttoken == TREDIR) {
+			*rpp = n = redirnode;
+			rpp = &n->nfile.next;
+			parsefname();	/* read name of redirection file */
+		} else if (lasttoken == TLP && app == &args->narg.next
+					    && rpp == orig_rpp) {
+			/* We have a function */
+			if (readtoken() != TRP)
+				synexpect(TRP);
+#ifdef notdef
+			if (! goodname(n->narg.text))
+				synerror("Bad function name");
+#endif
+			n->type = NDEFUN;
+			n->narg.next = command();
+			goto checkneg;
+		} else {
+			tokpushback++;
+			break;
+		}
+	}
+	*app = NULL;
+	*rpp = NULL;
+	n = (union node *)stalloc(sizeof (struct ncmd));
+	n->type = NCMD;
+	n->ncmd.backgnd = 0;
+	n->ncmd.args = args;
+	n->ncmd.redirect = redir;
+
+checkneg:
+	if (negate) {
+		n2 = (union node *)stalloc(sizeof (struct nnot));
+		n2->type = NNOT;
+		n2->nnot.com = n;
+		return n2;
+	}
+	else
+		return n;
+}
+
+STATIC union node *
+makename(void)
+{
+	union node *n;
+
+	n = (union node *)stalloc(sizeof (struct narg));
+	n->type = NARG;
+	n->narg.next = NULL;
+	n->narg.text = wordtext;
+	n->narg.backquote = backquotelist;
+	return n;
+}
+
+void fixredir(union node *n, const char *text, int err)
+	{
+	TRACE(("Fix redir %s %d\n", text, err));
+	if (!err)
+		n->ndup.vname = NULL;
+
+	if (is_digit(text[0]) && text[1] == '\0')
+		n->ndup.dupfd = digit_val(text[0]);
+	else if (text[0] == '-' && text[1] == '\0')
+		n->ndup.dupfd = -1;
+	else {
+
+		if (err)
+			synerror("Bad fd number");
+		else
+			n->ndup.vname = makename();
+	}
+}
+
+
+STATIC void
+parsefname(void)
+{
+	union node *n = redirnode;
+
+	if (readtoken() != TWORD)
+		synexpect(-1);
+	if (n->type == NHERE) {
+		struct heredoc *here = heredoc;
+		struct heredoc *p;
+		int i;
+
+		if (quoteflag == 0)
+			n->type = NXHERE;
+		TRACE(("Here document %d\n", n->type));
+		if (here->striptabs) {
+			while (*wordtext == '\t')
+				wordtext++;
+		}
+		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+			synerror("Illegal eof marker for << redirection");
+		rmescapes(wordtext);
+		here->eofmark = wordtext;
+		here->next = NULL;
+		if (heredoclist == NULL)
+			heredoclist = here;
+		else {
+			for (p = heredoclist ; p->next ; p = p->next);
+			p->next = here;
+		}
+	} else if (n->type == NTOFD || n->type == NFROMFD) {
+		fixredir(n, wordtext, 0);
+	} else {
+		n->nfile.fname = makename();
+	}
+}
+
+
+/*
+ * Input any here documents.
+ */
+
+STATIC void
+parseheredoc(void)
+{
+	struct heredoc *here;
+	union node *n;
+
+	while (heredoclist) {
+		here = heredoclist;
+		heredoclist = here->next;
+		if (needprompt) {
+			setprompt(2);
+			needprompt = 0;
+		}
+		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+				here->eofmark, here->striptabs);
+		n = (union node *)stalloc(sizeof (struct narg));
+		n->narg.type = NARG;
+		n->narg.next = NULL;
+		n->narg.text = wordtext;
+		n->narg.backquote = backquotelist;
+		here->here->nhere.doc = n;
+	}
+}
+
+STATIC int
+peektoken(void)
+{
+	int t;
+
+	t = readtoken();
+	tokpushback++;
+	return (t);
+}
+
+STATIC int
+readtoken(void)
+{
+	int t;
+	int savecheckkwd = checkkwd;
+#ifdef DEBUG
+	int alreadyseen = tokpushback;
+#endif
+	struct alias *ap;
+
+	top:
+	t = xxreadtoken();
+
+	if (checkkwd) {
+		/*
+		 * eat newlines
+		 */
+		if (checkkwd == 2) {
+			checkkwd = 0;
+			while (t == TNL) {
+				parseheredoc();
+				t = xxreadtoken();
+			}
+		} else
+			checkkwd = 0;
+		/*
+		 * check for keywords and aliases
+		 */
+		if (t == TWORD && !quoteflag)
+		{
+			const char *const *pp;
+
+			for (pp = parsekwd; *pp; pp++) {
+				if (**pp == *wordtext && equal(*pp, wordtext))
+				{
+					lasttoken = t = pp - 
+					    parsekwd + KWDOFFSET;
+					TRACE(("keyword %s recognized\n", tokname[t]));
+					goto out;
+				}
+			}
+			if(!noalias &&
+			    (ap = lookupalias(wordtext, 1)) != NULL) {
+				pushstring(ap->val, strlen(ap->val), ap);
+				checkkwd = savecheckkwd;
+				goto top;
+			}
+		}
+out:
+		checkkwd = (t == TNOT) ? savecheckkwd : 0;
+	}
+#ifdef DEBUG
+	if (!alreadyseen)
+	    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
+	else
+	    TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
+#endif
+	return (t);
+}
+
+
+/*
+ * Read the next input token.
+ * If the token is a word, we set backquotelist to the list of cmds in
+ *	backquotes.  We set quoteflag to true if any part of the word was
+ *	quoted.
+ * If the token is TREDIR, then we set redirnode to a structure containing
+ *	the redirection.
+ * In all cases, the variable startlinno is set to the number of the line
+ *	on which the token starts.
+ *
+ * [Change comment:  here documents and internal procedures]
+ * [Readtoken shouldn't have any arguments.  Perhaps we should make the
+ *  word parsing code into a separate routine.  In this case, readtoken
+ *  doesn't need to have any internal procedures, but parseword does.
+ *  We could also make parseoperator in essence the main routine, and
+ *  have parseword (readtoken1?) handle both words and redirection.]
+ */
+
+#define RETURN(token)	return lasttoken = token
+
+STATIC int
+xxreadtoken(void)
+{
+	int c;
+
+	if (tokpushback) {
+		tokpushback = 0;
+		return lasttoken;
+	}
+	if (needprompt) {
+		setprompt(2);
+		needprompt = 0;
+	}
+	startlinno = plinno;
+	for (;;) {	/* until token or start of word found */
+		c = pgetc_macro();
+		if (c == ' ' || c == '\t')
+			continue;		/* quick check for white space first */
+		switch (c) {
+		case ' ': case '\t':
+			continue;
+		case '#':
+			while ((c = pgetc()) != '\n' && c != PEOF);
+			pungetc();
+			continue;
+		case '\\':
+			if (pgetc() == '\n') {
+				startlinno = ++plinno;
+				if (doprompt)
+					setprompt(2);
+				else
+					setprompt(0);
+				continue;
+			}
+			pungetc();
+			goto breakloop;
+		case '\n':
+			plinno++;
+			needprompt = doprompt;
+			RETURN(TNL);
+		case PEOF:
+			RETURN(TEOF);
+		case '&':
+			if (pgetc() == '&')
+				RETURN(TAND);
+			pungetc();
+			RETURN(TBACKGND);
+		case '|':
+			if (pgetc() == '|')
+				RETURN(TOR);
+			pungetc();
+			RETURN(TPIPE);
+		case ';':
+			if (pgetc() == ';')
+				RETURN(TENDCASE);
+			pungetc();
+			RETURN(TSEMI);
+		case '(':
+			RETURN(TLP);
+		case ')':
+			RETURN(TRP);
+		default:
+			goto breakloop;
+		}
+	}
+breakloop:
+	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
+}
+
+
+
+/*
+ * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
+ * is not NULL, read a here document.  In the latter case, eofmark is the
+ * word which marks the end of the document and striptabs is true if
+ * leading tabs should be stripped from the document.  The argument firstc
+ * is the first character of the input token or document.
+ *
+ * Because C does not have internal subroutines, I have simulated them
+ * using goto's to implement the subroutine linkage.  The following macros
+ * will run code that appears at the end of readtoken1.
+ */
+
+#define CHECKEND()	{goto checkend; checkend_return:;}
+#define PARSEREDIR()	{goto parseredir; parseredir_return:;}
+#define PARSESUB()	{goto parsesub; parsesub_return:;}
+#define PARSEBACKQOLD()	{oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
+#define PARSEBACKQNEW()	{oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
+#define	PARSEARITH()	{goto parsearith; parsearith_return:;}
+
+/*
+ * Keep track of nested doublequotes in dblquote and doublequotep.
+ * We use dblquote for the first 32 levels, and we expand to a malloc'ed
+ * region for levels above that. Usually we never need to malloc.
+ * This code assumes that an int is 32 bits. We don't use uint32_t,
+ * because the rest of the code does not.
+ */
+#define ISDBLQUOTE() ((varnest < 32) ? (dblquote & (1 << varnest)) : \
+    (dblquotep[(varnest / 32) - 1] & (1 << (varnest % 32))))
+
+#define SETDBLQUOTE() \
+    if (varnest < 32) \
+	dblquote |= (1 << varnest); \
+    else \
+	dblquotep[(varnest / 32) - 1] |= (1 << (varnest % 32))
+
+#define CLRDBLQUOTE() \
+    if (varnest < 32) \
+	dblquote &= ~(1 << varnest); \
+    else \
+	dblquotep[(varnest / 32) - 1] &= ~(1 << (varnest % 32))
+
+STATIC int
+readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs)
+{
+	int c = firstc;
+	char *out;
+	int len;
+	char line[EOFMARKLEN + 1];
+	struct nodelist *bqlist;
+	int quotef;
+	int *dblquotep = NULL;
+	size_t maxnest = 32;
+	int dblquote;
+	int varnest;	/* levels of variables expansion */
+	int arinest;	/* levels of arithmetic expansion */
+	int parenlevel;	/* levels of parens in arithmetic */
+	int oldstyle;
+	char const *prevsyntax = NULL;	/* syntax before arithmetic */
+#if __GNUC__
+	/* Avoid longjmp clobbering */
+	(void) &maxnest;
+	(void) &dblquotep;
+	(void) &out;
+	(void) &quotef;
+	(void) &dblquote;
+	(void) &varnest;
+	(void) &arinest;
+	(void) &parenlevel;
+	(void) &oldstyle;
+	(void) &prevsyntax;
+	(void) &syntax;
+#endif
+
+	startlinno = plinno;
+	dblquote = 0;
+	varnest = 0;
+	if (syntax == DQSYNTAX) {
+		SETDBLQUOTE();
+	}
+	quotef = 0;
+	bqlist = NULL;
+	arinest = 0;
+	parenlevel = 0;
+
+	STARTSTACKSTR(out);
+	loop: {	/* for each line, until end of word */
+#if ATTY
+		if (c == '\034' && doprompt
+		 && attyset() && ! equal(termval(), "emacs")) {
+			attyline();
+			if (syntax == BASESYNTAX)
+				return readtoken();
+			c = pgetc();
+			goto loop;
+		}
+#endif
+		CHECKEND();	/* set c to PEOF if at end of here document */
+		for (;;) {	/* until end of line or end of word */
+			CHECKSTRSPACE(4, out);	/* permit 4 calls to USTPUTC */
+			switch(syntax[c]) {
+			case CNL:	/* '\n' */
+				if (syntax == BASESYNTAX)
+					goto endword;	/* exit outer loop */
+				USTPUTC(c, out);
+				plinno++;
+				if (doprompt)
+					setprompt(2);
+				else
+					setprompt(0);
+				c = pgetc();
+				goto loop;		/* continue outer loop */
+			case CWORD:
+				USTPUTC(c, out);
+				break;
+			case CCTL:
+				if (eofmark == NULL || ISDBLQUOTE())
+					USTPUTC(CTLESC, out);
+				USTPUTC(c, out);
+				break;
+			case CBACK:	/* backslash */
+				c = pgetc();
+				if (c == PEOF) {
+					USTPUTC('\\', out);
+					pungetc();
+					break;
+				}
+				if (c == '\n') {
+					if (doprompt)
+						setprompt(2);
+					else
+						setprompt(0);
+					break;
+				}
+				quotef = 1;
+				if (ISDBLQUOTE() && c != '\\' &&
+				    c != '`' && c != '$' &&
+				    (c != '"' || eofmark != NULL))
+					USTPUTC('\\', out);
+				if (SQSYNTAX[c] == CCTL)
+					USTPUTC(CTLESC, out);
+				else if (eofmark == NULL) {
+					USTPUTC(CTLQUOTEMARK, out);
+					USTPUTC(c, out);
+					if (varnest != 0)
+						USTPUTC(CTLQUOTEEND, out);
+					break;
+				}
+				USTPUTC(c, out);
+				break;
+			case CSQUOTE:
+				if (syntax != SQSYNTAX) {
+					if (eofmark == NULL)
+						USTPUTC(CTLQUOTEMARK, out);
+					quotef = 1;
+					syntax = SQSYNTAX;
+					break;
+				}
+				if (eofmark != NULL && arinest == 0 &&
+				    varnest == 0) {
+					/* Ignore inside quoted here document */
+					USTPUTC(c, out);
+					break;
+				}
+				/* End of single quotes... */
+				if (arinest)
+					syntax = ARISYNTAX;
+				else {
+					syntax = BASESYNTAX;
+					if (varnest != 0)
+						USTPUTC(CTLQUOTEEND, out);
+				}
+				break;
+			case CDQUOTE:
+				if (eofmark != NULL && arinest == 0 &&
+				    varnest == 0) {
+					/* Ignore inside here document */
+					USTPUTC(c, out);
+					break;
+				}
+				quotef = 1;
+				if (arinest) {
+					if (ISDBLQUOTE()) {
+						syntax = ARISYNTAX;
+						CLRDBLQUOTE();
+					} else {
+						syntax = DQSYNTAX;
+						SETDBLQUOTE();
+						USTPUTC(CTLQUOTEMARK, out);
+					}
+					break;
+				}
+				if (eofmark != NULL)
+					break;
+				if (ISDBLQUOTE()) {
+					if (varnest != 0)
+						USTPUTC(CTLQUOTEEND, out);
+					syntax = BASESYNTAX;
+					CLRDBLQUOTE();
+				} else {
+					syntax = DQSYNTAX;
+					SETDBLQUOTE();
+					USTPUTC(CTLQUOTEMARK, out);
+				}
+				break;
+			case CVAR:	/* '$' */
+				PARSESUB();		/* parse substitution */
+				break;
+			case CENDVAR:	/* CLOSEBRACE */
+				if (varnest > 0 && !ISDBLQUOTE()) {
+					varnest--;
+					USTPUTC(CTLENDVAR, out);
+				} else {
+					USTPUTC(c, out);
+				}
+				break;
+			case CLP:	/* '(' in arithmetic */
+				parenlevel++;
+				USTPUTC(c, out);
+				break;
+			case CRP:	/* ')' in arithmetic */
+				if (parenlevel > 0) {
+					USTPUTC(c, out);
+					--parenlevel;
+				} else {
+					if (pgetc() == ')') {
+						if (--arinest == 0) {
+							USTPUTC(CTLENDARI, out);
+							syntax = prevsyntax;
+							if (syntax == DQSYNTAX)
+								SETDBLQUOTE();
+							else
+								CLRDBLQUOTE();
+						} else
+							USTPUTC(')', out);
+					} else {
+						/*
+						 * unbalanced parens
+						 *  (don't 2nd guess - no error)
+						 */
+						pungetc();
+						USTPUTC(')', out);
+					}
+				}
+				break;
+			case CBQUOTE:	/* '`' */
+				PARSEBACKQOLD();
+				break;
+			case CEOF:
+				goto endword;		/* exit outer loop */
+			default:
+				if (varnest == 0)
+					goto endword;	/* exit outer loop */
+				USTPUTC(c, out);
+			}
+			c = pgetc_macro();
+		}
+	}
+endword:
+	if (syntax == ARISYNTAX)
+		synerror("Missing '))'");
+	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
+		synerror("Unterminated quoted string");
+	if (varnest != 0) {
+		startlinno = plinno;
+		/* { */
+		synerror("Missing '}'");
+	}
+	USTPUTC('\0', out);
+	len = out - stackblock();
+	out = stackblock();
+	if (eofmark == NULL) {
+		if ((c == '>' || c == '<')
+		 && quotef == 0
+		 && len <= 2
+		 && (*out == '\0' || is_digit(*out))) {
+			PARSEREDIR();
+			return lasttoken = TREDIR;
+		} else {
+			pungetc();
+		}
+	}
+	quoteflag = quotef;
+	backquotelist = bqlist;
+	grabstackblock(len);
+	wordtext = out;
+	if (dblquotep != NULL)
+	    ckfree(dblquotep);
+	return lasttoken = TWORD;
+/* end of readtoken routine */
+
+
+
+/*
+ * Check to see whether we are at the end of the here document.  When this
+ * is called, c is set to the first character of the next input line.  If
+ * we are at the end of the here document, this routine sets the c to PEOF.
+ */
+
+checkend: {
+	if (eofmark) {
+		if (striptabs) {
+			while (c == '\t')
+				c = pgetc();
+		}
+		if (c == *eofmark) {
+			if (pfgets(line, sizeof line) != NULL) {
+				char *p, *q;
+
+				p = line;
+				for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
+				if (*p == '\n' && *q == '\0') {
+					c = PEOF;
+					plinno++;
+					needprompt = doprompt;
+				} else {
+					pushstring(line, strlen(line), NULL);
+				}
+			}
+		}
+	}
+	goto checkend_return;
+}
+
+
+/*
+ * Parse a redirection operator.  The variable "out" points to a string
+ * specifying the fd to be redirected.  The variable "c" contains the
+ * first character of the redirection operator.
+ */
+
+parseredir: {
+	char fd = *out;
+	union node *np;
+
+	np = (union node *)stalloc(sizeof (struct nfile));
+	if (c == '>') {
+		np->nfile.fd = 1;
+		c = pgetc();
+		if (c == '>')
+			np->type = NAPPEND;
+		else if (c == '|')
+			np->type = NCLOBBER;
+		else if (c == '&')
+			np->type = NTOFD;
+		else {
+			np->type = NTO;
+			pungetc();
+		}
+	} else {	/* c == '<' */
+		np->nfile.fd = 0;
+		switch (c = pgetc()) {
+		case '<':
+			if (sizeof (struct nfile) != sizeof (struct nhere)) {
+				np = (union node *)stalloc(sizeof (struct nhere));
+				np->nfile.fd = 0;
+			}
+			np->type = NHERE;
+			heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
+			heredoc->here = np;
+			if ((c = pgetc()) == '-') {
+				heredoc->striptabs = 1;
+			} else {
+				heredoc->striptabs = 0;
+				pungetc();
+			}
+			break;
+
+		case '&':
+			np->type = NFROMFD;
+			break;
+
+		case '>':
+			np->type = NFROMTO;
+			break;
+
+		default:
+			np->type = NFROM;
+			pungetc();
+			break;
+		}
+	}
+	if (fd != '\0')
+		np->nfile.fd = digit_val(fd);
+	redirnode = np;
+	goto parseredir_return;
+}
+
+
+/*
+ * Parse a substitution.  At this point, we have read the dollar sign
+ * and nothing else.
+ */
+
+parsesub: {
+	int subtype;
+	int typeloc;
+	int flags;
+	char *p;
+	static const char types[] = "}-+?=";
+
+	c = pgetc();
+	if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
+		USTPUTC('$', out);
+		pungetc();
+	} else if (c == '(') {	/* $(command) or $((arith)) */
+		if (pgetc() == '(') {
+			PARSEARITH();
+		} else {
+			pungetc();
+			PARSEBACKQNEW();
+		}
+	} else {
+		USTPUTC(CTLVAR, out);
+		typeloc = out - stackblock();
+		USTPUTC(VSNORMAL, out);
+		subtype = VSNORMAL;
+		if (c == OPENBRACE) {
+			c = pgetc();
+			if (c == '#') {
+				if ((c = pgetc()) == CLOSEBRACE)
+					c = '#';
+				else
+					subtype = VSLENGTH;
+			}
+			else
+				subtype = 0;
+		}
+		if (is_name(c)) {
+			do {
+				STPUTC(c, out);
+				c = pgetc();
+			} while (is_in_name(c));
+		} else if (is_digit(c)) {
+			do {
+				USTPUTC(c, out);
+				c = pgetc();
+			} while (is_digit(c));
+		}
+		else if (is_special(c)) {
+			USTPUTC(c, out);
+			c = pgetc();
+		}
+		else
+badsub:			synerror("Bad substitution");
+
+		STPUTC('=', out);
+		flags = 0;
+		if (subtype == 0) {
+			switch (c) {
+			case ':':
+				flags = VSNUL;
+				c = pgetc();
+				/*FALLTHROUGH*/
+			default:
+				p = strchr(types, c);
+				if (p == NULL)
+					goto badsub;
+				subtype = p - types + VSNORMAL;
+				break;
+			case '%':
+			case '#':
+				{
+					int cc = c;
+					subtype = c == '#' ? VSTRIMLEFT :
+							     VSTRIMRIGHT;
+					c = pgetc();
+					if (c == cc)
+						subtype++;
+					else
+						pungetc();
+					break;
+				}
+			}
+		} else {
+			pungetc();
+		}
+		if (ISDBLQUOTE() || arinest)
+			flags |= VSQUOTE;
+		*(stackblock() + typeloc) = subtype | flags;
+		if (subtype != VSNORMAL) {
+			varnest++;
+			if (varnest >= maxnest) {
+				dblquotep = ckrealloc(dblquotep, maxnest / 8);
+				dblquotep[(maxnest / 32) - 1] = 0;
+				maxnest += 32;
+			}
+		}
+	}
+	goto parsesub_return;
+}
+
+
+/*
+ * Called to parse command substitutions.  Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
+ */
+
+parsebackq: {
+	struct nodelist **nlpp;
+	int savepbq;
+	union node *n;
+	char *volatile str;
+	struct jmploc jmploc;
+	struct jmploc *volatile savehandler;
+	int savelen;
+	int saveprompt;
+#ifdef __GNUC__
+	(void) &saveprompt;
+#endif
+
+	savepbq = parsebackquote;
+	if (setjmp(jmploc.loc)) {
+		if (str)
+			ckfree(str);
+		parsebackquote = 0;
+		handler = savehandler;
+		longjmp(handler->loc, 1);
+	}
+	INTOFF;
+	str = NULL;
+	savelen = out - stackblock();
+	if (savelen > 0) {
+		str = ckmalloc(savelen);
+		memcpy(str, stackblock(), savelen);
+	}
+	savehandler = handler;
+	handler = &jmploc;
+	INTON;
+        if (oldstyle) {
+                /* We must read until the closing backquote, giving special
+                   treatment to some slashes, and then push the string and
+                   reread it as input, interpreting it normally.  */
+                char *pout;
+                int pc;
+                int psavelen;
+                char *pstr;
+
+
+                STARTSTACKSTR(pout);
+		for (;;) {
+			if (needprompt) {
+				setprompt(2);
+				needprompt = 0;
+			}
+			switch (pc = pgetc()) {
+			case '`':
+				goto done;
+
+			case '\\':
+                                if ((pc = pgetc()) == '\n') {
+					plinno++;
+					if (doprompt)
+						setprompt(2);
+					else
+						setprompt(0);
+					/*
+					 * If eating a newline, avoid putting
+					 * the newline into the new character
+					 * stream (via the STPUTC after the
+					 * switch).
+					 */
+					continue;
+				}
+                                if (pc != '\\' && pc != '`' && pc != '$'
+                                    && (!ISDBLQUOTE() || pc != '"'))
+                                        STPUTC('\\', pout);
+				break;
+
+			case '\n':
+				plinno++;
+				needprompt = doprompt;
+				break;
+
+			case PEOF:
+			        startlinno = plinno;
+				synerror("EOF in backquote substitution");
+ 				break;
+
+			default:
+				break;
+			}
+			STPUTC(pc, pout);
+                }
+done:
+                STPUTC('\0', pout);
+                psavelen = pout - stackblock();
+                if (psavelen > 0) {
+			pstr = grabstackstr(pout);
+			setinputstring(pstr, 1);
+                }
+        }
+	nlpp = &bqlist;
+	while (*nlpp)
+		nlpp = &(*nlpp)->next;
+	*nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+	(*nlpp)->next = NULL;
+	parsebackquote = oldstyle;
+
+	if (oldstyle) {
+		saveprompt = doprompt;
+		doprompt = 0;
+	}
+
+	n = list(0);
+
+	if (oldstyle)
+		doprompt = saveprompt;
+	else {
+		if (readtoken() != TRP)
+			synexpect(TRP);
+	}
+
+	(*nlpp)->n = n;
+        if (oldstyle) {
+		/*
+		 * Start reading from old file again, ignoring any pushed back
+		 * tokens left from the backquote parsing
+		 */
+                popfile();
+		tokpushback = 0;
+	}
+	while (stackblocksize() <= savelen)
+		growstackblock();
+	STARTSTACKSTR(out);
+	if (str) {
+		memcpy(out, str, savelen);
+		STADJUST(savelen, out);
+		INTOFF;
+		ckfree(str);
+		str = NULL;
+		INTON;
+	}
+	parsebackquote = savepbq;
+	handler = savehandler;
+	if (arinest || ISDBLQUOTE())
+		USTPUTC(CTLBACKQ | CTLQUOTE, out);
+	else
+		USTPUTC(CTLBACKQ, out);
+	if (oldstyle)
+		goto parsebackq_oldreturn;
+	else
+		goto parsebackq_newreturn;
+}
+
+/*
+ * Parse an arithmetic expansion (indicate start of one and set state)
+ */
+parsearith: {
+
+	if (++arinest == 1) {
+		prevsyntax = syntax;
+		syntax = ARISYNTAX;
+		USTPUTC(CTLARI, out);
+		if (ISDBLQUOTE())
+			USTPUTC('"',out);
+		else
+			USTPUTC(' ',out);
+	} else {
+		/*
+		 * we collapse embedded arithmetic expansion to
+		 * parenthesis, which should be equivalent
+		 */
+		USTPUTC('(', out);
+	}
+	goto parsearith_return;
+}
+
+} /* end of readtoken */
+
+
+
+#ifdef mkinit
+RESET {
+	tokpushback = 0;
+	checkkwd = 0;
+}
+#endif
+
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+
+STATIC int
+noexpand(char *text)
+{
+	char *p;
+	char c;
+
+	p = text;
+	while ((c = *p++) != '\0') {
+		if (c == CTLQUOTEMARK)
+			continue;
+		if (c == CTLESC)
+			p++;
+		else if (BASESYNTAX[(int)c] == CCTL)
+			return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * Return true if the argument is a legal variable name (a letter or
+ * underscore followed by zero or more letters, underscores, and digits).
+ */
+
+int
+goodname(char *name)
+	{
+	char *p;
+
+	p = name;
+	if (! is_name(*p))
+		return 0;
+	while (*++p) {
+		if (! is_in_name(*p))
+			return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * Called when an unexpected token is read during the parse.  The argument
+ * is the token that is expected, or -1 if more than one type of token can
+ * occur at this point.
+ */
+
+STATIC void
+synexpect(int token)
+{
+	char msg[64];
+
+	if (token >= 0) {
+		fmtstr(msg, 64, "%s unexpected (expecting %s)",
+			tokname[lasttoken], tokname[token]);
+	} else {
+		fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
+	}
+	synerror(msg);
+	/* NOTREACHED */
+}
+
+
+STATIC void
+synerror(const char *msg)
+{
+	if (commandname)
+		outfmt(&errout, "%s: %d: ", commandname, startlinno);
+	outfmt(&errout, "Syntax error: %s\n", msg);
+	error((char *)NULL);
+	/* NOTREACHED */
+}
+
+STATIC void
+setprompt(int which)
+{
+	whichprompt = which;
+
+#ifdef WITH_HISTORY
+	if (!el)
+#endif
+		out2str(getprompt(NULL));
+}
+
+/*
+ * called by editline -- any expansions to the prompt
+ *    should be added here.
+ */
+const char *
+getprompt(void *unused)
+	{
+	switch (whichprompt) {
+	case 0:
+		return "";
+	case 1:
+		return ps1val();
+	case 2:
+		return ps2val();
+	default:
+		return "<internal prompt error>";
+	}
+}
diff --git a/sh/parser.h b/sh/parser.h
new file mode 100644
index 0000000..b343c71
--- /dev/null
+++ b/sh/parser.h
@@ -0,0 +1,82 @@
+/*	$NetBSD: parser.h,v 1.17 2004/06/26 22:09:49 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)parser.h	8.3 (Berkeley) 5/4/95
+ */
+
+/* control characters in argument strings */
+#define CTL_FIRST '\201'	/* first 'special' character */
+#define CTLESC '\201'		/* escape next character */
+#define CTLVAR '\202'		/* variable defn */
+#define CTLENDVAR '\203'
+#define CTLBACKQ '\204'
+#define CTLQUOTE 01		/* ored with CTLBACKQ code if in quotes */
+/*	CTLBACKQ | CTLQUOTE == '\205' */
+#define	CTLARI	'\206'		/* arithmetic expression */
+#define	CTLENDARI '\207'
+#define	CTLQUOTEMARK '\210'
+#define	CTLQUOTEEND '\211'	/* only inside ${...} */
+#define	CTL_LAST '\211'		/* last 'special' character */
+
+/* variable substitution byte (follows CTLVAR) */
+#define VSTYPE	0x0f		/* type of variable substitution */
+#define VSNUL	0x10		/* colon--treat the empty string as unset */
+#define VSQUOTE 0x80		/* inside double quotes--suppress splitting */
+
+/* values of VSTYPE field */
+#define VSNORMAL	0x1		/* normal variable:  $var or ${var} */
+#define VSMINUS		0x2		/* ${var-text} */
+#define VSPLUS		0x3		/* ${var+text} */
+#define VSQUESTION	0x4		/* ${var?message} */
+#define VSASSIGN	0x5		/* ${var=text} */
+#define VSTRIMLEFT	0x6		/* ${var#pattern} */
+#define VSTRIMLEFTMAX	0x7		/* ${var##pattern} */
+#define VSTRIMRIGHT	0x8		/* ${var%pattern} */
+#define VSTRIMRIGHTMAX 	0x9		/* ${var%%pattern} */
+#define VSLENGTH	0xa		/* ${#var} */
+
+
+/*
+ * NEOF is returned by parsecmd when it encounters an end of file.  It
+ * must be distinct from NULL, so we use the address of a variable that
+ * happens to be handy.
+ */
+extern int tokpushback;
+#define NEOF ((union node *)&tokpushback)
+extern int whichprompt;		/* 1 == PS1, 2 == PS2 */
+
+
+union node *parsecmd(int);
+void fixredir(union node *, const char *, int);
+int goodname(char *);
+const char *getprompt(void *);
diff --git a/sh/redir.c b/sh/redir.c
new file mode 100644
index 0000000..5c4c286
--- /dev/null
+++ b/sh/redir.c
@@ -0,0 +1,389 @@
+/*	$NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)redir.c	8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: redir.c,v 1.29 2004/07/08 03:57:33 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>	/* PIPE_BUF */
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * Code for dealing with input/output redirection.
+ */
+
+#include "main.h"
+#include "shell.h"
+#include "nodes.h"
+#include "jobs.h"
+#include "options.h"
+#include "expand.h"
+#include "redir.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+
+
+#define EMPTY -2		/* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096		/* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+
+#define signal bsd_signal
+
+MKINIT
+struct redirtab {
+	struct redirtab *next;
+	short renamed[10];
+};
+
+
+MKINIT struct redirtab *redirlist;
+
+/*
+ * We keep track of whether or not fd0 has been redirected.  This is for
+ * background commands, where we want to redirect fd0 to /dev/null only
+ * if it hasn't already been redirected.
+*/
+int fd0_redirected = 0;
+
+STATIC void openredirect(union node *, char[10], int);
+STATIC int openhere(union node *);
+
+
+/*
+ * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
+ */
+
+void
+redirect(union node *redir, int flags)
+{
+	union node *n;
+	struct redirtab *sv = NULL;
+	int i;
+	int fd;
+	int try;
+	char memory[10];	/* file descriptors to write to memory */
+
+	for (i = 10 ; --i >= 0 ; )
+		memory[i] = 0;
+	memory[1] = flags & REDIR_BACKQ;
+	if (flags & REDIR_PUSH) {
+		/* We don't have to worry about REDIR_VFORK here, as
+		 * flags & REDIR_PUSH is never true if REDIR_VFORK is set.
+		 */
+		sv = ckmalloc(sizeof (struct redirtab));
+		for (i = 0 ; i < 10 ; i++)
+			sv->renamed[i] = EMPTY;
+		sv->next = redirlist;
+		redirlist = sv;
+	}
+	for (n = redir ; n ; n = n->nfile.next) {
+		fd = n->nfile.fd;
+		try = 0;
+		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
+		    n->ndup.dupfd == fd)
+			continue; /* redirect from/to same file descriptor */
+
+		if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
+			INTOFF;
+again:
+			if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
+				switch (errno) {
+				case EBADF:
+					if (!try) {
+						openredirect(n, memory, flags);
+						try++;
+						goto again;
+					}
+					/* FALLTHROUGH*/
+				default:
+					INTON;
+					error("%d: %s", fd, strerror(errno));
+					/* NOTREACHED */
+				}
+			}
+			if (!try) {
+				sv->renamed[fd] = i;
+				close(fd);
+			}
+			INTON;
+		} else {
+			close(fd);
+		}
+                if (fd == 0)
+                        fd0_redirected++;
+		if (!try)
+			openredirect(n, memory, flags);
+	}
+	if (memory[1])
+		out1 = &memout;
+	if (memory[2])
+		out2 = &memout;
+}
+
+
+STATIC void
+openredirect(union node *redir, char memory[10], int flags)
+{
+	int fd = redir->nfile.fd;
+	char *fname;
+	int f;
+	int oflags = O_WRONLY|O_CREAT|O_TRUNC, eflags;
+
+	/*
+	 * We suppress interrupts so that we won't leave open file
+	 * descriptors around.  This may not be such a good idea because
+	 * an open of a device or a fifo can block indefinitely.
+	 */
+	INTOFF;
+	memory[fd] = 0;
+	switch (redir->nfile.type) {
+	case NFROM:
+		fname = redir->nfile.expfname;
+		if (flags & REDIR_VFORK)
+			eflags = O_NONBLOCK;
+		else
+			eflags = 0;
+		if ((f = open(fname, O_RDONLY|eflags)) < 0)
+			goto eopen;
+		if (eflags)
+			(void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags);
+		break;
+	case NFROMTO:
+		fname = redir->nfile.expfname;
+		if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+			goto ecreate;
+		break;
+	case NTO:
+		if (Cflag)
+			oflags |= O_EXCL;
+		/* FALLTHROUGH */
+	case NCLOBBER:
+		fname = redir->nfile.expfname;
+		if ((f = open(fname, oflags, 0666)) < 0)
+			goto ecreate;
+		break;
+	case NAPPEND:
+		fname = redir->nfile.expfname;
+		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+			goto ecreate;
+		break;
+	case NTOFD:
+	case NFROMFD:
+		if (redir->ndup.dupfd >= 0) {	/* if not ">&-" */
+			if (memory[redir->ndup.dupfd])
+				memory[fd] = 1;
+			else
+				copyfd(redir->ndup.dupfd, fd);
+		}
+		INTON;
+		return;
+	case NHERE:
+	case NXHERE:
+		f = openhere(redir);
+		break;
+	default:
+		abort();
+	}
+
+	if (f != fd) {
+		copyfd(f, fd);
+		close(f);
+	}
+	INTON;
+	return;
+ecreate:
+	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+}
+
+
+/*
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+
+STATIC int
+openhere(union node *redir)
+{
+	int pip[2];
+	int len = 0;
+
+	if (pipe(pip) < 0)
+		error("Pipe call failed");
+	if (redir->type == NHERE) {
+		len = strlen(redir->nhere.doc->narg.text);
+		if (len <= PIPESIZE) {
+			xwrite(pip[1], redir->nhere.doc->narg.text, len);
+			goto out;
+		}
+	}
+	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+		close(pip[0]);
+		signal(SIGINT, SIG_IGN);
+		signal(SIGQUIT, SIG_IGN);
+		signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+		signal(SIGTSTP, SIG_IGN);
+#endif
+		signal(SIGPIPE, SIG_DFL);
+		if (redir->type == NHERE)
+			xwrite(pip[1], redir->nhere.doc->narg.text, len);
+		else
+			expandhere(redir->nhere.doc, pip[1]);
+		_exit(0);
+	}
+out:
+	close(pip[1]);
+	return pip[0];
+}
+
+
+
+/*
+ * Undo the effects of the last redirection.
+ */
+
+void
+popredir(void)
+{
+	struct redirtab *rp = redirlist;
+	int i;
+
+	for (i = 0 ; i < 10 ; i++) {
+		if (rp->renamed[i] != EMPTY) {
+                        if (i == 0)
+                                fd0_redirected--;
+			close(i);
+			if (rp->renamed[i] >= 0) {
+				copyfd(rp->renamed[i], i);
+				close(rp->renamed[i]);
+			}
+		}
+	}
+	INTOFF;
+	redirlist = rp->next;
+	ckfree(rp);
+	INTON;
+}
+
+/*
+ * Undo all redirections.  Called on error or interrupt.
+ */
+
+#ifdef mkinit
+
+INCLUDE "redir.h"
+
+RESET {
+	while (redirlist)
+		popredir();
+}
+
+SHELLPROC {
+	clearredir(0);
+}
+
+#endif
+
+/* Return true if fd 0 has already been redirected at least once.  */
+int
+fd0_redirected_p () {
+        return fd0_redirected != 0;
+}
+
+/*
+ * Discard all saved file descriptors.
+ */
+
+void
+clearredir(vforked)
+	int vforked;
+{
+	struct redirtab *rp;
+	int i;
+
+	for (rp = redirlist ; rp ; rp = rp->next) {
+		for (i = 0 ; i < 10 ; i++) {
+			if (rp->renamed[i] >= 0) {
+				close(rp->renamed[i]);
+			}
+			if (!vforked)
+				rp->renamed[i] = EMPTY;
+		}
+	}
+}
+
+
+
+/*
+ * Copy a file descriptor to be >= to.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+
+int
+copyfd(int from, int to)
+{
+	int newfd;
+
+	newfd = fcntl(from, F_DUPFD, to);
+	if (newfd < 0) {
+		if (errno == EMFILE)
+			return EMPTY;
+		else
+			error("%d: %s", from, strerror(errno));
+	}
+	return newfd;
+}
diff --git a/sh/redir.h b/sh/redir.h
new file mode 100644
index 0000000..c9709e9
--- /dev/null
+++ b/sh/redir.h
@@ -0,0 +1,48 @@
+/*	$NetBSD: redir.h,v 1.15 2003/08/07 09:05:37 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)redir.h	8.2 (Berkeley) 5/4/95
+ */
+
+/* flags passed to redirect */
+#define REDIR_PUSH 01		/* save previous values of file descriptors */
+#define REDIR_BACKQ 02		/* save the command output in memory */
+#define REDIR_VFORK 04		/* running under vfork(2), be careful */
+
+union node;
+void redirect(union node *, int);
+void popredir(void);
+int fd0_redirected_p(void);
+void clearredir(int);
+int copyfd(int, int);
+
diff --git a/sh/sh.1 b/sh/sh.1
new file mode 100644
index 0000000..3ef55b4
--- /dev/null
+++ b/sh/sh.1
@@ -0,0 +1,1928 @@
+.\"	$NetBSD: sh.1,v 1.78 2004/06/03 19:54:37 hubertf Exp $
+.\" Copyright (c) 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Kenneth Almquist.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"	@(#)sh.1	8.6 (Berkeley) 5/4/95
+.\"
+.Dd April 17, 2004
+.Os
+.Dt SH 1
+.Sh NAME
+.Nm sh
+.Nd command interpreter (shell)
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl aCefnuvxIimqVEb
+.Op Cm +aCefnuvxIimqVEb
+.Ek
+.Bk -words
+.Op Fl o Ar option_name
+.Op Cm +o Ar option_name
+.Ek
+.Bk -words
+.Op Ar command_file Oo Ar argument ... Oc
+.Ek
+.Nm
+.Fl c
+.Bk -words
+.Op Fl aCefnuvxIimqVEb
+.Op Cm +aCefnuvxIimqVEb
+.Ek
+.Bk -words
+.Op Fl o Ar option_name
+.Op Cm +o Ar option_name
+.Ek
+.Bk -words
+.Ar command_string
+.Op Ar command_name Oo Ar argument ... Oc
+.Ek
+.Nm
+.Fl s
+.Bk -words
+.Op Fl aCefnuvxIimqVEb
+.Op Cm +aCefnuvxIimqVEb
+.Ek
+.Bk -words
+.Op Fl o Ar option_name
+.Op Cm +o Ar option_name
+.Ek
+.Bk -words
+.Op Ar argument ...
+.Ek
+.Sh DESCRIPTION
+.Nm
+is the standard command interpreter for the system.
+The current version of
+.Nm
+is in the process of being changed to conform with the
+.Tn POSIX
+1003.2 and 1003.2a specifications for the shell.
+This version has many
+features which make it appear similar in some respects to the Korn shell,
+but it is not a Korn shell clone (see
+.Xr ksh 1 ) .
+Only features designated by
+.Tn POSIX ,
+plus a few Berkeley extensions, are being incorporated into this shell.
+.\" We expect
+.\" .Tn POSIX
+.\" conformance by the time 4.4 BSD is released.
+This man page is not intended
+to be a tutorial or a complete specification of the shell.
+.Ss Overview
+The shell is a command that reads lines from either a file or the
+terminal, interprets them, and generally executes other commands.
+It is the program that is running when a user logs into the system
+(although a user can select a different shell with the
+.Xr chsh 1
+command).
+The shell implements a language that has flow control
+constructs, a macro facility that provides a variety of features in
+addition to data storage, along with built in history and line editing
+capabilities.
+It incorporates many features to aid interactive use and
+has the advantage that the interpretative language is common to both
+interactive and non-interactive use (shell scripts).
+That is, commands
+can be typed directly to the running shell or can be put into a file and
+the file can be executed directly by the shell.
+.Ss Invocation
+If no args are present and if the standard input of the shell
+is connected to a terminal (or if the
+.Fl i
+flag is set),
+and the
+.Fl c
+option is not present, the shell is considered an interactive shell.
+An interactive shell generally prompts before each command and handles
+programming and command errors differently (as described below).
+When first starting,
+the shell inspects argument 0, and if it begins with a dash
+.Sq - ,
+the shell is also considered
+a login shell.
+This is normally done automatically by the system
+when the user first logs in.
+A login shell first reads commands
+from the files
+.Pa /etc/profile
+and
+.Pa .profile
+if they exist.
+If the environment variable
+.Ev ENV
+is set on entry to a shell, or is set in the
+.Pa .profile
+of a login shell, the shell next reads
+commands from the file named in
+.Ev ENV .
+Therefore, a user should place commands that are to be executed only at
+login time in the
+.Pa .profile
+file, and commands that are executed for every shell inside the
+.Ev ENV
+file.
+To set the
+.Ev ENV
+variable to some file, place the following line in your
+.Pa .profile
+of your home directory
+.Pp
+.Dl ENV=$HOME/.shinit; export ENV
+.Pp
+substituting for
+.Dq .shinit
+any filename you wish.
+Since the
+.Ev ENV
+file is read for every invocation of the shell, including shell scripts
+and non-interactive shells, the following paradigm is useful for
+restricting commands in the
+.Ev ENV
+file to interactive invocations.
+Place commands within the
+.Dq case
+and
+.Dq esac
+below (these commands are described later):
+.Pp
+.Bl -item -compact -offset indent
+.It
+.Li case $- in *i*)
+.Bl -item -compact -offset indent
+.It
+.Li # commands for interactive use only
+.It
+.Li ...
+.El
+.It
+.Li esac
+.El
+.Pp
+If command line arguments besides the options have been specified, then
+the shell treats the first argument as the name of a file from which to
+read commands (a shell script), and the remaining arguments are set as the
+positional parameters of the shell ($1, $2, etc).
+Otherwise, the shell
+reads commands from its standard input.
+.Ss Argument List Processing
+All of the single letter options have a corresponding name that can be
+used as an argument to the
+.Fl o
+option.
+The set
+.Fl o
+name is provided next to the single letter option in
+the description below.
+Specifying a dash
+.Dq -
+turns the option on, while using a plus
+.Dq +
+disables the option.
+The following options can be set from the command line or
+with the
+.Ic set
+builtin (described later).
+.Bl -tag -width aaaallexportfoo -offset indent
+.It Fl a Em allexport
+Export all variables assigned to.
+.It Fl c
+Read commands from the
+.Ar command_string
+operand instead of from the standard input.
+Special parameter 0 will be set from the
+.Ar command_name
+operand and the positional parameters ($1, $2, etc.)
+set from the remaining argument operands.
+.It Fl C Em noclobber
+Don't overwrite existing files with
+.Dq \*[Gt] .
+.It Fl e Em errexit
+If not interactive, exit immediately if any untested command fails.
+The exit status of a command is considered to be
+explicitly tested if the command is used to control an
+.Ic if ,
+.Ic elif ,
+.Ic while ,
+or
+.Ic until ;
+or if the command is the left hand operand of an
+.Dq \*[Am]\*[Am]
+or
+.Dq ||
+operator.
+.It Fl f Em noglob
+Disable pathname expansion.
+.It Fl n Em noexec
+If not interactive, read commands but do not execute them.
+This is useful for checking the syntax of shell scripts.
+.It Fl u Em nounset
+Write a message to standard error when attempting to expand a variable
+that is not set, and if the shell is not interactive, exit immediately.
+.It Fl v Em verbose
+The shell writes its input to standard error as it is read.
+Useful for debugging.
+.It Fl x Em xtrace
+Write each command to standard error (preceded by a
+.Sq +\  )
+before it is executed.
+Useful for debugging.
+.It Fl q Em quietprofile
+If the
+.Fl v
+or
+.Fl x
+options have been set, do not apply them when reading
+initialization files, these being
+.Pa /etc/profile ,
+.Pa .profile ,
+and the file specified by the
+.Ev ENV
+environment variable.
+.It Fl I Em ignoreeof
+Ignore EOF's from input when interactive.
+.It Fl i Em interactive
+Force the shell to behave interactively.
+.It Fl m Em monitor
+Turn on job control (set automatically when interactive).
+.It Fl s Em stdin
+Read commands from standard input (set automatically if no file arguments
+are present).
+This option has no effect when set after the shell has
+already started running (i.e. with
+.Ic set ) .
+.It Fl V Em vi
+Enable the built-in
+.Xr vi 1
+command line editor (disables
+.Fl E
+if it has been set).
+(See the
+.Sx Command Line Editing
+section below.)
+.It Fl E Em emacs
+Enable the built-in emacs style
+command line editor (disables
+.Fl V
+if it has been set).
+(See the
+.Sx Command Line Editing
+section below.)
+.It Fl b Em notify
+Enable asynchronous notification of background job completion.
+(UNIMPLEMENTED for 4.4alpha)
+.It "\ \ " Em cdprint
+Make an interactive shell always print the new directory name when
+changed by the
+.Ic cd
+command.
+.El
+.Ss Lexical Structure
+The shell reads input in terms of lines from a file and breaks it up into
+words at whitespace (blanks and tabs), and at certain sequences of
+characters that are special to the shell called
+.Dq operators .
+There are two types of operators: control operators and redirection
+operators (their meaning is discussed later).
+Following is a list of operators:
+.Bl -ohang -offset indent
+.It "Control operators:"
+.Dl \*[Am]  \*[Am]\*[Am]  \&(  \&)  \&;  ;; | || \*[Lt]newline\*[Gt]
+.It "Redirection operators:"
+.Dl \*[Lt]  \*[Gt]  \*[Gt]|  \*[Lt]\*[Lt]  \*[Gt]\*[Gt]  \*[Lt]\*[Am]  \*[Gt]\*[Am]  \*[Lt]\*[Lt]-  \*[Lt]\*[Gt]
+.El
+.Ss Quoting
+Quoting is used to remove the special meaning of certain characters or
+words to the shell, such as operators, whitespace, or keywords.
+There are three types of quoting: matched single quotes,
+matched double quotes, and backslash.
+.Ss Backslash
+A backslash preserves the literal meaning of the following
+character, with the exception of
+.Aq newline .
+A backslash preceding a
+.Aq newline
+is treated as a line continuation.
+.Ss Single Quotes
+Enclosing characters in single quotes preserves the literal meaning of all
+the characters (except single quotes, making it impossible to put
+single-quotes in a single-quoted string).
+.Ss Double Quotes
+Enclosing characters within double quotes preserves the literal
+meaning of all characters except dollarsign
+.Pq $ ,
+backquote
+.Pq ` ,
+and backslash
+.Pq \e .
+The backslash inside double quotes is historically weird, and serves to
+quote only the following characters:
+.Dl $  `  \*q  \e  \*[Lt]newline\*[Gt] .
+Otherwise it remains literal.
+.Ss Reserved Words
+Reserved words are words that have special meaning to the
+shell and are recognized at the beginning of a line and
+after a control operator.
+The following are reserved words:
+.Bl -column while while while while while -offset indent
+.It ! Ta elif Ta fi Ta while Ta case
+.It else Ta for Ta then Ta { Ta }
+.It do Ta done Ta until Ta if Ta esac
+.El
+.Pp
+Their meaning is discussed later.
+.Ss Aliases
+An alias is a name and corresponding value set using the
+.Ic alias
+builtin command.
+Whenever a reserved word may occur (see above),
+and after checking for reserved words, the shell
+checks the word to see if it matches an alias.
+If it does, it replaces it in the input stream with its value.
+For example, if there is an alias called
+.Dq lf
+with the value
+.Dq "ls -F" ,
+then the input:
+.Pp
+.Dl lf foobar Aq return
+.Pp
+would become
+.Pp
+.Dl ls -F foobar Aq return
+.Pp
+Aliases provide a convenient way for naive users to create shorthands for
+commands without having to learn how to create functions with arguments.
+They can also be used to create lexically obscure code.
+This use is discouraged.
+.Ss Commands
+The shell interprets the words it reads according to a language, the
+specification of which is outside the scope of this man page (refer to the
+BNF in the
+.Tn POSIX
+1003.2 document).
+Essentially though, a line is read and if the first
+word of the line (or after a control operator) is not a reserved word,
+then the shell has recognized a simple command.
+Otherwise, a complex
+command or some other special construct may have been recognized.
+.Ss Simple Commands
+If a simple command has been recognized, the shell performs
+the following actions:
+.Bl -enum -offset indent
+.It
+Leading words of the form
+.Dq name=value
+are stripped off and assigned to the environment of the simple command.
+Redirection operators and their arguments (as described below) are
+stripped off and saved for processing.
+.It
+The remaining words are expanded as described in
+the section called
+.Dq Expansions ,
+and the first remaining word is considered the command name and the
+command is located.
+The remaining words are considered the arguments of the command.
+If no command name resulted, then the
+.Dq name=value
+variable assignments recognized in item 1 affect the current shell.
+.It
+Redirections are performed as described in the next section.
+.El
+.Ss Redirections
+Redirections are used to change where a command reads its input or sends
+its output.
+In general, redirections open, close, or duplicate an
+existing reference to a file.
+The overall format used for redirection is:
+.Pp
+.Dl [n] Va redir-op Ar file
+.Pp
+where
+.Va redir-op
+is one of the redirection operators mentioned previously.
+Following is a list of the possible redirections.
+The
+.Bq n
+is an optional number, as in
+.Sq 3
+(not
+.Sq Bq 3 ) ,
+that refers to a file descriptor.
+.Bl -tag -width aaabsfiles -offset indent
+.It [n] Ns \*[Gt] file
+Redirect standard output (or n) to file.
+.It [n] Ns \*[Gt]| file
+Same, but override the
+.Fl C
+option.
+.It [n] Ns \*[Gt]\*[Gt] file
+Append standard output (or n) to file.
+.It [n] Ns \*[Lt] file
+Redirect standard input (or n) from file.
+.It [n1] Ns \*[Lt]\*[Am] Ns n2
+Duplicate standard input (or n1) from file descriptor n2.
+.It [n] Ns \*[Lt]\*[Am]-
+Close standard input (or n).
+.It [n1] Ns \*[Gt]\*[Am] Ns n2
+Duplicate standard output (or n1) to n2.
+.It [n] Ns \*[Gt]\*[Am]-
+Close standard output (or n).
+.It [n] Ns \*[Lt]\*[Gt] file
+Open file for reading and writing on standard input (or n).
+.El
+.Pp
+The following redirection is often called a
+.Dq here-document .
+.Bl -item -offset indent
+.It
+.Li [n]\*[Lt]\*[Lt] delimiter
+.Dl here-doc-text ...
+.Li delimiter
+.El
+.Pp
+All the text on successive lines up to the delimiter is saved away and
+made available to the command on standard input, or file descriptor n if
+it is specified.
+If the delimiter as specified on the initial line is
+quoted, then the here-doc-text is treated literally, otherwise the text is
+subjected to parameter expansion, command substitution, and arithmetic
+expansion (as described in the section on
+.Dq Expansions ) .
+If the operator is
+.Dq \*[Lt]\*[Lt]-
+instead of
+.Dq \*[Lt]\*[Lt] ,
+then leading tabs in the here-doc-text are stripped.
+.Ss Search and Execution
+There are three types of commands: shell functions, builtin commands, and
+normal programs -- and the command is searched for (by name) in that order.
+They each are executed in a different way.
+.Pp
+When a shell function is executed, all of the shell positional parameters
+(except $0, which remains unchanged) are set to the arguments of the shell
+function.
+The variables which are explicitly placed in the environment of
+the command (by placing assignments to them before the function name) are
+made local to the function and are set to the values given.
+Then the command given in the function definition is executed.
+The positional parameters are restored to their original values
+when the command completes.
+This all occurs within the current shell.
+.Pp
+Shell builtins are executed internally to the shell, without spawning a
+new process.
+.Pp
+Otherwise, if the command name doesn't match a function or builtin, the
+command is searched for as a normal program in the file system (as
+described in the next section).
+When a normal program is executed, the shell runs the program,
+passing the arguments and the environment to the program.
+If the program is not a normal executable file (i.e., if it does
+not begin with the "magic number" whose
+.Tn ASCII
+representation is "#!", so
+.Xr execve 2
+returns
+.Er ENOEXEC
+then) the shell will interpret the program in a subshell.
+The child shell will reinitialize itself in this case,
+so that the effect will be as if a
+new shell had been invoked to handle the ad-hoc shell script, except that
+the location of hashed commands located in the parent shell will be
+remembered by the child.
+.Pp
+Note that previous versions of this document and the source code itself
+misleadingly and sporadically refer to a shell script without a magic
+number as a "shell procedure".
+.Ss Path Search
+When locating a command, the shell first looks to see if it has a shell
+function by that name.
+Then it looks for a builtin command by that name.
+If a builtin command is not found, one of two things happen:
+.Bl -enum
+.It
+Command names containing a slash are simply executed without performing
+any searches.
+.It
+The shell searches each entry in
+.Ev PATH
+in turn for the command.
+The value of the
+.Ev PATH
+variable should be a series of entries separated by colons.
+Each entry consists of a directory name.
+The current directory may be indicated
+implicitly by an empty directory name, or explicitly by a single period.
+.El
+.Ss Command Exit Status
+Each command has an exit status that can influence the behavior
+of other shell commands.
+The paradigm is that a command exits
+with zero for normal or success, and non-zero for failure,
+error, or a false indication.
+The man page for each command
+should indicate the various exit codes and what they mean.
+Additionally, the builtin commands return exit codes, as does
+an executed shell function.
+.Pp
+If a command consists entirely of variable assignments then the
+exit status of the command is that of the last command substitution
+if any, otherwise 0.
+.Ss Complex Commands
+Complex commands are combinations of simple commands with control
+operators or reserved words, together creating a larger complex command.
+More generally, a command is one of the following:
+.Bl -bullet
+.It
+simple command
+.It
+pipeline
+.It
+list or compound-list
+.It
+compound command
+.It
+function definition
+.El
+.Pp
+Unless otherwise stated, the exit status of a command is that of the last
+simple command executed by the command.
+.Ss Pipelines
+A pipeline is a sequence of one or more commands separated
+by the control operator |.
+The standard output of all but
+the last command is connected to the standard input
+of the next command.
+The standard output of the last
+command is inherited from the shell, as usual.
+.Pp
+The format for a pipeline is:
+.Pp
+.Dl [!] command1 [ | command2 ...]
+.Pp
+The standard output of command1 is connected to the standard input of
+command2.
+The standard input, standard output, or both of a command is
+considered to be assigned by the pipeline before any redirection specified
+by redirection operators that are part of the command.
+.Pp
+If the pipeline is not in the background (discussed later), the shell
+waits for all commands to complete.
+.Pp
+If the reserved word ! does not precede the pipeline, the exit status is
+the exit status of the last command specified in the pipeline.
+Otherwise, the exit status is the logical NOT of the exit status of the
+last command.
+That is, if the last command returns zero, the exit status
+is 1; if the last command returns greater than zero, the exit status is
+zero.
+.Pp
+Because pipeline assignment of standard input or standard output or both
+takes place before redirection, it can be modified by redirection.
+For example:
+.Pp
+.Dl $ command1 2\*[Gt]\*[Am]1 | command2
+.Pp
+sends both the standard output and standard error of command1
+to the standard input of command2.
+.Pp
+A ; or
+.Aq newline
+terminator causes the preceding AND-OR-list (described
+next) to be executed sequentially; a \*[Am] causes asynchronous execution of
+the preceding AND-OR-list.
+.Pp
+Note that unlike some other shells, each process in the pipeline is a
+child of the invoking shell (unless it is a shell builtin, in which case
+it executes in the current shell -- but any effect it has on the
+environment is wiped).
+.Ss Background Commands -- \*[Am]
+If a command is terminated by the control operator ampersand (\*[Am]), the
+shell executes the command asynchronously -- that is, the shell does not
+wait for the command to finish before executing the next command.
+.Pp
+The format for running a command in background is:
+.Pp
+.Dl command1 \*[Am] [command2 \*[Am] ...]
+.Pp
+If the shell is not interactive, the standard input of an asynchronous
+command is set to
+.Pa /dev/null .
+.Ss Lists -- Generally Speaking
+A list is a sequence of zero or more commands separated by newlines,
+semicolons, or ampersands, and optionally terminated by one of these three
+characters.
+The commands in a list are executed in the order they are written.
+If command is followed by an ampersand, the shell starts the
+command and immediately proceed onto the next command; otherwise it waits
+for the command to terminate before proceeding to the next one.
+.Ss Short-Circuit List Operators
+.Dq \*[Am]\*[Am]
+and
+.Dq ||
+are AND-OR list operators.
+.Dq \*[Am]\*[Am]
+executes the first command, and then executes the second command if and only
+if the exit status of the first command is zero.
+.Dq ||
+is similar, but executes the second command if and only if the exit status
+of the first command is nonzero.
+.Dq \*[Am]\*[Am]
+and
+.Dq ||
+both have the same priority.
+Note that these operators are left-associative, so
+.Dq true || echo bar && echo baz
+writes
+.Dq baz
+and nothing else.
+This is not the way it works in C.
+.Ss Flow-Control Constructs -- if, while, for, case
+The syntax of the if command is
+.Bd -literal -offset indent
+if list
+then list
+[ elif list
+then    list ] ...
+[ else list ]
+fi
+.Ed
+.Pp
+The syntax of the while command is
+.Bd -literal -offset indent
+while list
+do   list
+done
+.Ed
+.Pp
+The two lists are executed repeatedly while the exit status of the
+first list is zero.
+The until command is similar, but has the word
+until in place of while, which causes it to
+repeat until the exit status of the first list is zero.
+.Pp
+The syntax of the for command is
+.Bd -literal -offset indent
+for variable in word ...
+do   list
+done
+.Ed
+.Pp
+The words are expanded, and then the list is executed repeatedly with the
+variable set to each word in turn.
+do and done may be replaced with
+.Dq {
+and
+.Dq } .
+.Pp
+The syntax of the break and continue command is
+.Bd -literal -offset indent
+break [ num ]
+continue [ num ]
+.Ed
+.Pp
+Break terminates the num innermost for or while loops.
+Continue continues with the next iteration of the innermost loop.
+These are implemented as builtin commands.
+.Pp
+The syntax of the case command is
+.Bd -literal -offset indent
+case word in
+pattern) list ;;
+\&...
+esac
+.Ed
+.Pp
+The pattern can actually be one or more patterns (see
+.Sx Shell Patterns
+described later), separated by
+.Dq \*(Ba
+characters.
+.Ss Grouping Commands Together
+Commands may be grouped by writing either
+.Pp
+.Dl (list)
+.Pp
+or
+.Pp
+.Dl { list; }
+.Pp
+The first of these executes the commands in a subshell.
+Builtin commands grouped into a (list) will not affect the current shell.
+The second form does not fork another shell so is slightly more efficient.
+Grouping commands together this way allows you to redirect
+their output as though they were one program:
+.Pp
+.Bd -literal -offset indent
+{ echo -n \*q hello \*q ; echo \*q world" ; } \*[Gt] greeting
+.Ed
+.Pp
+Note that
+.Dq }
+must follow a control operator (here,
+.Dq \&; )
+so that it is recognized as a reserved word and not as another command argument.
+.Ss Functions
+The syntax of a function definition is
+.Pp
+.Dl name ( ) command
+.Pp
+A function definition is an executable statement; when executed it
+installs a function named name and returns an exit status of zero.
+The command is normally a list enclosed between
+.Dq {
+and
+.Dq } .
+.Pp
+Variables may be declared to be local to a function by using a local
+command.
+This should appear as the first statement of a function, and the syntax is
+.Pp
+.Dl local [ variable | - ] ...
+.Pp
+Local is implemented as a builtin command.
+.Pp
+When a variable is made local, it inherits the initial value and exported
+and readonly flags from the variable with the same name in the surrounding
+scope, if there is one.
+Otherwise, the variable is initially unset.
+The shell uses dynamic scoping, so that if you make the variable x local to
+function f, which then calls function g, references to the variable x made
+inside g will refer to the variable x declared inside f, not to the global
+variable named x.
+.Pp
+The only special parameter that can be made local is
+.Dq - .
+Making
+.Dq -
+local any shell options that are changed via the set command inside the
+function to be restored to their original values when the function
+returns.
+.Pp
+The syntax of the return command is
+.Pp
+.Dl return [ exitstatus ]
+.Pp
+It terminates the currently executing function.
+Return is implemented as a builtin command.
+.Ss Variables and Parameters
+The shell maintains a set of parameters.
+A parameter denoted by a name is called a variable.
+When starting up, the shell turns all the environment
+variables into shell variables.
+New variables can be set using the form
+.Pp
+.Dl name=value
+.Pp
+Variables set by the user must have a name consisting solely of
+alphabetics, numerics, and underscores - the first of which must not be
+numeric.
+A parameter can also be denoted by a number or a special
+character as explained below.
+.Ss Positional Parameters
+A positional parameter is a parameter denoted by a number (n \*[Gt] 0).
+The shell sets these initially to the values of its command line arguments
+that follow the name of the shell script.
+The
+.Ic set
+builtin can also be used to set or reset them.
+.Ss Special Parameters
+A special parameter is a parameter denoted by one of the following special
+characters.
+The value of the parameter is listed next to its character.
+.Bl -tag -width thinhyphena
+.It *
+Expands to the positional parameters, starting from one.
+When the
+expansion occurs within a double-quoted string it expands to a single
+field with the value of each parameter separated by the first character of
+the
+.Ev IFS
+variable, or by a
+.Aq space
+if
+.Ev IFS
+is unset.
+.It @
+Expands to the positional parameters, starting from one.
+When the expansion occurs within double-quotes, each positional
+parameter expands as a separate argument.
+If there are no positional parameters, the
+expansion of @ generates zero arguments, even when @ is
+double-quoted.
+What this basically means, for example, is
+if $1 is
+.Dq abc
+and $2 is
+.Dq def ghi ,
+then
+.Qq $@
+expands to
+the two arguments:
+.Pp
+.Sm off
+.Dl \*q abc \*q \  \*q def\ ghi \*q
+.Sm on
+.It #
+Expands to the number of positional parameters.
+.It \&?
+Expands to the exit status of the most recent pipeline.
+.It - (Hyphen.)
+Expands to the current option flags (the single-letter
+option names concatenated into a string) as specified on
+invocation, by the set builtin command, or implicitly
+by the shell.
+.It $
+Expands to the process ID of the invoked shell.
+A subshell retains the same value of $ as its parent.
+.It \&!
+Expands to the process ID of the most recent background
+command executed from the current shell.
+For a pipeline, the process ID is that of the last command in the pipeline.
+.It 0 (Zero.)
+Expands to the name of the shell or shell script.
+.El
+.Ss Word Expansions
+This clause describes the various expansions that are performed on words.
+Not all expansions are performed on every word, as explained later.
+.Pp
+Tilde expansions, parameter expansions, command substitutions, arithmetic
+expansions, and quote removals that occur within a single word expand to a
+single field.
+It is only field splitting or pathname expansion that can
+create multiple fields from a single word.
+The single exception to this
+rule is the expansion of the special parameter @ within double-quotes, as
+was described above.
+.Pp
+The order of word expansion is:
+.Bl -enum
+.It
+Tilde Expansion, Parameter Expansion, Command Substitution,
+Arithmetic Expansion (these all occur at the same time).
+.It
+Field Splitting is performed on fields
+generated by step (1) unless the
+.Ev IFS
+variable is null.
+.It
+Pathname Expansion (unless set
+.Fl f
+is in effect).
+.It
+Quote Removal.
+.El
+.Pp
+The $ character is used to introduce parameter expansion, command
+substitution, or arithmetic evaluation.
+.Ss Tilde Expansion (substituting a user's home directory)
+A word beginning with an unquoted tilde character (~) is
+subjected to tilde expansion.
+All the characters up to
+a slash (/) or the end of the word are treated as a username
+and are replaced with the user's home directory.
+If the username is missing (as in
+.Pa ~/foobar ) ,
+the tilde is replaced with the value of the
+.Va HOME
+variable (the current user's home directory).
+.Ss Parameter Expansion
+The format for parameter expansion is as follows:
+.Pp
+.Dl ${expression}
+.Pp
+where expression consists of all characters until the matching
+.Dq } .
+Any
+.Dq }
+escaped by a backslash or within a quoted string, and characters in
+embedded arithmetic expansions, command substitutions, and variable
+expansions, are not examined in determining the matching
+.Dq } .
+.Pp
+The simplest form for parameter expansion is:
+.Pp
+.Dl ${parameter}
+.Pp
+The value, if any, of parameter is substituted.
+.Pp
+The parameter name or symbol can be enclosed in braces, which are
+optional except for positional parameters with more than one digit or
+when parameter is followed by a character that could be interpreted as
+part of the name.
+If a parameter expansion occurs inside double-quotes:
+.Bl -enum
+.It
+Pathname expansion is not performed on the results of the expansion.
+.It
+Field splitting is not performed on the results of the
+expansion, with the exception of @.
+.El
+.Pp
+In addition, a parameter expansion can be modified by using one of the
+following formats.
+.Bl -tag -width aaparameterwordaaaaa
+.It ${parameter:-word}
+Use Default Values.
+If parameter is unset or null, the expansion of word
+is substituted; otherwise, the value of parameter is substituted.
+.It ${parameter:=word}
+Assign Default Values.
+If parameter is unset or null, the expansion of
+word is assigned to parameter.
+In all cases, the final value of parameter is substituted.
+Only variables, not positional parameters or special
+parameters, can be assigned in this way.
+.It ${parameter:?[word]}
+Indicate Error if Null or Unset.
+If parameter is unset or null, the
+expansion of word (or a message indicating it is unset if word is omitted)
+is written to standard error and the shell exits with a nonzero exit status.
+Otherwise, the value of parameter is substituted.
+An interactive shell need not exit.
+.It ${parameter:+word}
+Use Alternative Value.
+If parameter is unset or null, null is
+substituted; otherwise, the expansion of word is substituted.
+.El
+.Pp
+In the parameter expansions shown previously, use of the colon in the
+format results in a test for a parameter that is unset or null; omission
+of the colon results in a test for a parameter that is only unset.
+.Bl -tag -width aaparameterwordaaaaa
+.It ${#parameter}
+String Length.
+The length in characters of the value of parameter.
+.El
+.Pp
+The following four varieties of parameter expansion provide for substring
+processing.
+In each case, pattern matching notation (see
+.Sx Shell Patterns ) ,
+rather than regular expression notation, is used to evaluate the patterns.
+If parameter is * or @, the result of the expansion is unspecified.
+Enclosing the full parameter expansion string in double-quotes does not
+cause the following four varieties of pattern characters to be quoted,
+whereas quoting characters within the braces has this effect.
+.Bl -tag -width aaparameterwordaaaaa
+.It ${parameter%word}
+Remove Smallest Suffix Pattern.
+The word is expanded to produce a pattern.
+The parameter expansion then results in parameter, with the
+smallest portion of the suffix matched by the pattern deleted.
+.It ${parameter%%word}
+Remove Largest Suffix Pattern.
+The word is expanded to produce a pattern.
+The parameter expansion then results in parameter, with the largest
+portion of the suffix matched by the pattern deleted.
+.It ${parameter#word}
+Remove Smallest Prefix Pattern.
+The word is expanded to produce a pattern.
+The parameter expansion then results in parameter, with the
+smallest portion of the prefix matched by the pattern deleted.
+.It ${parameter##word}
+Remove Largest Prefix Pattern.
+The word is expanded to produce a pattern.
+The parameter expansion then results in parameter, with the largest
+portion of the prefix matched by the pattern deleted.
+.El
+.Ss Command Substitution
+Command substitution allows the output of a command to be substituted in
+place of the command name itself.
+Command substitution occurs when the command is enclosed as follows:
+.Pp
+.Dl $(command)
+.Pp
+or
+.Po
+.Dq backquoted
+version
+.Pc :
+.Pp
+.Dl `command`
+.Pp
+The shell expands the command substitution by executing command in a
+subshell environment and replacing the command substitution with the
+standard output of the command, removing sequences of one or more
+.Ao newline Ac Ns s
+at the end of the substitution.
+(Embedded
+.Ao newline Ac Ns s
+before
+the end of the output are not removed; however, during field splitting,
+they may be translated into
+.Ao space Ac Ns s ,
+depending on the value of
+.Ev IFS
+and quoting that is in effect.)
+.Ss Arithmetic Expansion
+Arithmetic expansion provides a mechanism for evaluating an arithmetic
+expression and substituting its value.
+The format for arithmetic expansion is as follows:
+.Pp
+.Dl $((expression))
+.Pp
+The expression is treated as if it were in double-quotes, except
+that a double-quote inside the expression is not treated specially.
+The shell expands all tokens in the expression for parameter expansion,
+command substitution, and quote removal.
+.Pp
+Next, the shell treats this as an arithmetic expression and
+substitutes the value of the expression.
+.Ss White Space Splitting (Field Splitting)
+After parameter expansion, command substitution, and
+arithmetic expansion the shell scans the results of
+expansions and substitutions that did not occur in double-quotes for
+field splitting and multiple fields can result.
+.Pp
+The shell treats each character of the
+.Ev IFS
+as a delimiter and use the delimiters to split the results of parameter
+expansion and command substitution into fields.
+.Ss Pathname Expansion (File Name Generation)
+Unless the
+.Fl f
+flag is set, file name generation is performed after word splitting is
+complete.
+Each word is viewed as a series of patterns, separated by slashes.
+The process of expansion replaces the word with the names of all
+existing files whose names can be formed by replacing each pattern with a
+string that matches the specified pattern.
+There are two restrictions on
+this: first, a pattern cannot match a string containing a slash, and
+second, a pattern cannot match a string starting with a period unless the
+first character of the pattern is a period.
+The next section describes the
+patterns used for both Pathname Expansion and the
+.Ic case
+command.
+.Ss Shell Patterns
+A pattern consists of normal characters, which match themselves,
+and meta-characters.
+The meta-characters are
+.Dq \&! ,
+.Dq * ,
+.Dq \&? ,
+and
+.Dq \&[ .
+These characters lose their special meanings if they are quoted.
+When command or variable substitution is performed
+and the dollar sign or back quotes are not double quoted,
+the value of the variable or the output of
+the command is scanned for these characters and they are turned into
+meta-characters.
+.Pp
+An asterisk
+.Pq Dq *
+matches any string of characters.
+A question mark matches any single character.
+A left bracket
+.Pq Dq \&[
+introduces a character class.
+The end of the character class is indicated by a
+.Pq Dq \&] ;
+if the
+.Dq \&]
+is missing then the
+.Dq \&[
+matches a
+.Dq \&[
+rather than introducing a character class.
+A character class matches any of the characters between the square brackets.
+A range of characters may be specified using a minus sign.
+The character class may be complemented
+by making an exclamation point the first character of the character class.
+.Pp
+To include a
+.Dq \&]
+in a character class, make it the first character listed (after the
+.Dq \&! ,
+if any).
+To include a minus sign, make it the first or last character listed.
+.Ss Builtins
+This section lists the builtin commands which are builtin because they
+need to perform some operation that can't be performed by a separate
+process.
+In addition to these, there are several other commands that may
+be builtin for efficiency (e.g.
+.Xr printf 1 ,
+.Xr echo 1 ,
+.Xr test 1 ,
+etc).
+.Bl -tag -width 5n
+.It :
+A null command that returns a 0 (true) exit value.
+.It \&. file
+The commands in the specified file are read and executed by the shell.
+.It alias Op Ar name Ns Op Ar "=string ..."
+If
+.Ar name=string
+is specified, the shell defines the alias
+.Ar name
+with value
+.Ar string .
+If just
+.Ar name
+is specified, the value of the alias
+.Ar name
+is printed.
+With no arguments, the
+.Ic alias
+builtin prints the
+names and values of all defined aliases (see
+.Ic unalias ) .
+.It bg [ Ar job ] ...
+Continue the specified jobs (or the current job if no
+jobs are given) in the background.
+.It Xo command
+.Op Fl p
+.Op Fl v
+.Op Fl V
+.Ar command
+.Op Ar arg ...
+.Xc
+Execute the specified command but ignore shell functions when searching
+for it.
+(This is useful when you
+have a shell function with the same name as a builtin command.)
+.Bl -tag -width 5n
+.It Fl p
+search for command using a
+.Ev PATH
+that guarantees to find all the standard utilities.
+.It Fl V
+Do not execute the command but
+search for the command and print the resolution of the
+command search.
+This is the same as the type builtin.
+.It Fl v
+Do not execute the command but
+search for the command and print the absolute pathname
+of utilities, the name for builtins or the expansion of aliases.
+.El
+.It cd Op Ar directory Op Ar replace
+Switch to the specified directory (default
+.Ev $HOME ) .
+If
+.Ar replace
+is specified, then the new directory name is generated by replacing
+the first occurrence of
+.Ar directory
+in the current directory name with
+.Ar replace .
+Otherwise if an entry for
+.Ev CDPATH
+appears in the environment of the
+.Ic cd
+command or the shell variable
+.Ev CDPATH
+is set and the directory name does not begin with a slash, then the
+directories listed in
+.Ev CDPATH
+will be searched for the specified directory.
+The format of
+.Ev CDPATH
+is the same as that of
+.Ev PATH .
+In an interactive shell, the
+.Ic cd
+command will print out the name of the
+directory that it actually switched to if this is different from the name
+that the user gave.
+These may be different either because the
+.Ev CDPATH
+mechanism was used or because a symbolic link was crossed.
+.It eval Ar string ...
+Concatenate all the arguments with spaces.
+Then re-parse and execute the command.
+.It exec Op Ar command arg ...
+Unless command is omitted, the shell process is replaced with the
+specified program (which must be a real program, not a shell builtin or
+function).
+Any redirections on the
+.Ic exec
+command are marked as permanent, so that they are not undone when the
+.Ic exec
+command finishes.
+.It exit Op Ar exitstatus
+Terminate the shell process.
+If
+.Ar exitstatus
+is given it is used as the exit status of the shell; otherwise the
+exit status of the preceding command is used.
+.It export Ar name ...
+.It export Fl p
+The specified names are exported so that they will appear in the
+environment of subsequent commands.
+The only way to un-export a variable is to unset it.
+The shell allows the value of a variable to be set at the
+same time it is exported by writing
+.Pp
+.Dl export name=value
+.Pp
+With no arguments the export command lists the names of all exported variables.
+With the
+.Fl p
+option specified the output will be formatted suitably for non-interactive use.
+.It Xo fc Op Fl e Ar editor
+.Op Ar first Op Ar last
+.Xc
+.It Xo fc Fl l
+.Op Fl nr
+.Op Ar first Op Ar last
+.Xc
+.It Xo fc Fl s Op Ar old=new
+.Op Ar first
+.Xc
+The
+.Ic fc
+builtin lists, or edits and re-executes, commands previously entered
+to an interactive shell.
+.Bl -tag -width 5n
+.It Fl e No editor
+Use the editor named by editor to edit the commands.
+The editor string is a command name, subject to search via the
+.Ev PATH
+variable.
+The value in the
+.Ev FCEDIT
+variable is used as a default when
+.Fl e
+is not specified.
+If
+.Ev FCEDIT
+is null or unset, the value of the
+.Ev EDITOR
+variable is used.
+If
+.Ev EDITOR
+is null or unset,
+.Xr ed 1
+is used as the editor.
+.It Fl l No (ell)
+List the commands rather than invoking an editor on them.
+The commands are written in the sequence indicated by
+the first and last operands, as affected by
+.Fl r ,
+with each command preceded by the command number.
+.It Fl n
+Suppress command numbers when listing with -l.
+.It Fl r
+Reverse the order of the commands listed (with
+.Fl l )
+or edited (with neither
+.Fl l
+nor
+.Fl s ) .
+.It Fl s
+Re-execute the command without invoking an editor.
+.It first
+.It last
+Select the commands to list or edit.
+The number of previous commands that
+can be accessed are determined by the value of the
+.Ev HISTSIZE
+variable.
+The value of first or last or both are one of the following:
+.Bl -tag -width 5n
+.It [+]number
+A positive number representing a command number; command numbers can be
+displayed with the
+.Fl l
+option.
+.It Fl number
+A negative decimal number representing the command that was executed
+number of commands previously.
+For example, \-1 is the immediately previous command.
+.El
+.It string
+A string indicating the most recently entered command that begins with
+that string.
+If the old=new operand is not also specified with
+.Fl s ,
+the string form of the first operand cannot contain an embedded equal sign.
+.El
+.Pp
+The following environment variables affect the execution of fc:
+.Bl -tag -width HISTSIZE
+.It Ev FCEDIT
+Name of the editor to use.
+.It Ev HISTSIZE
+The number of previous commands that are accessible.
+.El
+.It fg Op Ar job
+Move the specified job or the current job to the foreground.
+.It getopts Ar optstring var
+The
+.Tn POSIX
+.Ic getopts
+command, not to be confused with the
+.Em Bell Labs
+-derived
+.Xr getopt 1 .
+.Pp
+The first argument should be a series of letters, each of which may be
+optionally followed by a colon to indicate that the option requires an
+argument.
+The variable specified is set to the parsed option.
+.Pp
+The
+.Ic getopts
+command deprecates the older
+.Xr getopt 1
+utility due to its handling of arguments containing whitespace.
+.Pp
+The
+.Ic getopts
+builtin may be used to obtain options and their arguments
+from a list of parameters.
+When invoked,
+.Ic getopts
+places the value of the next option from the option string in the list in
+the shell variable specified by
+.Va var
+and its index in the shell variable
+.Ev OPTIND .
+When the shell is invoked,
+.Ev OPTIND
+is initialized to 1.
+For each option that requires an argument, the
+.Ic getopts
+builtin will place it in the shell variable
+.Ev OPTARG .
+If an option is not allowed for in the
+.Va optstring ,
+then
+.Ev OPTARG
+will be unset.
+.Pp
+.Va optstring
+is a string of recognized option letters (see
+.Xr getopt 3 ) .
+If a letter is followed by a colon, the option is expected to have an
+argument which may or may not be separated from it by white space.
+If an option character is not found where expected,
+.Ic getopts
+will set the variable
+.Va var
+to a
+.Dq \&? ;
+.Ic getopts
+will then unset
+.Ev OPTARG
+and write output to standard error.
+By specifying a colon as the first character of
+.Va optstring
+all errors will be ignored.
+.Pp
+A nonzero value is returned when the last option is reached.
+If there are no remaining arguments,
+.Ic getopts
+will set
+.Va var
+to the special option,
+.Dq -- ,
+otherwise, it will set
+.Va var
+to
+.Dq \&? .
+.Pp
+The following code fragment shows how one might process the arguments
+for a command that can take the options
+.Op a
+and
+.Op b ,
+and the option
+.Op c ,
+which requires an argument.
+.Pp
+.Bd -literal -offset indent
+while getopts abc: f
+do
+	case $f in
+	a | b)	flag=$f;;
+	c)	carg=$OPTARG;;
+	\\?)	echo $USAGE; exit 1;;
+	esac
+done
+shift `expr $OPTIND - 1`
+.Ed
+.Pp
+This code will accept any of the following as equivalent:
+.Pp
+.Bd -literal -offset indent
+cmd \-acarg file file
+cmd \-a \-c arg file file
+cmd \-carg -a file file
+cmd \-a \-carg \-\- file file
+.Ed
+.It hash Fl rv Ar command ...
+The shell maintains a hash table which remembers the
+locations of commands.
+With no arguments whatsoever,
+the
+.Ic hash
+command prints out the contents of this table.
+Entries which have not been looked at since the last
+.Ic cd
+command are marked with an asterisk; it is possible for these entries
+to be invalid.
+.Pp
+With arguments, the
+.Ic hash
+command removes the specified commands from the hash table (unless
+they are functions) and then locates them.
+With the
+.Fl v
+option, hash prints the locations of the commands as it finds them.
+The
+.Fl r
+option causes the hash command to delete all the entries in the hash table
+except for functions.
+.It inputrc Ar file
+Read the
+.Va file
+to set keybindings as defined by
+.Xr editrc 5 .
+.It jobid Op Ar job
+Print the process id's of the processes in the job.
+If the
+.Ar job
+argument is omitted, the current job is used.
+.It jobs
+This command lists out all the background processes
+which are children of the current shell process.
+.It pwd Op Fl LP
+Print the current directory.
+If 
+.Fl L
+is specified the cached value (initially set from
+.Ev PWD )
+is checked to see if it refers to the current directory, if it does
+the value is printed.
+Otherwise the current directory name is found using
+.Xr getcwd(3) .
+The environment variable
+.Ev PWD
+is set to printed value.
+.Pp
+The default is
+.Ic pwd
+.Fl L ,
+but note that the builtin
+.Ic cd
+command doesn't currently support
+.Fl L
+or
+.Fl P
+and will cache (almost) the absolute path.
+If
+.Ic cd
+is changed,
+.Ic pwd
+may be changed to default to
+.Ic pwd
+.Fl P .
+.Pp
+If the current directory is renamed and replaced by a symlink to the
+same directory, or the initial
+.Ev PWD
+value followed a symbolic link, then the cached value may not
+be the absolute path.
+.Pp
+The builtin command may differ from the program of the same name because
+the program will use
+.Ev PWD
+and the builtin uses a separately cached value.
+.It Xo read Op Fl p Ar prompt
+.Op Fl r
+.Ar variable
+.Op Ar ...
+.Xc
+The prompt is printed if the
+.Fl p
+option is specified and the standard input is a terminal.
+Then a line is read from the standard input.
+The trailing newline is deleted from the
+line and the line is split as described in the section on word splitting
+above, and the pieces are assigned to the variables in order.
+If there are more pieces than variables, the remaining pieces
+(along with the characters in
+.Ev IFS
+that separated them) are assigned to the last variable.
+If there are more variables than pieces,
+the remaining variables are assigned the null string.
+The
+.Ic read
+builtin will indicate success unless EOF is encountered on input, in
+which case failure is returned.
+.Pp
+By default, unless the
+.Fl r
+option is specified, the backslash
+.Dq \e
+acts as an escape character, causing the following character to be treated
+literally.
+If a backslash is followed by a newline, the backslash and the
+newline will be deleted.
+.It readonly Ar name ...
+.It readonly Fl p
+The specified names are marked as read only, so that they cannot be
+subsequently modified or unset.
+The shell allows the value of a variable
+to be set at the same time it is marked read only by writing
+.Pp
+.Dl readonly name=value
+.Pp
+With no arguments the readonly command lists the names of all read only
+variables.
+With the
+.Fl p
+option specified the output will be formatted suitably for non-interactive use.
+.Pp
+.It Xo set
+.Oo {
+.Fl options | Cm +options | Cm -- }
+.Oc Ar arg ...
+.Xc
+The
+.Ic set
+command performs three different functions.
+.Pp
+With no arguments, it lists the values of all shell variables.
+.Pp
+If options are given, it sets the specified option
+flags, or clears them as described in the section called
+.Sx Argument List Processing .
+.Pp
+The third use of the set command is to set the values of the shell's
+positional parameters to the specified args.
+To change the positional
+parameters without changing any options, use
+.Dq --
+as the first argument to set.
+If no args are present, the set command
+will clear all the positional parameters (equivalent to executing
+.Dq shift $# . )
+.It setvar Ar variable Ar value
+Assigns value to variable.
+(In general it is better to write
+variable=value rather than using
+.Ic setvar .
+.Ic setvar
+is intended to be used in
+functions that assign values to variables whose names are passed as
+parameters.)
+.It shift Op Ar n
+Shift the positional parameters n times.
+A
+.Ic shift
+sets the value of
+.Va $1
+to the value of
+.Va $2 ,
+the value of
+.Va $2
+to the value of
+.Va $3 ,
+and so on, decreasing
+the value of
+.Va $#
+by one.
+If there are zero positional parameters,
+.Ic shift
+does nothing.
+.It Xo trap
+.Op Fl l
+.Xc
+.It Xo trap
+.Op Ar action
+.Ar signal ...
+.Xc
+Cause the shell to parse and execute action when any of the specified
+signals are received.
+The signals are specified by signal number or as the name of the signal.
+If
+.Ar signal
+is
+.Li 0 ,
+the action is executed when the shell exits.
+.Ar action
+may be null, which cause the specified signals to be ignored.
+With
+.Ar action
+omitted or set to `-' the specified signals are set to their default action.
+When the shell forks off a subshell, it resets trapped (but not ignored)
+signals to the default action.
+The
+.Ic trap
+command has no effect on signals that were
+ignored on entry to the shell.
+Issuing
+.Ic trap
+with option
+.Ar -l
+will print a list of valid signal names.
+.Ic trap
+without any arguments cause it to write a list of signals and their
+associated action to the standard output in a format that is suitable
+as an input to the shell that achieves the same trapping results.
+.Pp
+Examples:
+.Pp
+.Dl trap
+.Pp
+List trapped signals and their corresponding action
+.Pp
+.Dl trap -l
+.Pp
+Print a list of valid signals
+.Pp
+.Dl trap '' INT QUIT tstp 30
+.Pp
+Ignore signals INT QUIT TSTP USR1
+.Pp
+.Dl trap date INT
+.Pp
+Print date upon receiving signal INT
+.It type Op Ar name ...
+Interpret each name as a command and print the resolution of the command
+search.
+Possible resolutions are:
+shell keyword, alias, shell builtin,
+command, tracked alias and not found.
+For aliases the alias expansion is
+printed; for commands and tracked aliases the complete pathname of the
+command is printed.
+.It ulimit Xo
+.Op Fl H \*(Ba Fl S
+.Op Fl a \*(Ba Fl tfdscmlpn Op Ar value
+.Xc
+Inquire about or set the hard or soft limits on processes or set new
+limits.
+The choice between hard limit (which no process is allowed to
+violate, and which may not be raised once it has been lowered) and soft
+limit (which causes processes to be signaled but not necessarily killed,
+and which may be raised) is made with these flags:
+.Bl -tag -width Fl
+.It Fl H
+set or inquire about hard limits
+.It Fl S
+set or inquire about soft limits.
+If neither
+.Fl H
+nor
+.Fl S
+is specified, the soft limit is displayed or both limits are set.
+If both are specified, the last one wins.
+.El
+.Pp
+.Bl -tag -width Fl
+The limit to be interrogated or set, then, is chosen by specifying
+any one of these flags:
+.It Fl a
+show all the current limits
+.It Fl b
+show or set the limit on the socket buffer size of a process (in bytes)
+.It Fl t
+show or set the limit on CPU time (in seconds)
+.It Fl f
+show or set the limit on the largest file that can be created
+(in 512-byte blocks)
+.It Fl d
+show or set the limit on the data segment size of a process (in kilobytes)
+.It Fl s
+show or set the limit on the stack size of a process (in kilobytes)
+.It Fl c
+show or set the limit on the largest core dump size that can be produced
+(in 512-byte blocks)
+.It Fl m
+show or set the limit on the total physical memory that can be
+in use by a process (in kilobytes)
+.It Fl l
+show or set the limit on how much memory a process can lock with
+.Xr mlock 2
+(in kilobytes)
+.It Fl p
+show or set the limit on the number of processes this user can
+have at one time
+.It Fl n
+show or set the limit on the number of files a process can have open at once
+.El
+.Pp
+If none of these is specified, it is the limit on file size that is shown
+or set.
+If value is specified, the limit is set to that number; otherwise
+the current limit is displayed.
+.Pp
+Limits of an arbitrary process can be displayed or set using the
+.Xr sysctl 8
+utility.
+.Pp
+.It umask Op Ar mask
+Set the value of umask (see
+.Xr umask 2 )
+to the specified octal value.
+If the argument is omitted, the umask value is printed.
+.It unalias Xo
+.Op Fl a
+.Op Ar name
+.Xc
+If
+.Ar name
+is specified, the shell removes that alias.
+If
+.Fl a
+is specified, all aliases are removed.
+.It unset Ar name ...
+The specified variables and functions are unset and unexported.
+If a given name corresponds to both a variable and a function, both
+the variable and the function are unset.
+.It wait Op Ar job
+Wait for the specified job to complete and return the exit status of the
+last process in the job.
+If the argument is omitted, wait for all jobs to
+complete and then return an exit status of zero.
+.El
+.Ss Command Line Editing
+When
+.Nm
+is being used interactively from a terminal, the current command
+and the command history (see
+.Ic fc
+in
+.Sx Builtins )
+can be edited using emacs-mode or vi-mode command-line editing.
+The command
+.Ql set -o emacs
+enables emacs-mode editing.
+The command
+.Ql set -o vi
+enables vi-mode editing and places sh into vi insert mode.
+(See the
+.Sx Argument List Processing
+section above.)
+.Pp
+The vi mode uses commands similar to a subset of those described in the
+.Xr vi 1
+man page.
+With vi-mode
+enabled, sh can be switched between insert mode and command mode.
+It's similar to vi: typing
+.Aq ESC
+will throw you into command VI command mode.
+Hitting
+.Aq return
+while in command mode will pass the line to the shell.
+.Pp
+The emacs mode uses commands similar to a subset available in
+the emacs editor.
+With emacs-mode enabled, special keys can be used to modify the text
+in the buffer using the control key.
+.Pp
+.Nm
+uses the
+.Xr editline 3
+library.
+.Sh EXIT STATUS
+Errors that are detected by the shell, such as a syntax error, will cause the
+shell to exit with a non-zero exit status.
+If the shell is not an
+interactive shell, the execution of the shell file will be aborted.
+Otherwise
+the shell will return the exit status of the last command executed, or
+if the exit builtin is used with a numeric argument, it will return the
+argument.
+.Sh ENVIRONMENT
+.Bl -tag -width MAILCHECK
+.It Ev HOME
+Set automatically by
+.Xr login 1
+from the user's login directory in the password file
+.Pq Xr passwd 5 .
+This environment variable also functions as the default argument for the
+cd builtin.
+.It Ev PATH
+The default search path for executables.
+See the above section
+.Sx Path Search .
+.It Ev CDPATH
+The search path used with the cd builtin.
+.It Ev LANG
+The string used to specify localization information that allows users
+to work with different culture-specific and language conventions.
+See
+.Xr nls 7 .
+.It Ev MAIL
+The name of a mail file, that will be checked for the arrival of new mail.
+Overridden by
+.Ev MAILPATH .
+.It Ev MAILCHECK
+The frequency in seconds that the shell checks for the arrival of mail
+in the files specified by the
+.Ev MAILPATH
+or the
+.Ev MAIL
+file.
+If set to 0, the check will occur at each prompt.
+.It Ev MAILPATH
+A colon
+.Dq \&:
+separated list of file names, for the shell to check for incoming mail.
+This environment setting overrides the
+.Ev MAIL
+setting.
+There is a maximum of 10 mailboxes that can be monitored at once.
+.It Ev PS1
+The primary prompt string, which defaults to
+.Dq $ \  ,
+unless you are the superuser, in which case it defaults to
+.Dq # \  .
+.It Ev PS2
+The secondary prompt string, which defaults to
+.Dq \*[Gt] \  .
+.It Ev PS4
+Output before each line when execution trace (set -x) is enabled,
+defaults to
+.Dq + \  .
+.It Ev IFS
+Input Field Separators.
+This is normally set to
+.Aq space ,
+.Aq tab ,
+and
+.Aq newline .
+See the
+.Sx White Space Splitting
+section for more details.
+.It Ev TERM
+The default terminal setting for the shell.
+This is inherited by
+children of the shell, and is used in the history editing modes.
+.It Ev HISTSIZE
+The number of lines in the history buffer for the shell.
+.El
+.Sh FILES
+.Bl -item -width HOMEprofilexxxx
+.It
+.Pa $HOME/.profile
+.It
+.Pa /etc/profile
+.El
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr echo 1 ,
+.Xr getopt 1 ,
+.Xr ksh 1 ,
+.Xr login 1 ,
+.Xr printf 1 ,
+.Xr test 1 ,
+.Xr editline 3 ,
+.Xr getopt 3 ,
+.\" .Xr profile 4 ,
+.Xr editrc 5 ,
+.Xr passwd 5 ,
+.Xr environ 7 ,
+.Xr nls 7 ,
+.Xr sysctl 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
+It was, however, unmaintainable so we wrote this one.
+.Sh BUGS
+Setuid shell scripts should be avoided at all costs, as they are a
+significant security risk.
+.Pp
+PS1, PS2, and PS4 should be subject to parameter expansion before
+being displayed.
diff --git a/sh/shell.h b/sh/shell.h
new file mode 100644
index 0000000..94be27a
--- /dev/null
+++ b/sh/shell.h
@@ -0,0 +1,83 @@
+/*	$NetBSD: shell.h,v 1.17 2003/08/07 09:05:38 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)shell.h	8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * The follow should be set to reflect the type of system you have:
+ *	JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ *	SHORTNAMES -> 1 if your linker cannot handle long names.
+ *	define BSD if you are running 4.2 BSD or later.
+ *	define SYSV if you are running under System V.
+ *	define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
+ *	define DEBUG=2 to compile in and turn on debugging.
+ *	define DO_SHAREDVFORK to indicate that vfork(2) shares its address
+ *	       with its parent.
+ *
+ * When debugging is on, debugging info will be written to ./trace and
+ * a quit signal will generate a core dump.
+ */
+
+#include <sys/param.h>
+
+#define JOBS 1
+#ifndef BSD
+#define BSD 1
+#endif
+
+#ifndef DO_SHAREDVFORK
+#if __NetBSD_Version__ >= 104000000
+#define DO_SHAREDVFORK
+#endif
+#endif
+
+typedef void *pointer;
+#ifndef NULL
+#define NULL (void *)0
+#endif
+#define STATIC	/* empty */
+#define MKINIT	/* empty */
+
+#include <sys/cdefs.h>
+
+extern char nullstr[1];		/* null string */
+
+
+#ifdef DEBUG
+#define TRACE(param)	trace param
+#define TRACEV(param)	tracev param
+#else
+#define TRACE(param)
+#define TRACEV(param)
+#endif
diff --git a/sh/show.c b/sh/show.c
new file mode 100644
index 0000000..e92aa51
--- /dev/null
+++ b/sh/show.c
@@ -0,0 +1,425 @@
+/*	$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "shell.h"
+#include "parser.h"
+#include "nodes.h"
+#include "mystring.h"
+#include "show.h"
+#include "options.h"
+
+
+#ifdef DEBUG
+static void shtree(union node *, int, char *, FILE*);
+static void shcmd(union node *, FILE *);
+static void sharg(union node *, FILE *);
+static void indent(int, char *, FILE *);
+static void trstring(char *);
+
+
+void
+showtree(union node *n)
+{
+	trputs("showtree called\n");
+	shtree(n, 1, NULL, stdout);
+}
+
+
+static void
+shtree(union node *n, int ind, char *pfx, FILE *fp)
+{
+	struct nodelist *lp;
+	const char *s;
+
+	if (n == NULL)
+		return;
+
+	indent(ind, pfx, fp);
+	switch(n->type) {
+	case NSEMI:
+		s = "; ";
+		goto binop;
+	case NAND:
+		s = " && ";
+		goto binop;
+	case NOR:
+		s = " || ";
+binop:
+		shtree(n->nbinary.ch1, ind, NULL, fp);
+	   /*    if (ind < 0) */
+			fputs(s, fp);
+		shtree(n->nbinary.ch2, ind, NULL, fp);
+		break;
+	case NCMD:
+		shcmd(n, fp);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	case NPIPE:
+		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+			shcmd(lp->n, fp);
+			if (lp->next)
+				fputs(" | ", fp);
+		}
+		if (n->npipe.backgnd)
+			fputs(" &", fp);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	default:
+		fprintf(fp, "<node type %d>", n->type);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	}
+}
+
+
+
+static void
+shcmd(union node *cmd, FILE *fp)
+{
+	union node *np;
+	int first;
+	const char *s;
+	int dftfd;
+
+	first = 1;
+	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
+		if (! first)
+			putchar(' ');
+		sharg(np, fp);
+		first = 0;
+	}
+	for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
+		if (! first)
+			putchar(' ');
+		switch (np->nfile.type) {
+			case NTO:	s = ">";  dftfd = 1; break;
+			case NCLOBBER:	s = ">|"; dftfd = 1; break;
+			case NAPPEND:	s = ">>"; dftfd = 1; break;
+			case NTOFD:	s = ">&"; dftfd = 1; break;
+			case NFROM:	s = "<";  dftfd = 0; break;
+			case NFROMFD:	s = "<&"; dftfd = 0; break;
+			case NFROMTO:	s = "<>"; dftfd = 0; break;
+			default:  	s = "*error*"; dftfd = 0; break;
+		}
+		if (np->nfile.fd != dftfd)
+			fprintf(fp, "%d", np->nfile.fd);
+		fputs(s, fp);
+		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
+			fprintf(fp, "%d", np->ndup.dupfd);
+		} else {
+			sharg(np->nfile.fname, fp);
+		}
+		first = 0;
+	}
+}
+
+
+
+static void
+sharg(union node *arg, FILE *fp)
+{
+	char *p;
+	struct nodelist *bqlist;
+	int subtype;
+
+	if (arg->type != NARG) {
+		printf("<node type %d>\n", arg->type);
+		abort();
+	}
+	bqlist = arg->narg.backquote;
+	for (p = arg->narg.text ; *p ; p++) {
+		switch (*p) {
+		case CTLESC:
+			putc(*++p, fp);
+			break;
+		case CTLVAR:
+			putc('$', fp);
+			putc('{', fp);
+			subtype = *++p;
+			if (subtype == VSLENGTH)
+				putc('#', fp);
+
+			while (*p != '=')
+				putc(*p++, fp);
+
+			if (subtype & VSNUL)
+				putc(':', fp);
+
+			switch (subtype & VSTYPE) {
+			case VSNORMAL:
+				putc('}', fp);
+				break;
+			case VSMINUS:
+				putc('-', fp);
+				break;
+			case VSPLUS:
+				putc('+', fp);
+				break;
+			case VSQUESTION:
+				putc('?', fp);
+				break;
+			case VSASSIGN:
+				putc('=', fp);
+				break;
+			case VSTRIMLEFT:
+				putc('#', fp);
+				break;
+			case VSTRIMLEFTMAX:
+				putc('#', fp);
+				putc('#', fp);
+				break;
+			case VSTRIMRIGHT:
+				putc('%', fp);
+				break;
+			case VSTRIMRIGHTMAX:
+				putc('%', fp);
+				putc('%', fp);
+				break;
+			case VSLENGTH:
+				break;
+			default:
+				printf("<subtype %d>", subtype);
+			}
+			break;
+		case CTLENDVAR:
+		     putc('}', fp);
+		     break;
+		case CTLBACKQ:
+		case CTLBACKQ|CTLQUOTE:
+			putc('$', fp);
+			putc('(', fp);
+			shtree(bqlist->n, -1, NULL, fp);
+			putc(')', fp);
+			break;
+		default:
+			putc(*p, fp);
+			break;
+		}
+	}
+}
+
+
+static void
+indent(int amount, char *pfx, FILE *fp)
+{
+	int i;
+
+	for (i = 0 ; i < amount ; i++) {
+		if (pfx && i == amount - 1)
+			fputs(pfx, fp);
+		putc('\t', fp);
+	}
+}
+#endif
+
+
+
+/*
+ * Debugging stuff.
+ */
+
+
+FILE *tracefile;
+
+
+#ifdef DEBUG
+void
+trputc(int c)
+{
+	if (debug != 1)
+		return;
+	putc(c, tracefile);
+}
+#endif
+
+void
+trace(const char *fmt, ...)
+{
+#ifdef DEBUG
+	va_list va;
+
+	if (debug != 1)
+		return;
+	va_start(va, fmt);
+	(void) vfprintf(tracefile, fmt, va);
+	va_end(va);
+#endif
+}
+
+void
+tracev(const char *fmt, va_list va)
+{
+#ifdef DEBUG
+	if (debug != 1)
+		return;
+	(void) vfprintf(tracefile, fmt, va);
+#endif
+}
+
+
+#ifdef DEBUG
+void
+trputs(const char *s)
+{
+	if (debug != 1)
+		return;
+	fputs(s, tracefile);
+}
+
+
+static void
+trstring(char *s)
+{
+	char *p;
+	char c;
+
+	if (debug != 1)
+		return;
+	putc('"', tracefile);
+	for (p = s ; *p ; p++) {
+		switch (*p) {
+		case '\n':  c = 'n';  goto backslash;
+		case '\t':  c = 't';  goto backslash;
+		case '\r':  c = 'r';  goto backslash;
+		case '"':  c = '"';  goto backslash;
+		case '\\':  c = '\\';  goto backslash;
+		case CTLESC:  c = 'e';  goto backslash;
+		case CTLVAR:  c = 'v';  goto backslash;
+		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
+		case CTLBACKQ:  c = 'q';  goto backslash;
+		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
+backslash:	  putc('\\', tracefile);
+			putc(c, tracefile);
+			break;
+		default:
+			if (*p >= ' ' && *p <= '~')
+				putc(*p, tracefile);
+			else {
+				putc('\\', tracefile);
+				putc(*p >> 6 & 03, tracefile);
+				putc(*p >> 3 & 07, tracefile);
+				putc(*p & 07, tracefile);
+			}
+			break;
+		}
+	}
+	putc('"', tracefile);
+}
+#endif
+
+
+void
+trargs(char **ap)
+{
+#ifdef DEBUG
+	if (debug != 1)
+		return;
+	while (*ap) {
+		trstring(*ap++);
+		if (*ap)
+			putc(' ', tracefile);
+		else
+			putc('\n', tracefile);
+	}
+#endif
+}
+
+
+#ifdef DEBUG
+void
+opentrace(void)
+{
+	char s[100];
+#ifdef O_APPEND
+	int flags;
+#endif
+
+	if (debug != 1) {
+		if (tracefile)
+			fflush(tracefile);
+		/* leave open because libedit might be using it */
+		return;
+	}
+#ifdef not_this_way
+	{
+		char *p;
+		if ((p = getenv("HOME")) == NULL) {
+			if (geteuid() == 0)
+				p = "/";
+			else
+				p = "/tmp";
+		}
+		scopy(p, s);
+		strcat(s, "/trace");
+	}
+#else
+	scopy("./trace", s);
+#endif /* not_this_way */
+	if (tracefile) {
+		if (!freopen(s, "a", tracefile)) {
+			fprintf(stderr, "Can't re-open %s\n", s);
+			debug = 0;
+			return;
+		}
+	} else {
+		if ((tracefile = fopen(s, "a")) == NULL) {
+			fprintf(stderr, "Can't open %s\n", s);
+			debug = 0;
+			return;
+		}
+	}
+#ifdef O_APPEND
+	if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
+		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+#endif
+	setlinebuf(tracefile);
+	fputs("\nTracing started.\n", tracefile);
+}
+#endif /* DEBUG */
diff --git a/sh/show.h b/sh/show.h
new file mode 100644
index 0000000..3152ff2
--- /dev/null
+++ b/sh/show.h
@@ -0,0 +1,45 @@
+/*	$NetBSD: show.h,v 1.7 2003/08/07 09:05:38 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1995
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)show.h	1.1 (Berkeley) 5/4/95
+ */
+
+#include <stdarg.h>
+
+union node;
+void showtree(union node *);
+void trace(const char *, ...);
+void tracev(const char *, va_list);
+void trargs(char **);
+#ifdef DEBUG
+void trputc(int);
+void trputs(const char *);
+void opentrace(void);
+#endif
diff --git a/sh/syntax.c b/sh/syntax.c
new file mode 100644
index 0000000..094f674
--- /dev/null
+++ b/sh/syntax.c
@@ -0,0 +1,102 @@
+/*	$NetBSD: syntax.c,v 1.1 2004/01/17 17:38:12 dsl Exp $	*/
+
+#include "shell.h"
+#include "syntax.h"
+#include "parser.h"
+#include <limits.h>
+
+#if CWORD != 0
+#error initialisation assumes 'CWORD' is zero
+#endif
+
+#define ndx(ch) (ch + 1 - CHAR_MIN)
+#define set(ch, val) [ndx(ch)] = val,
+#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val,
+
+/* syntax table used when not in quotes */
+const char basesyntax[257] = { CEOF,
+    set_range(CTL_FIRST, CTL_LAST, CCTL)
+    set('\n', CNL)
+    set('\\', CBACK)
+    set('\'', CSQUOTE)
+    set('"', CDQUOTE)
+    set('`', CBQUOTE)
+    set('$', CVAR)
+    set('}', CENDVAR)
+    set('<', CSPCL)
+    set('>', CSPCL)
+    set('(', CSPCL)
+    set(')', CSPCL)
+    set(';', CSPCL)
+    set('&', CSPCL)
+    set('|', CSPCL)
+    set(' ', CSPCL)
+    set('\t', CSPCL)
+};
+
+/* syntax table used when in double quotes */
+const char dqsyntax[257] = { CEOF,
+    set_range(CTL_FIRST, CTL_LAST, CCTL)
+    set('\n', CNL)
+    set('\\', CBACK)
+    set('"', CDQUOTE)
+    set('`', CBQUOTE)
+    set('$', CVAR)
+    set('}', CENDVAR)
+    /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+    set('!', CCTL)
+    set('*', CCTL)
+    set('?', CCTL)
+    set('[', CCTL)
+    set('=', CCTL)
+    set('~', CCTL)
+    set(':', CCTL)
+    set('/', CCTL)
+    set('-', CCTL)
+};
+
+/* syntax table used when in single quotes */
+const char sqsyntax[257] = { CEOF,
+    set_range(CTL_FIRST, CTL_LAST, CCTL)
+    set('\n', CNL)
+    set('\'', CSQUOTE)
+    /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */
+    set('!', CCTL)
+    set('*', CCTL)
+    set('?', CCTL)
+    set('[', CCTL)
+    set('=', CCTL)
+    set('~', CCTL)
+    set(':', CCTL)
+    set('/', CCTL)
+    set('-', CCTL)
+};
+
+/* syntax table used when in arithmetic */
+const char arisyntax[257] = { CEOF,
+    set_range(CTL_FIRST, CTL_LAST, CCTL)
+    set('\n', CNL)
+    set('\\', CBACK)
+    set('`', CBQUOTE)
+    set('\'', CSQUOTE)
+    set('"', CDQUOTE)
+    set('$', CVAR)
+    set('}', CENDVAR)
+    set('(', CLP)
+    set(')', CRP)
+};
+
+/* character classification table */
+const char is_type[257] = { 0,
+    set_range('0', '9', ISDIGIT)
+    set_range('a', 'z', ISLOWER)
+    set_range('A', 'Z', ISUPPER)
+    set('_', ISUNDER)
+    set('#', ISSPECL)
+    set('?', ISSPECL)
+    set('$', ISSPECL)
+    set('!', ISSPECL)
+    set('-', ISSPECL)
+    set('*', ISSPECL)
+    set('@', ISSPECL)
+};
diff --git a/sh/syntax.h b/sh/syntax.h
new file mode 100644
index 0000000..89a32dc
--- /dev/null
+++ b/sh/syntax.h
@@ -0,0 +1,83 @@
+/*	$NetBSD: syntax.h,v 1.2 2004/01/17 17:38:12 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <ctype.h>
+
+/* Syntax classes */
+#define CWORD 0			/* character is nothing special */
+#define CNL 1			/* newline character */
+#define CBACK 2			/* a backslash character */
+#define CSQUOTE 3		/* single quote */
+#define CDQUOTE 4		/* double quote */
+#define CBQUOTE 5		/* backwards single quote */
+#define CVAR 6			/* a dollar sign */
+#define CENDVAR 7		/* a '}' character */
+#define CLP 8			/* a left paren in arithmetic */
+#define CRP 9			/* a right paren in arithmetic */
+#define CEOF 10			/* end of file */
+#define CCTL 11			/* like CWORD, except it must be escaped */
+#define CSPCL 12		/* these terminate a word */
+
+/* Syntax classes for is_ functions */
+#define ISDIGIT 01		/* a digit */
+#define ISUPPER 02		/* an upper case letter */
+#define ISLOWER 04		/* a lower case letter */
+#define ISUNDER 010		/* an underscore */
+#define ISSPECL 020		/* the name of a special parameter */
+
+#define PEOF (CHAR_MIN - 1)
+#define SYNBASE (-PEOF)
+/* XXX UPEOF is CHAR_MAX, so is a valid 'char' value... */
+#define UPEOF ((char)PEOF)
+
+
+#define BASESYNTAX (basesyntax + SYNBASE)
+#define DQSYNTAX (dqsyntax + SYNBASE)
+#define SQSYNTAX (sqsyntax + SYNBASE)
+#define ARISYNTAX (arisyntax + SYNBASE)
+
+/* These defines assume that the digits are contiguous */
+#define is_digit(c)	((unsigned)((c) - '0') <= 9)
+#define is_alpha(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && isalpha((unsigned char)(c)))
+#define is_name(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalpha((unsigned char)(c))))
+#define is_in_name(c)	(((char)(c)) != UPEOF && ((c) < CTL_FIRST || (c) > CTL_LAST) && ((c) == '_' || isalnum((unsigned char)(c))))
+#define is_special(c)	((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
+#define digit_val(c)	((c) - '0')
+
+extern const char basesyntax[];
+extern const char dqsyntax[];
+extern const char sqsyntax[];
+extern const char arisyntax[];
+extern const char is_type[];
diff --git a/sh/token.h b/sh/token.h
new file mode 100644
index 0000000..c961f01
--- /dev/null
+++ b/sh/token.h
@@ -0,0 +1,112 @@
+#define TEOF 0
+#define TNL 1
+#define TSEMI 2
+#define TBACKGND 3
+#define TAND 4
+#define TOR 5
+#define TPIPE 6
+#define TLP 7
+#define TRP 8
+#define TENDCASE 9
+#define TENDBQUOTE 10
+#define TREDIR 11
+#define TWORD 12
+#define TIF 13
+#define TTHEN 14
+#define TELSE 15
+#define TELIF 16
+#define TFI 17
+#define TWHILE 18
+#define TUNTIL 19
+#define TFOR 20
+#define TDO 21
+#define TDONE 22
+#define TBEGIN 23
+#define TEND 24
+#define TCASE 25
+#define TESAC 26
+#define TNOT 27
+
+/* Array indicating which tokens mark the end of a list */
+const char tokendlist[] = {
+	1,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	1,
+	1,
+	1,
+	0,
+	0,
+	0,
+	1,
+	1,
+	1,
+	1,
+	0,
+	0,
+	0,
+	1,
+	1,
+	0,
+	1,
+	0,
+	1,
+	0,
+};
+
+const char *const tokname[] = {
+	"end of file",
+	"newline",
+	"\";\"",
+	"\"&\"",
+	"\"&&\"",
+	"\"||\"",
+	"\"|\"",
+	"\"(\"",
+	"\")\"",
+	"\";;\"",
+	"\"`\"",
+	"redirection",
+	"word",
+	"\"if\"",
+	"\"then\"",
+	"\"else\"",
+	"\"elif\"",
+	"\"fi\"",
+	"\"while\"",
+	"\"until\"",
+	"\"for\"",
+	"\"do\"",
+	"\"done\"",
+	"\"{\"",
+	"\"}\"",
+	"\"case\"",
+	"\"esac\"",
+	"\"!\"",
+};
+
+#define KWDOFFSET 13
+
+const char *const parsekwd[] = {
+	"if",
+	"then",
+	"else",
+	"elif",
+	"fi",
+	"while",
+	"until",
+	"for",
+	"do",
+	"done",
+	"{",
+	"}",
+	"case",
+	"esac",
+	"!",
+	0
+};
diff --git a/sh/trap.c b/sh/trap.c
new file mode 100644
index 0000000..b3b2db4
--- /dev/null
+++ b/sh/trap.c
@@ -0,0 +1,470 @@
+/*	$NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)trap.c	8.5 (Berkeley) 6/5/95";
+#else
+__RCSID("$NetBSD: trap.c,v 1.31 2005/01/11 19:38:57 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"	/* for other headers */
+#include "eval.h"
+#include "jobs.h"
+#include "show.h"
+#include "options.h"
+#include "syntax.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "trap.h"
+#include "mystring.h"
+#include "var.h"
+
+static const char *sys_signame[NSIG] = {
+	"Unused",
+	"HUP",      "INT",    "QUIT",    "ILL",
+	"TRAP",     "ABRT",   "BUS",     "FPE",
+	"KILL",     "USR1",   "SEGV",    "USR2",
+	"PIPE",     "ALRM",   "TERM",
+	"Unknown",
+	"CHLD",
+	"CONT",     "STOP",   "TSTP",    "TTIN",
+	"TTOU",     "URG",    "XCPU",    "XFSZ",
+	"VTALRM",   "PROF",   "WINCH",   "IO",
+	"PWR",      "SYS"
+};
+
+/*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes.  A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+
+#define S_DFL 1			/* default signal handling (SIG_DFL) */
+#define S_CATCH 2		/* signal is caught */
+#define S_IGN 3			/* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4		/* signal is ignored permenantly */
+#define S_RESET 5		/* temporary - to reset a hard ignored sig */
+
+
+char *trap[NSIG+1];		/* trap handler commands */
+MKINIT char sigmode[NSIG];	/* current value of signal */
+char gotsig[NSIG];		/* indicates specified signal received */
+int pendingsigs;		/* indicates some signal received */
+
+static int getsigaction(int, sig_t *);
+
+/*
+ * return the signal number described by `p' (as a number or a name)
+ * or -1 if it isn't one
+ */
+
+static int
+signame_to_signum(const char *p)
+{
+	int i;
+
+	if (is_number(p))
+		return number(p);
+
+	if (strcasecmp(p, "exit") == 0 )
+		return 0;
+	
+	if (strncasecmp(p, "sig", 3) == 0)
+		p += 3;
+
+	for (i = 0; i < NSIG; ++i)
+		if (strcasecmp (p, sys_signame[i]) == 0)
+			return i;
+	return -1;
+}
+
+/*
+ * Print a list of valid signal names
+ */
+static void
+printsignals(void)
+{
+	int n;
+
+	out1str("EXIT ");
+
+	for (n = 1; n < NSIG; n++) {
+		out1fmt("%s", sys_signame[n]);
+		if ((n == NSIG/2) ||  n == (NSIG - 1))
+			out1str("\n");
+		else
+			out1c(' ');
+	}
+}
+
+/*
+ * The trap builtin.
+ */
+
+int
+trapcmd(int argc, char **argv)
+{
+	char *action;
+	char **ap;
+	int signo;
+
+	if (argc <= 1) {
+		for (signo = 0 ; signo <= NSIG ; signo++)
+			if (trap[signo] != NULL) {
+				out1fmt("trap -- ");
+				print_quoted(trap[signo]);
+				out1fmt(" %s\n",
+				    (signo) ? sys_signame[signo] : "EXIT");
+			}
+		return 0;
+	}
+	ap = argv + 1;
+
+	action = NULL;
+
+	if (strcmp(*ap, "--") == 0)
+		if (*++ap == NULL)
+			return 0;
+
+	if (signame_to_signum(*ap) == -1) {
+		if ((*ap)[0] == '-') {
+			if ((*ap)[1] == '\0')
+				ap++;
+			else if ((*ap)[1] == 'l' && (*ap)[2] == '\0') {
+				printsignals();
+				return 0;
+			}
+			else
+				error("bad option %s\n", *ap);
+		}
+		else
+			action = *ap++;
+	}
+
+	while (*ap) {
+		if (is_number(*ap))
+			signo = number(*ap);
+		else
+			signo = signame_to_signum(*ap);
+
+		if (signo < 0 || signo > NSIG)
+			error("%s: bad trap", *ap);
+
+		INTOFF;
+		if (action)
+			action = savestr(action);
+
+		if (trap[signo])
+			ckfree(trap[signo]);
+
+		trap[signo] = action;
+
+		if (signo != 0)
+			setsignal(signo, 0);
+		INTON;
+		ap++;
+	}
+	return 0;
+}
+
+
+
+/*
+ * Clear traps on a fork or vfork.
+ * Takes one arg vfork, to tell it to not be destructive of
+ * the parents variables.
+ */
+
+void
+clear_traps(int vforked)
+{
+	char **tp;
+
+	for (tp = trap ; tp <= &trap[NSIG] ; tp++) {
+		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
+			INTOFF;
+			if (!vforked) {
+				ckfree(*tp);
+				*tp = NULL;
+			}
+			if (tp != &trap[0])
+				setsignal(tp - trap, vforked);
+			INTON;
+		}
+	}
+}
+
+
+
+/*
+ * Set the signal handler for the specified signal.  The routine figures
+ * out what it should be set to.
+ */
+
+long
+setsignal(int signo, int vforked)
+{
+	int action;
+	sig_t sigact = SIG_DFL;
+	struct sigaction act, oact;
+	char *t, tsig;
+
+	if ((t = trap[signo]) == NULL)
+		action = S_DFL;
+	else if (*t != '\0')
+		action = S_CATCH;
+	else
+		action = S_IGN;
+	if (rootshell && !vforked && action == S_DFL) {
+		switch (signo) {
+		case SIGINT:
+			if (iflag || minusc || sflag == 0)
+				action = S_CATCH;
+			break;
+		case SIGQUIT:
+#ifdef DEBUG
+			if (debug)
+				break;
+#endif
+			/* FALLTHROUGH */
+		case SIGTERM:
+			if (iflag)
+				action = S_IGN;
+			break;
+#if JOBS
+		case SIGTSTP:
+		case SIGTTOU:
+			if (mflag)
+				action = S_IGN;
+			break;
+#endif
+		}
+	}
+
+	t = &sigmode[signo - 1];
+	tsig = *t;
+	if (tsig == 0) {
+		/*
+		 * current setting unknown
+		 */
+		if (!getsigaction(signo, &sigact)) {
+			/*
+			 * Pretend it worked; maybe we should give a warning
+			 * here, but other shells don't. We don't alter
+			 * sigmode, so that we retry every time.
+			 */
+			return 0;
+		}
+		if (sigact == SIG_IGN) {
+			if (mflag && (signo == SIGTSTP ||
+			     signo == SIGTTIN || signo == SIGTTOU)) {
+				tsig = S_IGN;	/* don't hard ignore these */
+			} else
+				tsig = S_HARD_IGN;
+		} else {
+			tsig = S_RESET;	/* force to be set */
+		}
+	}
+	if (tsig == S_HARD_IGN || tsig == action)
+		return 0;
+	switch (action) {
+		case S_DFL:	sigact = SIG_DFL;	break;
+		case S_CATCH:  	sigact = onsig;		break;
+		case S_IGN:	sigact = SIG_IGN;	break;
+	}
+	if (!vforked)
+		*t = action;
+    act.sa_handler = sigact;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+#ifdef SA_INTERRUPT
+    act.sa_flags |= SA_INTERRUPT;
+#endif
+    if(sigaction(signo, &act, &oact) < 0)
+        return (long) SIG_ERR;
+    return (long) oact.sa_handler;
+}
+
+/*
+ * Return the current setting for sig w/o changing it.
+ */
+static int
+getsigaction(int signo, sig_t *sigact)
+{
+	struct sigaction sa;
+
+	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
+		return 0;
+	*sigact = (sig_t) sa.sa_handler;
+	return 1;
+}
+
+/*
+ * Ignore a signal.
+ */
+
+void
+ignoresig(int signo, int vforked)
+{
+	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN)
+		bsd_signal(signo, SIG_IGN);
+	if (!vforked)
+		sigmode[signo - 1] = S_HARD_IGN;
+}
+
+
+#ifdef mkinit
+INCLUDE <signal.h>
+INCLUDE "trap.h"
+
+SHELLPROC {
+	char *sm;
+
+	clear_traps(0);
+	for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
+		if (*sm == S_IGN)
+			*sm = S_HARD_IGN;
+	}
+}
+#endif
+
+
+
+/*
+ * Signal handler.
+ */
+
+void
+onsig(int signo)
+{
+	bsd_signal(signo, onsig);
+	if (signo == SIGINT && trap[SIGINT] == NULL) {
+		onint();
+		return;
+	}
+	gotsig[signo - 1] = 1;
+	pendingsigs++;
+}
+
+
+
+/*
+ * Called to execute a trap.  Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
+ */
+
+void
+dotrap(void)
+{
+	int i;
+	int savestatus;
+
+	for (;;) {
+		for (i = 1 ; ; i++) {
+			if (gotsig[i - 1])
+				break;
+			if (i >= NSIG)
+				goto done;
+		}
+		gotsig[i - 1] = 0;
+		savestatus=exitstatus;
+		evalstring(trap[i], 0);
+		exitstatus=savestatus;
+	}
+done:
+	pendingsigs = 0;
+}
+
+
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+
+
+void
+setinteractive(int on)
+{
+	static int is_interactive;
+
+	if (on == is_interactive)
+		return;
+	setsignal(SIGINT, 0);
+	setsignal(SIGQUIT, 0);
+	setsignal(SIGTERM, 0);
+	is_interactive = on;
+}
+
+
+
+/*
+ * Called to exit the shell.
+ */
+
+void
+exitshell(int status)
+{
+	struct jmploc loc1, loc2;
+	char *p;
+
+	TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+	if (setjmp(loc1.loc)) {
+		goto l1;
+	}
+	if (setjmp(loc2.loc)) {
+		goto l2;
+	}
+	handler = &loc1;
+	if ((p = trap[0]) != NULL && *p != '\0') {
+		trap[0] = NULL;
+		evalstring(p, 0);
+	}
+l1:   handler = &loc2;			/* probably unnecessary */
+	flushall();
+#if JOBS
+	setjobctl(0);
+#endif
+l2:   _exit(status);
+	/* NOTREACHED */
+}
diff --git a/sh/trap.h b/sh/trap.h
new file mode 100644
index 0000000..125ef40
--- /dev/null
+++ b/sh/trap.h
@@ -0,0 +1,46 @@
+/*	$NetBSD: trap.h,v 1.17 2003/08/07 09:05:39 agc Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)trap.h	8.3 (Berkeley) 6/5/95
+ */
+
+extern int pendingsigs;
+
+int trapcmd(int, char **);
+void clear_traps(int);
+long setsignal(int, int);
+void ignoresig(int, int);
+void onsig(int);
+void dotrap(void);
+void setinteractive(int);
+void exitshell(int) __attribute__((__noreturn__));
diff --git a/sh/var.c b/sh/var.c
new file mode 100644
index 0000000..a1f1689
--- /dev/null
+++ b/sh/var.c
@@ -0,0 +1,825 @@
+/*	$NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: var.c,v 1.36 2004/10/06 10:23:43 enami Exp $");
+#endif
+#endif /* not lint */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <paths.h>
+
+/*
+ * Shell variables.
+ */
+
+#include "shell.h"
+#include "output.h"
+#include "expand.h"
+#include "nodes.h"	/* for other headers */
+#include "eval.h"	/* defines cmdenviron */
+#include "exec.h"
+#include "syntax.h"
+#include "options.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "parser.h"
+#include "show.h"
+#ifndef SMALL
+#include "myhistedit.h"
+#endif
+
+#ifdef SMALL
+#define VTABSIZE 39
+#else
+#define VTABSIZE 517
+#endif
+
+
+struct varinit {
+	struct var *var;
+	int flags;
+	const char *text;
+	void (*func)(const char *);
+};
+
+
+#if ATTY
+struct var vatty;
+#endif
+#ifdef WITH_HISTORY
+struct var vhistsize;
+struct var vterm;
+#endif
+struct var vifs;
+struct var vmpath;
+struct var vpath;
+struct var vps1;
+struct var vps2;
+struct var vps4;
+struct var vvers;
+struct var voptind;
+
+const struct varinit varinit[] = {
+#if ATTY
+	{ &vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY=",
+	  NULL },
+#endif
+#ifdef WITH_HISTORY
+	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
+	  sethistsize },
+#endif
+	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
+	  NULL },
+	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
+	  NULL },
+	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
+	  changepath },
+	/*
+	 * vps1 depends on uid
+	 */
+	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
+	  NULL },
+	{ &vps4,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",
+	  NULL },
+#ifdef WITH_HISTORY
+	{ &vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM=",
+	  setterm },
+#endif
+	{ &voptind,	VSTRFIXED|VTEXTFIXED|VNOFUNC,	"OPTIND=1",
+	  getoptsreset },
+	{ NULL,	0,				NULL,
+	  NULL }
+};
+
+struct var *vartab[VTABSIZE];
+
+STATIC int strequal(const char *, const char *);
+STATIC struct var *find_var(const char *, struct var ***, int *);
+
+/*
+ * Initialize the varable symbol tables and import the environment
+ */
+
+#ifdef mkinit
+INCLUDE "var.h"
+MKINIT char **environ;
+INIT {
+	char **envp;
+
+	initvar();
+	for (envp = environ ; *envp ; envp++) {
+		if (strchr(*envp, '=')) {
+			setvareq(*envp, VEXPORT|VTEXTFIXED);
+		}
+	}
+}
+#endif
+
+
+/*
+ * This routine initializes the builtin variables.  It is called when the
+ * shell is initialized and again when a shell procedure is spawned.
+ */
+
+void
+initvar(void)
+{
+	const struct varinit *ip;
+	struct var *vp;
+	struct var **vpp;
+
+	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
+		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
+			continue;
+		vp->next = *vpp;
+		*vpp = vp;
+		vp->text = strdup(ip->text);
+		vp->flags = ip->flags;
+		vp->func = ip->func;
+	}
+	/*
+	 * PS1 depends on uid
+	 */
+	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
+		vps1.next = *vpp;
+		*vpp = &vps1;
+		vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# ");
+		vps1.flags = VSTRFIXED|VTEXTFIXED;
+	}
+}
+
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
+
+int
+setvarsafe(const char *name, const char *val, int flags)
+{
+	struct jmploc jmploc;
+	struct jmploc *volatile savehandler = handler;
+	int err = 0;
+#ifdef __GNUC__
+	(void) &err;
+#endif
+
+	if (setjmp(jmploc.loc))
+		err = 1;
+	else {
+		handler = &jmploc;
+		setvar(name, val, flags);
+	}
+	handler = savehandler;
+	return err;
+}
+
+/*
+ * Set the value of a variable.  The flags argument is ored with the
+ * flags of the variable.  If val is NULL, the variable is unset.
+ */
+
+void
+setvar(const char *name, const char *val, int flags)
+{
+	const char *p;
+	const char *q;
+	char *d;
+	int len;
+	int namelen;
+	char *nameeq;
+	int isbad;
+
+	isbad = 0;
+	p = name;
+	if (! is_name(*p))
+		isbad = 1;
+	p++;
+	for (;;) {
+		if (! is_in_name(*p)) {
+			if (*p == '\0' || *p == '=')
+				break;
+			isbad = 1;
+		}
+		p++;
+	}
+	namelen = p - name;
+	if (isbad)
+		error("%.*s: bad variable name", namelen, name);
+	len = namelen + 2;		/* 2 is space for '=' and '\0' */
+	if (val == NULL) {
+		flags |= VUNSET;
+	} else {
+		len += strlen(val);
+	}
+	d = nameeq = ckmalloc(len);
+	q = name;
+	while (--namelen >= 0)
+		*d++ = *q++;
+	*d++ = '=';
+	*d = '\0';
+	if (val)
+		scopy(val, d);
+	setvareq(nameeq, flags);
+}
+
+
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value.  Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ */
+
+void
+setvareq(char *s, int flags)
+{
+	struct var *vp, **vpp;
+	int nlen;
+
+	if (aflag)
+		flags |= VEXPORT;
+	vp = find_var(s, &vpp, &nlen);
+	if (vp != NULL) {
+		if (vp->flags & VREADONLY)
+			error("%.*s: is read only", vp->name_len, s);
+		if (flags & VNOSET)
+			return;
+		INTOFF;
+
+		if (vp->func && (flags & VNOFUNC) == 0)
+			(*vp->func)(s + vp->name_len + 1);
+
+		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+			ckfree(vp->text);
+
+		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
+		vp->flags |= flags & ~VNOFUNC;
+		vp->text = s;
+
+		INTON;
+		return;
+	}
+	/* not found */
+	if (flags & VNOSET)
+		return;
+	vp = ckmalloc(sizeof (*vp));
+	vp->flags = flags & ~VNOFUNC;
+	vp->text = s;
+	vp->name_len = nlen;
+	vp->next = *vpp;
+	vp->func = NULL;
+	*vpp = vp;
+}
+
+
+
+/*
+ * Process a linked list of variable assignments.
+ */
+
+void
+listsetvar(struct strlist *list, int flags)
+{
+	struct strlist *lp;
+
+	INTOFF;
+	for (lp = list ; lp ; lp = lp->next) {
+		setvareq(savestr(lp->text), flags);
+	}
+	INTON;
+}
+
+void
+listmklocal(struct strlist *list, int flags)
+{
+	struct strlist *lp;
+
+	for (lp = list ; lp ; lp = lp->next)
+		mklocal(lp->text, flags);
+}
+
+
+/*
+ * Find the value of a variable.  Returns NULL if not set.
+ */
+
+char *
+lookupvar(const char *name)
+{
+	struct var *v;
+
+	v = find_var(name, NULL, NULL);
+	if (v == NULL || v->flags & VUNSET)
+		return NULL;
+	return v->text + v->name_len + 1;
+}
+
+
+
+/*
+ * Search the environment of a builtin command.  If the second argument
+ * is nonzero, return the value of a variable even if it hasn't been
+ * exported.
+ */
+
+char *
+bltinlookup(const char *name, int doall)
+{
+	struct strlist *sp;
+	struct var *v;
+
+	for (sp = cmdenviron ; sp ; sp = sp->next) {
+		if (strequal(sp->text, name))
+			return strchr(sp->text, '=') + 1;
+	}
+
+	v = find_var(name, NULL, NULL);
+
+	if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT)))
+		return NULL;
+	return v->text + v->name_len + 1;
+}
+
+
+
+/*
+ * Generate a list of exported variables.  This routine is used to construct
+ * the third argument to execve when executing a program.
+ */
+
+char **
+environment(void)
+{
+	int nenv;
+	struct var **vpp;
+	struct var *vp;
+	char **env;
+	char **ep;
+
+	nenv = 0;
+	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+		for (vp = *vpp ; vp ; vp = vp->next)
+			if (vp->flags & VEXPORT)
+				nenv++;
+	}
+	ep = env = stalloc((nenv + 1) * sizeof *env);
+	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+		for (vp = *vpp ; vp ; vp = vp->next)
+			if (vp->flags & VEXPORT)
+				*ep++ = vp->text;
+	}
+	*ep = NULL;
+	return env;
+}
+
+
+/*
+ * Called when a shell procedure is invoked to clear out nonexported
+ * variables.  It is also necessary to reallocate variables of with
+ * VSTACK set since these are currently allocated on the stack.
+ */
+
+#ifdef mkinit
+void shprocvar(void);
+
+SHELLPROC {
+	shprocvar();
+}
+#endif
+
+void
+shprocvar(void)
+{
+	struct var **vpp;
+	struct var *vp, **prev;
+
+	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+		for (prev = vpp ; (vp = *prev) != NULL ; ) {
+			if ((vp->flags & VEXPORT) == 0) {
+				*prev = vp->next;
+				if ((vp->flags & VTEXTFIXED) == 0)
+					ckfree(vp->text);
+				if ((vp->flags & VSTRFIXED) == 0)
+					ckfree(vp);
+			} else {
+				if (vp->flags & VSTACK) {
+					vp->text = savestr(vp->text);
+					vp->flags &=~ VSTACK;
+				}
+				prev = &vp->next;
+			}
+		}
+	}
+	initvar();
+}
+
+
+
+/*
+ * Command to list all variables which are set.  Currently this command
+ * is invoked from the set command when the set command is called without
+ * any variables.
+ */
+
+void
+print_quoted(const char *p)
+{
+	const char *q;
+
+	if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
+		out1fmt("%s", p);
+		return;
+	}
+	while (*p) {
+		if (*p == '\'') {
+			out1fmt("\\'");
+			p++;
+			continue;
+		}
+		q = index(p, '\'');
+		if (!q) {
+			out1fmt("'%s'", p );
+			return;
+		}
+		out1fmt("'%.*s'", (int)(q - p), p );
+		p = q;
+	}
+}
+
+static int
+sort_var(const void *v_v1, const void *v_v2)
+{
+	const struct var * const *v1 = v_v1;
+	const struct var * const *v2 = v_v2;
+
+	/* XXX Will anyone notice we include the '=' of the shorter name? */
+	return strcmp((*v1)->text, (*v2)->text);
+}
+
+/*
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
+ */
+
+int
+showvars(const char *name, int flag, int show_value)
+{
+	struct var **vpp;
+	struct var *vp;
+	const char *p;
+
+	static struct var **list;	/* static in case we are interrupted */
+	static int list_len;
+	int count = 0;
+
+	if (!list) {
+		list_len = 32;
+		list = ckmalloc(list_len * sizeof *list);
+	}
+
+	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
+		for (vp = *vpp ; vp ; vp = vp->next) {
+			if (flag && !(vp->flags & flag))
+				continue;
+			if (vp->flags & VUNSET && !(show_value & 2))
+				continue;
+			if (count >= list_len) {
+				list = ckrealloc(list,
+					(list_len << 1) * sizeof *list);
+				list_len <<= 1;
+			}
+			list[count++] = vp;
+		}
+	}
+
+	qsort(list, count, sizeof *list, sort_var);
+
+	for (vpp = list; count--; vpp++) {
+		vp = *vpp;
+		if (name)
+			out1fmt("%s ", name);
+		for (p = vp->text ; *p != '=' ; p++)
+			out1c(*p);
+		if (!(vp->flags & VUNSET) && show_value) {
+			out1fmt("=");
+			print_quoted(++p);
+		}
+		out1c('\n');
+	}
+	return 0;
+}
+
+
+
+/*
+ * The export and readonly commands.
+ */
+
+int
+exportcmd(int argc, char **argv)
+{
+	struct var *vp;
+	char *name;
+	const char *p;
+	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+	int pflag;
+
+	pflag = nextopt("p") == 'p' ? 3 : 0;
+	if (argc <= 1 || pflag) {
+		showvars( pflag ? argv[0] : 0, flag, pflag );
+		return 0;
+	}
+
+	while ((name = *argptr++) != NULL) {
+		if ((p = strchr(name, '=')) != NULL) {
+			p++;
+		} else {
+			vp = find_var(name, NULL, NULL);
+			if (vp != NULL) {
+				vp->flags |= flag;
+				continue;
+			}
+		}
+		setvar(name, p, flag);
+	}
+	return 0;
+}
+
+
+/*
+ * The "local" command.
+ */
+
+int
+localcmd(int argc, char **argv)
+{
+	char *name;
+
+	if (! in_function())
+		error("Not in a function");
+	while ((name = *argptr++) != NULL) {
+		mklocal(name, 0);
+	}
+	return 0;
+}
+
+
+/*
+ * Make a variable a local variable.  When a variable is made local, it's
+ * value and flags are saved in a localvar structure.  The saved values
+ * will be restored when the shell function returns.  We handle the name
+ * "-" as a special case.
+ */
+
+void
+mklocal(const char *name, int flags)
+{
+	struct localvar *lvp;
+	struct var **vpp;
+	struct var *vp;
+
+	INTOFF;
+	lvp = ckmalloc(sizeof (struct localvar));
+	if (name[0] == '-' && name[1] == '\0') {
+		char *p;
+		p = ckmalloc(sizeof_optlist);
+		lvp->text = memcpy(p, optlist, sizeof_optlist);
+		vp = NULL;
+	} else {
+		vp = find_var(name, &vpp, NULL);
+		if (vp == NULL) {
+			if (strchr(name, '='))
+				setvareq(savestr(name), VSTRFIXED|flags);
+			else
+				setvar(name, NULL, VSTRFIXED|flags);
+			vp = *vpp;	/* the new variable */
+			lvp->text = NULL;
+			lvp->flags = VUNSET;
+		} else {
+			lvp->text = vp->text;
+			lvp->flags = vp->flags;
+			vp->flags |= VSTRFIXED|VTEXTFIXED;
+			if (name[vp->name_len] == '=')
+				setvareq(savestr(name), flags);
+		}
+	}
+	lvp->vp = vp;
+	lvp->next = localvars;
+	localvars = lvp;
+	INTON;
+}
+
+
+/*
+ * Called after a function returns.
+ */
+
+void
+poplocalvars(void)
+{
+	struct localvar *lvp;
+	struct var *vp;
+
+	while ((lvp = localvars) != NULL) {
+		localvars = lvp->next;
+		vp = lvp->vp;
+		TRACE(("poplocalvar %s", vp ? vp->text : "-"));
+		if (vp == NULL) {	/* $- saved */
+			memcpy(optlist, lvp->text, sizeof_optlist);
+			ckfree(lvp->text);
+		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+			(void)unsetvar(vp->text, 0);
+		} else {
+			if (vp->func && (vp->flags & VNOFUNC) == 0)
+				(*vp->func)(lvp->text + vp->name_len + 1);
+			if ((vp->flags & VTEXTFIXED) == 0)
+				ckfree(vp->text);
+			vp->flags = lvp->flags;
+			vp->text = lvp->text;
+		}
+		ckfree(lvp);
+	}
+}
+
+
+int
+setvarcmd(int argc, char **argv)
+{
+	if (argc <= 2)
+		return unsetcmd(argc, argv);
+	else if (argc == 3)
+		setvar(argv[1], argv[2], 0);
+	else
+		error("List assignment not implemented");
+	return 0;
+}
+
+
+/*
+ * The unset builtin command.  We unset the function before we unset the
+ * variable to allow a function to be unset when there is a readonly variable
+ * with the same name.
+ */
+
+int
+unsetcmd(int argc, char **argv)
+{
+	char **ap;
+	int i;
+	int flg_func = 0;
+	int flg_var = 0;
+	int ret = 0;
+
+	while ((i = nextopt("evf")) != '\0') {
+		if (i == 'f')
+			flg_func = 1;
+		else
+			flg_var = i;
+	}
+	if (flg_func == 0 && flg_var == 0)
+		flg_var = 1;
+
+	for (ap = argptr; *ap ; ap++) {
+		if (flg_func)
+			ret |= unsetfunc(*ap);
+		if (flg_var)
+			ret |= unsetvar(*ap, flg_var == 'e');
+	}
+	return ret;
+}
+
+
+/*
+ * Unset the specified variable.
+ */
+
+int
+unsetvar(const char *s, int unexport)
+{
+	struct var **vpp;
+	struct var *vp;
+
+	vp = find_var(s, &vpp, NULL);
+	if (vp == NULL)
+		return 1;
+
+	if (vp->flags & VREADONLY)
+		return (1);
+
+	INTOFF;
+	if (unexport) {
+		vp->flags &= ~VEXPORT;
+	} else {
+		if (vp->text[vp->name_len + 1] != '\0')
+			setvar(s, nullstr, 0);
+		vp->flags &= ~VEXPORT;
+		vp->flags |= VUNSET;
+		if ((vp->flags & VSTRFIXED) == 0) {
+			if ((vp->flags & VTEXTFIXED) == 0)
+				ckfree(vp->text);
+			*vpp = vp->next;
+			ckfree(vp);
+		}
+	}
+	INTON;
+	return 0;
+}
+
+
+/*
+ * Returns true if the two strings specify the same varable.  The first
+ * variable name is terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
+
+STATIC int
+strequal(const char *p, const char *q)
+{
+	while (*p == *q++) {
+		if (*p++ == '=')
+			return 1;
+	}
+	if (*p == '=' && *(q - 1) == '\0')
+		return 1;
+	return 0;
+}
+
+/*
+ * Search for a variable.
+ * 'name' may be terminated by '=' or a NUL.
+ * vppp is set to the pointer to vp, or the list head if vp isn't found
+ * lenp is set to the number of charactets in 'name'
+ */
+
+STATIC struct var *
+find_var(const char *name, struct var ***vppp, int *lenp)
+{
+	unsigned int hashval;
+	int len;
+	struct var *vp, **vpp;
+	const char *p = name;
+
+	hashval = 0;
+	while (*p && *p != '=')
+		hashval = 2 * hashval + (unsigned char)*p++;
+	len = p - name;
+
+	if (lenp)
+		*lenp = len;
+	vpp = &vartab[hashval % VTABSIZE];
+	if (vppp)
+		*vppp = vpp;
+
+	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
+		if (vp->name_len != len)
+			continue;
+		if (memcmp(vp->text, name, len) != 0)
+			continue;
+		if (vppp)
+			*vppp = vpp;
+		return vp;
+	}
+	return NULL;
+}
diff --git a/sh/var.h b/sh/var.h
new file mode 100644
index 0000000..b7b7db8
--- /dev/null
+++ b/sh/var.h
@@ -0,0 +1,131 @@
+/*	$NetBSD: var.h,v 1.23 2004/10/02 12:16:53 dsl Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)var.h	8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * Shell variables.
+ */
+
+/* flags */
+#define VEXPORT		0x01	/* variable is exported */
+#define VREADONLY	0x02	/* variable cannot be modified */
+#define VSTRFIXED	0x04	/* variable struct is statically allocated */
+#define VTEXTFIXED	0x08	/* text is statically allocated */
+#define VSTACK		0x10	/* text is allocated on the stack */
+#define VUNSET		0x20	/* the variable is not set */
+#define VNOFUNC		0x40	/* don't call the callback function */
+#define VNOSET		0x80	/* do not set variable - just readonly test */
+
+
+struct var {
+	struct var *next;		/* next entry in hash list */
+	int flags;			/* flags are defined above */
+	char *text;			/* name=value */
+	int name_len;			/* length of name */
+	void (*func)(const char *);
+					/* function to be called when  */
+					/* the variable gets set/unset */
+};
+
+
+struct localvar {
+	struct localvar *next;		/* next local variable in list */
+	struct var *vp;			/* the variable that was made local */
+	int flags;			/* saved flags */
+	char *text;			/* saved text */
+};
+
+
+struct localvar *localvars;
+
+#if ATTY
+extern struct var vatty;
+#endif
+extern struct var vifs;
+extern struct var vmpath;
+extern struct var vpath;
+extern struct var vps1;
+extern struct var vps2;
+extern struct var vps4;
+#ifdef WITH_HISTORY 
+extern struct var vterm;
+extern struct var vtermcap;
+extern struct var vhistsize;
+#endif
+
+/*
+ * The following macros access the values of the above variables.
+ * They have to skip over the name.  They return the null string
+ * for unset variables.
+ */
+
+#define ifsval()	(vifs.text + 4)
+#define ifsset()	((vifs.flags & VUNSET) == 0)
+#define mpathval()	(vmpath.text + 9)
+#define pathval()	(vpath.text + 5)
+#define ps1val()	(vps1.text + 4)
+#define ps2val()	(vps2.text + 4)
+#define ps4val()	(vps4.text + 4)
+#define optindval()	(voptind.text + 7)
+#ifdef WITH_HISTORY
+#define histsizeval()	(vhistsize.text + 9)
+#define termval()	(vterm.text + 5)
+#endif
+
+#if ATTY
+#define attyset()	((vatty.flags & VUNSET) == 0)
+#endif
+#define mpathset()	((vmpath.flags & VUNSET) == 0)
+
+void initvar(void);
+void setvar(const char *, const char *, int);
+void setvareq(char *, int);
+struct strlist;
+void listsetvar(struct strlist *, int);
+char *lookupvar(const char *);
+char *bltinlookup(const char *, int);
+char **environment(void);
+void shprocvar(void);
+int showvars(const char *, int, int);
+int exportcmd(int, char **);
+int localcmd(int, char **);
+void mklocal(const char *, int);
+void listmklocal(struct strlist *, int);
+void poplocalvars(void);
+int setvarcmd(int, char **);
+int unsetcmd(int, char **);
+int unsetvar(const char *, int);
+int setvarsafe(const char *, const char *, int);
+void print_quoted(const char *);
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
new file mode 100644
index 0000000..5a8dc0b
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,91 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+TOOLS := \
+	ls \
+	mount \
+	cat \
+	ps \
+	kill \
+	ln \
+	insmod \
+	rmmod \
+	lsmod \
+	ifconfig \
+	setconsole \
+	rm \
+	mkdir \
+	rmdir \
+	reboot \
+	getevent \
+	sendevent \
+	date \
+	wipe \
+	sync \
+	umount \
+	start \
+	stop \
+	notify \
+	cmp \
+	dmesg \
+	route \
+	hd \
+	dd \
+	df \
+	getprop \
+	setprop \
+	watchprops \
+	log \
+	sleep \
+	renice \
+	printenv \
+	smd \
+	chmod \
+    chown \
+	mkdosfs \
+	netstat \
+	ioctl \
+	mv \
+	schedtop \
+	top \
+	iftop \
+	id \
+	vmstat
+
+LOCAL_SRC_FILES:= \
+	toolbox.c \
+	$(patsubst %,%.c,$(TOOLS))
+
+LOCAL_SHARED_LIBRARIES := libcutils libc
+
+LOCAL_MODULE:= toolbox
+
+# Including this will define $(intermediates).
+#
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+	$(transform-generated-source)
+
+# Make #!/system/bin/toolbox launchers for each tool.
+#
+SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS))
+$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE)
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(TOOLBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(TOOLBOX_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+    $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS)
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/MODULE_LICENSE_BSD
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
new file mode 100644
index 0000000..12f28b9
--- /dev/null
+++ b/toolbox/NOTICE
@@ -0,0 +1,131 @@
+
+Copyright (c) 2008, The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the 
+   distribution.
+ * Neither the name of The Android Open Source Project nor the names
+   of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1998 Robert Nordier
+Copyright (c) 1989, 1993
+     The Regents of the University of California.  All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kevin Fall.
+This code is derived from software contributed to Berkeley by
+Keith Muller of the University of California, San Diego and Lance
+Visser of Convex Computer Corporation.
+This code is derived from software contributed to Berkeley by
+Mike Muuss.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+ Copyright (c) 1989, 1993
+	The Regents of the University of California.  All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Kevin Fall.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+
+ Copyright (c) 1991, 1993, 1994
+	The Regents of the University of California.  All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Keith Muller of the University of California, San Diego and Lance
+ Visser of Convex Computer Corporation.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
diff --git a/toolbox/alarm.c b/toolbox/alarm.c
new file mode 100644
index 0000000..9bd58aa
--- /dev/null
+++ b/toolbox/alarm.c
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <asm/ioctl.h>
+//#include <linux/rtc.h>
+#include <linux/android_alarm.h>
+
+int alarm_main(int argc, char *argv[])
+{
+	int c;
+    int res;
+	struct tm tm;
+	time_t t;
+	struct timespec ts;
+//	struct rtc_time rtc_time;
+	char strbuf[26];
+	int afd;
+	int nfd;
+//	struct timeval timeout = { 0, 0 };
+    int wait = 0;
+	fd_set rfds;
+	const char wake_lock_id[] = "alarm_test"; 
+	int waitalarmmask = 0;
+
+    int useutc = 0;
+	android_alarm_type_t alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+	android_alarm_type_t alarmtype_high = ANDROID_ALARM_RTC_WAKEUP;
+	android_alarm_type_t alarmtype = 0;
+
+    do {
+        //c = getopt(argc, argv, "uw:");
+        c = getopt(argc, argv, "uwat:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'u':
+            useutc = 1;
+            break;
+		case 't':
+			alarmtype_low = alarmtype_high = strtol(optarg, NULL, 0);
+			break;
+		case 'a':
+			alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+			alarmtype_high = ANDROID_ALARM_TYPE_COUNT - 1;
+			break;
+        case 'w':
+            //timeout.tv_sec = strtol(optarg, NULL, 0);
+            wait = 1;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(optind + 2 < argc) {
+        fprintf(stderr,"%s [-uwa] [-t type] [seconds]\n", argv[0]);
+        return 1;
+    }
+
+    afd = open("/dev/alarm", O_RDWR);
+    if(afd < 0) {
+        fprintf(stderr, "Unable to open rtc: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if(optind == argc) {
+		for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+			waitalarmmask |= 1U << alarmtype;
+		}
+#if 0
+        res = ioctl(fd, RTC_ALM_READ, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to read alarm: %s\n", strerror(errno));
+			return 1;
+        }
+#endif
+#if 0
+		t = timegm(&tm);
+        if(useutc)
+            gmtime_r(&t, &tm);
+        else
+            localtime_r(&t, &tm);
+#endif
+#if 0
+        asctime_r(&tm, strbuf);
+        printf("%s", strbuf);
+#endif
+    }
+    else if(optind + 1 == argc) {
+#if 0
+        res = ioctl(fd, RTC_RD_TIME, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+			return 1;
+        }
+        asctime_r(&tm, strbuf);
+        printf("Now: %s", strbuf);
+        time(&tv.tv_sec);
+#endif
+#if 0
+		time(&ts.tv_sec);
+		ts.tv_nsec = 0;
+		
+        //strptime(argv[optind], NULL, &tm);
+        //tv.tv_sec = mktime(&tm);
+        //tv.tv_usec = 0;
+#endif
+		for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+			waitalarmmask |= 1U << alarmtype;
+		    res = ioctl(afd, ANDROID_ALARM_GET_TIME(alarmtype), &ts);
+		    if(res < 0) {
+		        fprintf(stderr, "Unable to get current time: %s\n", strerror(errno));
+				return 1;
+		    }
+		    ts.tv_sec += strtol(argv[optind], NULL, 0);
+		    //strtotimeval(argv[optind], &tv);
+			gmtime_r(&ts.tv_sec, &tm);
+		    printf("time %s -> %ld.%09ld\n", argv[optind], ts.tv_sec, ts.tv_nsec);
+		    asctime_r(&tm, strbuf);
+		    printf("Requested %s", strbuf);
+			
+		    res = ioctl(afd, ANDROID_ALARM_SET(alarmtype), &ts);
+		    if(res < 0) {
+		        fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+				return 1;
+		    }
+		}
+#if 0
+        res = ioctl(fd, RTC_ALM_SET, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+			return 1;
+        }
+        res = ioctl(fd, RTC_AIE_ON);
+        if(res < 0) {
+            fprintf(stderr, "Unable to enable alarm: %s\n", strerror(errno));
+			return 1;
+        }
+#endif
+    }
+    else {
+        fprintf(stderr,"%s [-u] [date]\n", argv[0]);
+        return 1;
+    }
+
+	if(wait) {
+		while(waitalarmmask) {
+			printf("wait for alarm %x\n", waitalarmmask);
+			res = ioctl(afd, ANDROID_ALARM_WAIT);
+			if(res < 0) {
+				fprintf(stderr, "alarm wait failed\n");
+			}
+			printf("got alarm %x\n", res);
+			waitalarmmask &= ~res;
+			nfd = open("/sys/android_power/acquire_full_wake_lock", O_RDWR);
+			write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+			close(nfd);
+			//sleep(5);
+			nfd = open("/sys/android_power/release_wake_lock", O_RDWR);
+			write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+			close(nfd);
+		}
+		printf("done\n");
+	}
+#if 0	
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	res = select(fd + 1, &rfds, NULL, NULL, &timeout);
+    if(res < 0) {
+        fprintf(stderr, "select failed: %s\n", strerror(errno));
+		return 1;
+    }
+	if(res > 0) {
+		int event;
+		read(fd, &event, sizeof(event));
+		fprintf(stderr, "got %x\n", event);
+	}
+	else {
+		fprintf(stderr, "timeout waiting for alarm\n");
+	}
+#endif
+
+    close(afd);
+
+    return 0;
+}
diff --git a/toolbox/cat.c b/toolbox/cat.c
new file mode 100644
index 0000000..6ac31f8
--- /dev/null
+++ b/toolbox/cat.c
@@ -0,0 +1,291 @@
+/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $	*/
+
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CAT_BUFSIZ (4096)
+
+static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
+static int rval;
+static const char *filename;
+
+static void
+cook_buf(FILE *fp)
+{
+	int ch, gobble, line, prev;
+	int stdout_err = 0;
+
+	line = gobble = 0;
+	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+		if (prev == '\n') {
+			if (ch == '\n') {
+				if (sflag) {
+					if (!gobble && putchar(ch) == EOF)
+						break;
+					gobble = 1;
+					continue;
+				}
+				if (nflag) {
+					if (!bflag) {
+						if (fprintf(stdout,
+						    "%6d\t", ++line) < 0) {
+							stdout_err++;
+							break;
+						}
+					} else if (eflag) {
+						if (fprintf(stdout,
+						    "%6s\t", "") < 0) {
+							stdout_err++;
+							break;
+						}
+					}
+				}
+			} else if (nflag) {
+				if (fprintf(stdout, "%6d\t", ++line) < 0) {
+					stdout_err++;
+					break;
+				}
+			}
+		}
+		gobble = 0;
+		if (ch == '\n') {
+			if (eflag)
+				if (putchar('$') == EOF)
+					break;
+		} else if (ch == '\t') {
+			if (tflag) {
+				if (putchar('^') == EOF || putchar('I') == EOF)
+					break;
+				continue;
+			}
+		} else if (vflag) {
+			if (!isascii(ch)) {
+				if (putchar('M') == EOF || putchar('-') == EOF)
+					break;
+				ch = (ch) & 0x7f;
+			}
+			if (iscntrl(ch)) {
+				if (putchar('^') == EOF ||
+				    putchar(ch == '\177' ? '?' :
+				    ch | 0100) == EOF)
+					break;
+				continue;
+			}
+		}
+		if (putchar(ch) == EOF)
+			break;
+	}
+	if (stdout_err) {
+		perror(filename);
+		rval = 1;
+	}
+}
+
+static void
+cook_args(char **argv)
+{
+	FILE *fp;
+
+	fp = stdin;
+	filename = "stdin";
+	do {
+		if (*argv) {
+			if (!strcmp(*argv, "-"))
+				fp = stdin;
+			else if ((fp = fopen(*argv,
+			    fflag ? "rf" : "r")) == NULL) {
+				perror("fopen");
+				rval = 1;
+				++argv;
+				continue;
+			}
+			filename = *argv++;
+		}
+		cook_buf(fp);
+		if (fp != stdin)
+			fclose(fp);
+	} while (*argv);
+}
+
+static void
+raw_cat(int rfd)
+{
+	static char *buf;
+	static char fb_buf[CAT_BUFSIZ];
+	static size_t bsize;
+
+	struct stat sbuf;
+	ssize_t nr, nw, off;
+	int wfd;
+
+	wfd = fileno(stdout);
+	if (buf == NULL) {
+		if (fstat(wfd, &sbuf) == 0) {
+			bsize = sbuf.st_blksize > CAT_BUFSIZ ?
+			    sbuf.st_blksize : CAT_BUFSIZ;
+			buf = malloc(bsize);
+		}
+		if (buf == NULL) {
+			buf = fb_buf;
+			bsize = CAT_BUFSIZ;
+		}
+	}
+	while ((nr = read(rfd, buf, bsize)) > 0)
+		for (off = 0; nr; nr -= nw, off += nw)
+			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
+			{
+				perror("write");
+				exit(EXIT_FAILURE);
+			}
+	if (nr < 0) {
+		fprintf(stderr,"%s: invalid length\n", filename);
+		rval = 1;
+	}
+}
+
+static void
+raw_args(char **argv)
+{
+	int fd;
+
+	fd = fileno(stdin);
+	filename = "stdin";
+	do {
+		if (*argv) {
+			if (!strcmp(*argv, "-"))
+				fd = fileno(stdin);
+			else if (fflag) {
+				struct stat st;
+				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
+				if (fd < 0)
+					goto skip;
+
+				if (fstat(fd, &st) == -1) {
+					close(fd);
+					goto skip;
+				}
+				if (!S_ISREG(st.st_mode)) {
+					close(fd);
+					errno = EINVAL;
+					goto skipnomsg;
+				}
+			}
+			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+skip:
+				perror(*argv);
+skipnomsg:
+				rval = 1;
+				++argv;
+				continue;
+			}
+			filename = *argv++;
+		}
+		raw_cat(fd);
+		if (fd != fileno(stdin))
+			close(fd);
+	} while (*argv);
+}
+
+int
+cat_main(int argc, char *argv[])
+{
+	int ch;
+	struct flock stdout_lock;
+
+	while ((ch = getopt(argc, argv, "beflnstv")) != -1)
+		switch (ch) {
+		case 'b':
+			bflag = nflag = 1;	/* -b implies -n */
+			break;
+		case 'e':
+			eflag = vflag = 1;	/* -e implies -v */
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 's':
+			sflag = 1;
+			break;
+		case 't':
+			tflag = vflag = 1;	/* -t implies -v */
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		default:
+		case '?':
+			fprintf(stderr,
+				"usage: cat [-beflnstv] [-] [file ...]\n");
+			exit(EXIT_FAILURE);
+		}
+	argv += optind;
+
+	if (lflag) {
+		stdout_lock.l_len = 0;
+		stdout_lock.l_start = 0;
+		stdout_lock.l_type = F_WRLCK;
+		stdout_lock.l_whence = SEEK_SET;
+		if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
+		{
+			perror("fcntl");
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (bflag || eflag || nflag || sflag || tflag || vflag)
+		cook_args(argv);
+	else
+		raw_args(argv);
+	if (fclose(stdout))
+	{
+		perror("fclose");
+		exit(EXIT_FAILURE);
+	}
+	exit(rval);
+}
diff --git a/toolbox/chmod.c b/toolbox/chmod.c
new file mode 100644
index 0000000..31a53bf
--- /dev/null
+++ b/toolbox/chmod.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <time.h>
+
+int chmod_main(int argc, char **argv)
+{
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr, "Usage: chmod <MODE> <FILE>\n");
+        return 10;
+    }
+
+    int mode = 0;
+    const char* s = argv[1];
+    while (*s) {
+        if (*s >= '0' && *s <= '7') {
+            mode = (mode<<3) | (*s-'0');
+        }
+        else {
+            fprintf(stderr, "Bad mode\n");
+            return 10;
+        }
+        s++;
+    }
+    for (i = 2; i < argc; i++) {
+        if (chmod(argv[i], mode) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
+            return 10;
+        }
+    }
+    return 0;
+}
+
diff --git a/toolbox/chown.c b/toolbox/chown.c
new file mode 100644
index 0000000..13617db
--- /dev/null
+++ b/toolbox/chown.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <unistd.h>
+#include <time.h>
+
+int chown_main(int argc, char **argv)
+{
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr, "Usage: chown <USER>[.GROUP] <FILE1> [FILE2] ...\n");
+        return 10;
+    }
+
+    // Copy argv[1] to 'user' so we can truncate it at the period
+    // if a group id specified.
+    char user[32];
+    char *group = NULL;
+    strncpy(user, argv[1], sizeof(user));
+    if ((group = strchr(user, '.')) != NULL) {
+        *group++ = '\0';
+    }
+
+    // Lookup uid (and gid if specified)
+    struct passwd *pw;
+    struct group *grp = NULL;
+    uid_t uid;
+    gid_t gid = -1; // passing -1 to chown preserves current group
+
+    pw = getpwnam(user);
+    if (pw == NULL) {
+        fprintf(stderr, "No such user '%s'\n", user);
+        return 10;
+    }
+    uid = pw->pw_uid;
+
+    if (group != NULL) {
+        grp = getgrnam(group);
+        if (grp == NULL) {
+            fprintf(stderr, "No such group '%s'\n", group);
+            return 10;
+        }
+        gid = grp->gr_gid; 
+    }
+
+    for (i = 2; i < argc; i++) {
+        if (chown(argv[i], uid, gid) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
+            return 10;
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/cmp.c b/toolbox/cmp.c
new file mode 100644
index 0000000..9bd2e19
--- /dev/null
+++ b/toolbox/cmp.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int cmp_main(int argc, char *argv[])
+{
+    int c;
+    int fd1, fd2;
+	char buf1[4096], buf2[4096];
+    int res, res1, res2;
+	int rv = 0;
+	int i;
+	int filepos = 0;
+
+	int show_byte = 0;
+	int show_all = 0;
+	int limit = 0;
+
+    do {
+        c = getopt(argc, argv, "bln:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'b':
+            show_byte = 1;
+            break;
+        case 'l':
+            show_all = 1;
+            break;
+        case 'n':
+            limit = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind + 2 != argc) {
+        fprintf(stderr, "Usage: %s [-b] [-l] [-n count] file1 file2\n", argv[0]);
+        exit(1);
+    }
+
+    fd1 = open(argv[optind], O_RDONLY);
+    if(fd1 < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+
+    fd2 = open(argv[optind+1], O_RDONLY);
+    if(fd2 < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind+1], strerror(errno));
+        return 1;
+    }
+    
+    while(1) {
+        res1 = read(fd1, &buf1, sizeof(buf1));
+        res2 = read(fd2, &buf2, sizeof(buf2));
+		res = res1 < res2 ? res1 : res2;
+		if(res1 == 0 && res2 == 0) {
+			return rv;
+		}
+		for(i = 0; i < res; i++) {
+			if(buf1[i] != buf2[i]) {
+				printf("%s %s differ byte %d", argv[optind], argv[optind+1], filepos + i);
+				if(show_byte)
+					printf(" 0x%02x 0x%02x", buf1[i], buf2[i]);
+				printf("\n");
+				if(!show_all)
+					return 1;
+				rv = 1;
+			}
+			if(limit) {
+				limit--;
+				if(limit == 0)
+					return rv;
+			}
+		}
+		if(res1 != res2 || res < 0) {
+			printf("%s on %s\n", res < 0 ? "Read error" : "EOF", res1 < res2 ? argv[optind] : argv[optind+1]);
+			return 1;
+		}
+		filepos += res;
+    }
+}
diff --git a/toolbox/date.c b/toolbox/date.c
new file mode 100644
index 0000000..13b5210
--- /dev/null
+++ b/toolbox/date.c
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <linux/android_alarm.h>
+
+static void settime(char *s) {
+    struct tm tm;
+    int day = atoi(s);
+    int hour;
+    time_t t;
+    int fd;
+    struct timespec ts;
+
+    while (*s && *s != '.')
+        s++;
+
+    if (*s)
+        s++;
+
+    hour = atoi(s);
+
+    tm.tm_year = day / 10000 - 1900;
+    tm.tm_mon = (day % 10000) / 100 - 1;
+    tm.tm_mday = (day % 100);
+    tm.tm_hour = hour / 10000;
+    tm.tm_min = (hour % 10000) / 100;
+    tm.tm_sec = (hour % 100);
+    tm.tm_isdst = -1;
+
+    t = mktime(&tm);
+    
+    fd = open("/dev/alarm", O_RDWR);
+    ts.tv_sec = t;
+    ts.tv_nsec = 0;
+    ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+}
+
+int date_main(int argc, char *argv[])
+{
+	int c;
+    int res;
+	struct tm tm;
+	time_t t;
+	struct timeval tv;
+    struct timespec ts;
+	char strbuf[260];
+    int fd;
+
+    int useutc = 0;
+
+    tzset();
+
+    do {
+        c = getopt(argc, argv, "us:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'u':
+            useutc = 1;
+            break;
+        case 's':
+            settime(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(optind + 2 < argc) {
+        fprintf(stderr,"%s [-u] [date]\n", argv[0]);
+        return 1;
+    }
+
+    int hasfmt = argc == optind + 1 && argv[optind][0] == '+';
+    if(optind == argc || hasfmt) {
+        char buf[2000];
+        time(&t);
+        if (useutc) {
+            gmtime_r(&t, &tm);
+            strftime(strbuf, sizeof(strbuf),
+                     (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S GMT %Y"),
+                     &tm);
+        } else {
+            localtime_r(&t, &tm);
+            strftime(strbuf, sizeof(strbuf),
+                     (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S %Z %Y"),
+                     &tm);
+        }
+        printf("%s\n", strbuf);
+    }
+    else if(optind + 1 == argc) {
+#if 0
+        struct tm *tmptr;
+        tmptr = getdate(argv[optind]);
+        if(tmptr == NULL) {
+            fprintf(stderr,"getdate_r failed\n");
+            return 1;
+        }
+        tm = *tmptr;
+#if 0
+        if(getdate_r(argv[optind], &tm) < 0) {
+            fprintf(stderr,"getdate_r failed %s\n", strerror(errno));
+            return 1;
+        }
+#endif
+#endif
+        //strptime(argv[optind], NULL, &tm);
+        //tv.tv_sec = mktime(&tm);
+        //tv.tv_usec = 0;
+        strtotimeval(argv[optind], &tv);
+        printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec);
+        fd = open("/dev/alarm", O_RDWR);
+        ts.tv_sec = tv.tv_sec;
+        ts.tv_nsec = tv.tv_usec * 1000;
+        res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+        //res = settimeofday(&tv, NULL);
+        if(res < 0) {
+            fprintf(stderr,"settimeofday failed %s\n", strerror(errno));
+            return 1;
+        }
+    }
+    else {
+        fprintf(stderr,"%s [-s 20070325.123456] [-u] [date]\n", argv[0]);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/dd.c b/toolbox/dd.c
new file mode 100644
index 0000000..c6af3ea
--- /dev/null
+++ b/toolbox/dd.c
@@ -0,0 +1,1358 @@
+/*	$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\
+	The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dd.c	8.5 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+
+#define NO_CONV
+
+//#include "extern.h"
+void block(void);
+void block_close(void);
+void dd_out(int);
+void def(void);
+void def_close(void);
+void jcl(char **);
+void pos_in(void);
+void pos_out(void);
+void summary(void);
+void summaryx(int);
+void terminate(int);
+void unblock(void);
+void unblock_close(void);
+ssize_t bwrite(int, const void *, size_t);
+
+extern IO		in, out;
+extern STAT		st;
+extern void		(*cfunc)(void);
+extern uint64_t		cpy_cnt;
+extern uint64_t		cbsz;
+extern u_int		ddflags;
+extern u_int		files_cnt;
+extern int		progress;
+extern const u_char	*ctab;
+extern const u_char	a2e_32V[], a2e_POSIX[];
+extern const u_char	e2a_32V[], e2a_POSIX[];
+extern const u_char	a2ibm_32V[], a2ibm_POSIX[];
+extern u_char		casetab[];
+
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static void dd_close(void);
+static void dd_in(void);
+static void getfdtype(IO *);
+static int redup_clean_fd(int);
+static void setup(void);
+
+
+IO		in, out;		/* input/output state */
+STAT		st;			/* statistics */
+void		(*cfunc)(void);		/* conversion function */
+uint64_t	cpy_cnt;		/* # of blocks to copy */
+static off_t	pending = 0;		/* pending seek if sparse */
+u_int		ddflags;		/* conversion options */
+uint64_t	cbsz;			/* conversion block size */
+u_int		files_cnt = 1;		/* # of files to copy */
+int		progress = 0;		/* display sign of life */
+const u_char	*ctab;			/* conversion table */
+sigset_t	infoset;		/* a set blocking SIGINFO */
+
+int
+dd_main(int argc, char *argv[])
+{
+	int ch;
+
+	while ((ch = getopt(argc, argv, "")) != -1) {
+		switch (ch) {
+		default:
+			fprintf(stderr, "usage: dd [operand ...]\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+	}
+	argc -= (optind - 1);
+	argv += (optind - 1);
+
+	jcl(argv);
+	setup();
+
+//	(void)signal(SIGINFO, summaryx);
+	(void)signal(SIGINT, terminate);
+	(void)sigemptyset(&infoset);
+//	(void)sigaddset(&infoset, SIGINFO);
+
+	(void)atexit(summary);
+
+	while (files_cnt--)
+		dd_in();
+
+	dd_close();
+	exit(0);
+	/* NOTREACHED */
+}
+
+static void
+setup(void)
+{
+
+	if (in.name == NULL) {
+		in.name = "stdin";
+		in.fd = STDIN_FILENO;
+	} else {
+		in.fd = open(in.name, O_RDONLY, 0);
+		if (in.fd < 0) {
+			fprintf(stderr, "%s: cannot open for read: %s\n",
+				in.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/* Ensure in.fd is outside the stdio descriptor range */
+		in.fd = redup_clean_fd(in.fd);
+	}
+
+	getfdtype(&in);
+
+	if (files_cnt > 1 && !(in.flags & ISTAPE)) {
+		fprintf(stderr,
+			"files is not supported for non-tape devices\n");
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	if (out.name == NULL) {
+		/* No way to check for read access here. */
+		out.fd = STDOUT_FILENO;
+		out.name = "stdout";
+	} else {
+#define	OFLAGS \
+    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+		out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */);
+		/*
+		 * May not have read access, so try again with write only.
+		 * Without read we may have a problem if output also does
+		 * not support seeks.
+		 */
+		if (out.fd < 0) {
+			out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */);
+			out.flags |= NOREAD;
+		}
+		if (out.fd < 0) {
+			fprintf(stderr, "%s: cannot open for write: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/* Ensure out.fd is outside the stdio descriptor range */
+		out.fd = redup_clean_fd(out.fd);
+	}
+
+	getfdtype(&out);
+
+	/*
+	 * Allocate space for the input and output buffers.  If not doing
+	 * record oriented I/O, only need a single buffer.
+	 */
+	if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) {
+			exit(1);
+			/* NOTREACHED */
+		}
+		out.db = in.db;
+	} else if ((in.db =
+	    malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+	    (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
+		exit(1);
+		/* NOTREACHED */
+	}
+	in.dbp = in.db;
+	out.dbp = out.db;
+
+	/* Position the input/output streams. */
+	if (in.offset)
+		pos_in();
+	if (out.offset)
+		pos_out();
+
+	/*
+	 * Truncate the output file; ignore errors because it fails on some
+	 * kinds of output files, tapes, for example.
+	 */
+	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+		(void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+
+	/*
+	 * If converting case at the same time as another conversion, build a
+	 * table that does both at once.  If just converting case, use the
+	 * built-in tables.
+	 */
+	if (ddflags & (C_LCASE|C_UCASE)) {
+#ifdef	NO_CONV
+		/* Should not get here, but just in case... */
+		fprintf(stderr, "case conv and -DNO_CONV\n");
+		exit(1);
+		/* NOTREACHED */
+#else	/* NO_CONV */
+		u_int cnt;
+
+		if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
+			if (ddflags & C_LCASE) {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = tolower(ctab[cnt]);
+			} else {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = toupper(ctab[cnt]);
+			}
+		} else {
+			if (ddflags & C_LCASE) {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = tolower(cnt);
+			} else {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = toupper(cnt);
+			}
+		}
+
+		ctab = casetab;
+#endif	/* NO_CONV */
+	}
+
+	(void)gettimeofday(&st.start, NULL);	/* Statistics timestamp. */
+}
+
+static void
+getfdtype(IO *io)
+{
+//	struct mtget mt;
+	struct stat sb;
+
+	if (fstat(io->fd, &sb)) {
+		fprintf(stderr, "%s: cannot fstat: %s\n",
+			io->name, strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+	if (S_ISCHR(sb.st_mode))
+		io->flags |= /*ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; */ ISCHR;
+	else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+		io->flags |= ISPIPE;		/* XXX fixed in 4.4BSD */
+}
+
+/*
+ * Move the parameter file descriptor to a descriptor that is outside the
+ * stdio descriptor range, if necessary.  This is required to avoid
+ * accidentally outputting completion or error messages into the
+ * output file that were intended for the tty.
+ */
+static int
+redup_clean_fd(int fd)
+{
+	int newfd;
+
+	if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
+	    fd != STDERR_FILENO)
+		/* File descriptor is ok, return immediately. */
+		return fd;
+
+	/*
+	 * 3 is the first descriptor greater than STD*_FILENO.  Any
+	 * free descriptor valued 3 or above is acceptable...
+	 */
+	newfd = fcntl(fd, F_DUPFD, 3);
+	if (newfd < 0) {
+		fprintf(stderr, "dupfd IO: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	close(fd);
+
+	return newfd;
+}
+
+static void
+dd_in(void)
+{
+	int flags;
+	int64_t n;
+
+	for (flags = ddflags;;) {
+		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+			return;
+
+		/*
+		 * Clear the buffer first if doing "sync" on input.
+		 * If doing block operations use spaces.  This will
+		 * affect not only the C_NOERROR case, but also the
+		 * last partial input block which should be padded
+		 * with zero and not garbage.
+		 */
+		if (flags & C_SYNC) {
+			if (flags & (C_BLOCK|C_UNBLOCK))
+				(void)memset(in.dbp, ' ', in.dbsz);
+			else
+				(void)memset(in.dbp, 0, in.dbsz);
+		}
+
+		n = read(in.fd, in.dbp, in.dbsz);
+		if (n == 0) {
+			in.dbrcnt = 0;
+			return;
+		}
+
+		/* Read error. */
+		if (n < 0) {
+
+			/*
+			 * If noerror not specified, die.  POSIX requires that
+			 * the warning message be followed by an I/O display.
+			 */
+			fprintf(stderr, "%s: read error: %s\n",
+				in.name, strerror(errno));
+			if (!(flags & C_NOERROR)) {
+				exit(1);
+				/* NOTREACHED */
+			}
+			summary();
+
+			/*
+			 * If it's not a tape drive or a pipe, seek past the
+			 * error.  If your OS doesn't do the right thing for
+			 * raw disks this section should be modified to re-read
+			 * in sector size chunks.
+			 */
+			if (!(in.flags & (ISPIPE|ISTAPE)) &&
+			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+				fprintf(stderr, "%s: seek error: %s\n",
+					in.name, strerror(errno));
+
+			/* If sync not specified, omit block and continue. */
+			if (!(ddflags & C_SYNC))
+				continue;
+
+			/* Read errors count as full blocks. */
+			in.dbcnt += in.dbrcnt = in.dbsz;
+			++st.in_full;
+
+		/* Handle full input blocks. */
+		} else if (n == in.dbsz) {
+			in.dbcnt += in.dbrcnt = n;
+			++st.in_full;
+
+		/* Handle partial input blocks. */
+		} else {
+			/* If sync, use the entire block. */
+			if (ddflags & C_SYNC)
+				in.dbcnt += in.dbrcnt = in.dbsz;
+			else
+				in.dbcnt += in.dbrcnt = n;
+			++st.in_part;
+		}
+
+		/*
+		 * POSIX states that if bs is set and no other conversions
+		 * than noerror, notrunc or sync are specified, the block
+		 * is output without buffering as it is read.
+		 */
+		if (ddflags & C_BS) {
+			out.dbcnt = in.dbcnt;
+			dd_out(1);
+			in.dbcnt = 0;
+			continue;
+		}
+
+/*		if (ddflags & C_SWAB) {
+			if ((n = in.dbrcnt) & 1) {
+				++st.swab;
+				--n;
+			}
+			swab(in.dbp, in.dbp, n);
+		}
+*/
+		in.dbp += in.dbrcnt;
+		(*cfunc)();
+	}
+}
+
+/*
+ * Cleanup any remaining I/O and flush output.  If necesssary, output file
+ * is truncated.
+ */
+static void
+dd_close(void)
+{
+
+	if (cfunc == def)
+		def_close();
+	else if (cfunc == block)
+		block_close();
+	else if (cfunc == unblock)
+		unblock_close();
+	if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+		(void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
+		out.dbcnt = out.dbsz;
+	}
+	/* If there are pending sparse blocks, make sure
+	 * to write out the final block un-sparse
+	 */
+	if ((out.dbcnt == 0) && pending) {
+		memset(out.db, 0, out.dbsz);
+		out.dbcnt = out.dbsz;
+		out.dbp = out.db + out.dbcnt;
+		pending -= out.dbsz;
+	}
+	if (out.dbcnt)
+		dd_out(1);
+
+	/*
+	 * Reporting nfs write error may be defered until next
+	 * write(2) or close(2) system call.  So, we need to do an
+	 * extra check.  If an output is stdout, the file structure
+	 * may be shared among with other processes and close(2) just
+	 * decreases the reference count.
+	 */
+	if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) {
+		fprintf(stderr, "fsync stdout: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+	if (close(out.fd) == -1) {
+		fprintf(stderr, "close: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+}
+
+void
+dd_out(int force)
+{
+	static int warned;
+	int64_t cnt, n, nw;
+	u_char *outp;
+
+	/*
+	 * Write one or more blocks out.  The common case is writing a full
+	 * output block in a single write; increment the full block stats.
+	 * Otherwise, we're into partial block writes.  If a partial write,
+	 * and it's a character device, just warn.  If a tape device, quit.
+	 *
+	 * The partial writes represent two cases.  1: Where the input block
+	 * was less than expected so the output block was less than expected.
+	 * 2: Where the input block was the right size but we were forced to
+	 * write the block in multiple chunks.  The original versions of dd(1)
+	 * never wrote a block in more than a single write, so the latter case
+	 * never happened.
+	 *
+	 * One special case is if we're forced to do the write -- in that case
+	 * we play games with the buffer size, and it's usually a partial write.
+	 */
+	outp = out.db;
+	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+		for (cnt = n;; cnt -= nw) {
+
+			if (!force && ddflags & C_SPARSE) {
+				int sparse, i;
+				sparse = 1;	/* Is buffer sparse? */
+				for (i = 0; i < cnt; i++)
+					if (outp[i] != 0) {
+						sparse = 0;
+						break;
+					}
+				if (sparse) {
+					pending += cnt;
+					outp += cnt;
+					nw = 0;
+					break;
+				}
+			}
+			if (pending != 0) {
+				if (lseek(out.fd, pending, SEEK_CUR) ==
+				    -1) {
+					fprintf(stderr,
+						"%s: seek error creating "
+						"sparse file: %s\n",
+						out.name, strerror(errno));
+					exit(1);
+				}
+			}
+			nw = bwrite(out.fd, outp, cnt);
+			if (nw <= 0) {
+				if (nw == 0) {
+					fprintf(stderr, "%s: end of device\n",
+						out.name);
+					exit(1);
+					/* NOTREACHED */
+				}
+				if (errno != EINTR) {
+					fprintf(stderr, "%s: write error: %s\n",
+						out.name, strerror(errno));
+					/* NOTREACHED */
+					exit(1);
+				}
+				nw = 0;
+			}
+			if (pending) {
+				st.bytes += pending;
+				st.sparse += pending/out.dbsz;
+				st.out_full += pending/out.dbsz;
+				pending = 0;
+			}
+			outp += nw;
+			st.bytes += nw;
+			if (nw == n) {
+				if (n != out.dbsz)
+					++st.out_part;
+				else
+					++st.out_full;
+				break;
+			}
+			++st.out_part;
+			if (nw == cnt)
+				break;
+			if (out.flags & ISCHR && !warned) {
+				warned = 1;
+				fprintf(stderr, "%s: short write on character "
+					"device\n", out.name);
+			}
+			if (out.flags & ISTAPE) {
+				fprintf(stderr,
+					"%s: short write on tape device",
+					out.name);
+				exit(1);
+				/* NOTREACHED */
+			}
+		}
+		if ((out.dbcnt -= n) < out.dbsz)
+			break;
+	}
+
+	/* Reassemble the output block. */
+	if (out.dbcnt)
+		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+	out.dbp = out.db + out.dbcnt;
+
+	if (progress)
+		(void)write(STDERR_FILENO, ".", 1);
+}
+
+/*
+ * A protected against SIGINFO write
+ */
+ssize_t
+bwrite(int fd, const void *buf, size_t len)
+{
+	sigset_t oset;
+	ssize_t rv;
+	int oerrno;
+
+	(void)sigprocmask(SIG_BLOCK, &infoset, &oset);
+	rv = write(fd, buf, len);
+	oerrno = errno;
+	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
+	errno = oerrno;
+	return (rv);
+}
+
+/*
+ * Position input/output data streams before starting the copy.  Device type
+ * dependent.  Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void
+pos_in(void)
+{
+	int bcnt, cnt, nr, warned;
+
+	/* If not a pipe or tape device, try to seek on it. */
+	if (!(in.flags & (ISPIPE|ISTAPE))) {
+		if (lseek(in.fd,
+		    (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+			fprintf(stderr, "%s: seek error: %s",
+				in.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+		return;
+		/* NOTREACHED */
+	}
+
+	/*
+	 * Read the data.  If a pipe, read until satisfy the number of bytes
+	 * being skipped.  No differentiation for reading complete and partial
+	 * blocks for other devices.
+	 */
+	for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+		if ((nr = read(in.fd, in.db, bcnt)) > 0) {
+			if (in.flags & ISPIPE) {
+				if (!(bcnt -= nr)) {
+					bcnt = in.dbsz;
+					--cnt;
+				}
+			} else
+				--cnt;
+			continue;
+		}
+
+		if (nr == 0) {
+			if (files_cnt > 1) {
+				--files_cnt;
+				continue;
+			}
+			fprintf(stderr, "skip reached end of input\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * Input error -- either EOF with no more files, or I/O error.
+		 * If noerror not set die.  POSIX requires that the warning
+		 * message be followed by an I/O display.
+		 */
+		if (ddflags & C_NOERROR) {
+			if (!warned) {
+
+				fprintf(stderr, "%s: error occurred\n",
+					in.name);
+				warned = 1;
+				summary();
+			}
+			continue;
+		}
+		fprintf(stderr, "%s: read error: %s", in.name, strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+}
+
+void
+pos_out(void)
+{
+//	struct mtop t_op;
+	int cnt, n;
+
+	/*
+	 * If not a tape, try seeking on the file.  Seeking on a pipe is
+	 * going to fail, but don't protect the user -- they shouldn't
+	 * have specified the seek operand.
+	 */
+	if (!(out.flags & ISTAPE)) {
+		if (lseek(out.fd,
+		    (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+			fprintf(stderr, "%s: seek error: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+		return;
+	}
+
+	/* If no read access, try using mtio. */
+	if (out.flags & NOREAD) {
+/*		t_op.mt_op = MTFSR;
+		t_op.mt_count = out.offset;
+
+		if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)*/
+			fprintf(stderr, "%s: cannot read", out.name);
+			exit(1);
+			/* NOTREACHED */
+		return;
+	}
+
+	/* Read it. */
+	for (cnt = 0; cnt < out.offset; ++cnt) {
+		if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+			continue;
+
+		if (n < 0) {
+			fprintf(stderr, "%s: cannot position by reading: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * If reach EOF, fill with NUL characters; first, back up over
+		 * the EOF mark.  Note, cnt has not yet been incremented, so
+		 * the EOF read does not count as a seek'd block.
+		 */
+/*		t_op.mt_op = MTBSR;
+		t_op.mt_count = 1;
+		if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) */ {
+			fprintf(stderr, "%s: cannot position\n", out.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		while (cnt++ < out.offset)
+			if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) {
+				fprintf(stderr, "%s: cannot position "
+					"by writing: %s\n",
+					out.name, strerror(errno));
+				exit(1);
+				/* NOTREACHED */
+			}
+		break;
+	}
+}
+
+/*
+ * def --
+ * Copy input to output.  Input is buffered until reaches obs, and then
+ * output until less than obs remains.  Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void
+def(void)
+{
+	uint64_t cnt;
+	u_char *inp;
+	const u_char *t;
+
+	if ((t = ctab) != NULL)
+		for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+			*inp = t[*inp];
+
+	/* Make the output buffer look right. */
+	out.dbp = in.dbp;
+	out.dbcnt = in.dbcnt;
+
+	if (in.dbcnt >= out.dbsz) {
+		/* If the output buffer is full, write it. */
+		dd_out(0);
+
+		/*
+		 * Ddout copies the leftover output to the beginning of
+		 * the buffer and resets the output buffer.  Reset the
+		 * input buffer to match it.
+	 	 */
+		in.dbp = out.dbp;
+		in.dbcnt = out.dbcnt;
+	}
+}
+
+void
+def_close(void)
+{
+
+	/* Just update the count, everything is already in the buffer. */
+	if (in.dbcnt)
+		out.dbcnt = in.dbcnt;
+}
+
+#ifdef	NO_CONV
+/* Build a smaller version (i.e. for a miniroot) */
+/* These can not be called, but just in case...  */
+static const char no_block[] = "unblock and -DNO_CONV?\n";
+void block(void)		{ fprintf(stderr, "%s", no_block + 2); exit(1); }
+void block_close(void)		{ fprintf(stderr, "%s", no_block + 2); exit(1); }
+void unblock(void)		{ fprintf(stderr, "%s", no_block); exit(1); }
+void unblock_close(void)	{ fprintf(stderr, "%s", no_block); exit(1); }
+#else	/* NO_CONV */
+
+/*
+ * Copy variable length newline terminated records with a max size cbsz
+ * bytes to output.  Records less than cbs are padded with spaces.
+ *
+ * max in buffer:  MAX(ibs, cbsz)
+ * max out buffer: obs + cbsz
+ */
+void
+block(void)
+{
+	static int intrunc;
+	int ch = 0;	/* pacify gcc */
+	uint64_t cnt, maxlen;
+	u_char *inp, *outp;
+	const u_char *t;
+
+	/*
+	 * Record truncation can cross block boundaries.  If currently in a
+	 * truncation state, keep tossing characters until reach a newline.
+	 * Start at the beginning of the buffer, as the input buffer is always
+	 * left empty.
+	 */
+	if (intrunc) {
+		for (inp = in.db, cnt = in.dbrcnt;
+		    cnt && *inp++ != '\n'; --cnt);
+		if (!cnt) {
+			in.dbcnt = 0;
+			in.dbp = in.db;
+			return;
+		}
+		intrunc = 0;
+		/* Adjust the input buffer numbers. */
+		in.dbcnt = cnt - 1;
+		in.dbp = inp + cnt - 1;
+	}
+
+	/*
+	 * Copy records (max cbsz size chunks) into the output buffer.  The
+	 * translation is done as we copy into the output buffer.
+	 */
+	for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
+		maxlen = MIN(cbsz, in.dbcnt);
+		if ((t = ctab) != NULL)
+			for (cnt = 0;
+			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+				*outp++ = t[ch];
+		else
+			for (cnt = 0;
+			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+				*outp++ = ch;
+		/*
+		 * Check for short record without a newline.  Reassemble the
+		 * input block.
+		 */
+		if (ch != '\n' && in.dbcnt < cbsz) {
+			(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+			break;
+		}
+
+		/* Adjust the input buffer numbers. */
+		in.dbcnt -= cnt;
+		if (ch == '\n')
+			--in.dbcnt;
+
+		/* Pad short records with spaces. */
+		if (cnt < cbsz)
+			(void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
+		else {
+			/*
+			 * If the next character wouldn't have ended the
+			 * block, it's a truncation.
+			 */
+			if (!in.dbcnt || *inp != '\n')
+				++st.trunc;
+
+			/* Toss characters to a newline. */
+			for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
+			if (!in.dbcnt)
+				intrunc = 1;
+			else
+				--in.dbcnt;
+		}
+
+		/* Adjust output buffer numbers. */
+		out.dbp += cbsz;
+		if ((out.dbcnt += cbsz) >= out.dbsz)
+			dd_out(0);
+		outp = out.dbp;
+	}
+	in.dbp = in.db + in.dbcnt;
+}
+
+void
+block_close(void)
+{
+
+	/*
+	 * Copy any remaining data into the output buffer and pad to a record.
+	 * Don't worry about truncation or translation, the input buffer is
+	 * always empty when truncating, and no characters have been added for
+	 * translation.  The bottom line is that anything left in the input
+	 * buffer is a truncated record.  Anything left in the output buffer
+	 * just wasn't big enough.
+	 */
+	if (in.dbcnt) {
+		++st.trunc;
+		(void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
+		(void)memset(out.dbp + in.dbcnt,
+		    ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
+		out.dbcnt += cbsz;
+	}
+}
+
+/*
+ * Convert fixed length (cbsz) records to variable length.  Deletes any
+ * trailing blanks and appends a newline.
+ *
+ * max in buffer:  MAX(ibs, cbsz) + cbsz
+ * max out buffer: obs + cbsz
+ */
+void
+unblock(void)
+{
+	uint64_t cnt;
+	u_char *inp;
+	const u_char *t;
+
+	/* Translation and case conversion. */
+	if ((t = ctab) != NULL)
+		for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
+			*inp = t[*inp];
+	/*
+	 * Copy records (max cbsz size chunks) into the output buffer.  The
+	 * translation has to already be done or we might not recognize the
+	 * spaces.
+	 */
+	for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
+		for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
+		if (t >= inp) {
+			cnt = t - inp + 1;
+			(void)memmove(out.dbp, inp, cnt);
+			out.dbp += cnt;
+			out.dbcnt += cnt;
+		}
+		++out.dbcnt;
+		*out.dbp++ = '\n';
+		if (out.dbcnt >= out.dbsz)
+			dd_out(0);
+	}
+	if (in.dbcnt)
+		(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+	in.dbp = in.db + in.dbcnt;
+}
+
+void
+unblock_close(void)
+{
+	uint64_t cnt;
+	u_char *t;
+
+	if (in.dbcnt) {
+		warnx("%s: short input record", in.name);
+		for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
+		if (t >= in.db) {
+			cnt = t - in.db + 1;
+			(void)memmove(out.dbp, in.db, cnt);
+			out.dbp += cnt;
+			out.dbcnt += cnt;
+		}
+		++out.dbcnt;
+		*out.dbp++ = '\n';
+	}
+}
+
+#endif	/* NO_CONV */
+
+#define	tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
+
+void
+summary(void)
+{
+	char buf[100];
+	int64_t mS;
+	struct timeval tv;
+
+	if (progress)
+		(void)write(STDERR_FILENO, "\n", 1);
+
+	(void)gettimeofday(&tv, NULL);
+	mS = tv2mS(tv) - tv2mS(st.start);
+	if (mS == 0)
+		mS = 1;
+	/* Use snprintf(3) so that we don't reenter stdio(3). */
+	(void)snprintf(buf, sizeof(buf),
+	    "%llu+%llu records in\n%llu+%llu records out\n",
+	    (unsigned long long)st.in_full,  (unsigned long long)st.in_part,
+	    (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+	(void)write(STDERR_FILENO, buf, strlen(buf));
+	if (st.swab) {
+		(void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
+		    (unsigned long long)st.swab,
+		    (st.swab == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	if (st.trunc) {
+		(void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
+		    (unsigned long long)st.trunc,
+		    (st.trunc == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	if (st.sparse) {
+		(void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
+		    (unsigned long long)st.sparse,
+		    (st.sparse == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	(void)snprintf(buf, sizeof(buf),
+	    "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
+	    (unsigned long long) st.bytes,
+	    (long) (mS / 1000),
+	    (int) (mS % 1000),
+	    (unsigned long long) (st.bytes * 1000LL / mS));
+	(void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+void
+terminate(int notused)
+{
+
+	exit(0);
+	/* NOTREACHED */
+}
+
+static int	c_arg(const void *, const void *);
+#ifndef	NO_CONV
+static int	c_conv(const void *, const void *);
+#endif
+static void	f_bs(char *);
+static void	f_cbs(char *);
+static void	f_conv(char *);
+static void	f_count(char *);
+static void	f_files(char *);
+static void	f_ibs(char *);
+static void	f_if(char *);
+static void	f_obs(char *);
+static void	f_of(char *);
+static void	f_seek(char *);
+static void	f_skip(char *);
+static void	f_progress(char *);
+
+static const struct arg {
+	const char *name;
+	void (*f)(char *);
+	u_int set, noset;
+} args[] = {
+     /* the array needs to be sorted by the first column so
+	bsearch() can be used to find commands quickly */
+	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
+	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
+	{ "conv",	f_conv,		0,	 0 },
+	{ "count",	f_count,	C_COUNT, C_COUNT },
+	{ "files",	f_files,	C_FILES, C_FILES },
+	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
+	{ "if",		f_if,		C_IF,	 C_IF },
+	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
+	{ "of",		f_of,		C_OF,	 C_OF },
+	{ "progress",	f_progress,	0,	 0 },
+	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
+	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
+};
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void
+jcl(char **argv)
+{
+	struct arg *ap, tmp;
+	char *oper, *arg;
+
+	in.dbsz = out.dbsz = 512;
+
+	while ((oper = *++argv) != NULL) {
+		if ((arg = strchr(oper, '=')) == NULL) {
+			fprintf(stderr, "unknown operand %s\n", oper);
+			exit(1);
+			/* NOTREACHED */
+		}
+		*arg++ = '\0';
+		if (!*arg) {
+			fprintf(stderr, "no value specified for %s\n", oper);
+			exit(1);
+			/* NOTREACHED */
+		}
+		tmp.name = oper;
+		if (!(ap = (struct arg *)bsearch(&tmp, args,
+		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
+		    c_arg))) {
+			fprintf(stderr, "unknown operand %s\n", tmp.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+		if (ddflags & ap->noset) {
+			fprintf(stderr,
+			    "%s: illegal argument combination or already set\n",
+			    tmp.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+		ddflags |= ap->set;
+		ap->f(arg);
+	}
+
+	/* Final sanity checks. */
+
+	if (ddflags & C_BS) {
+		/*
+		 * Bs is turned off by any conversion -- we assume the user
+		 * just wanted to set both the input and output block sizes
+		 * and didn't want the bs semantics, so we don't warn.
+		 */
+		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+		    C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
+			ddflags &= ~C_BS;
+			ddflags |= C_IBS|C_OBS;
+		}
+
+		/* Bs supersedes ibs and obs. */
+		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
+			fprintf(stderr, "bs supersedes ibs and obs\n");
+	}
+
+	/*
+	 * Ascii/ebcdic and cbs implies block/unblock.
+	 * Block/unblock requires cbs and vice-versa.
+	 */
+	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
+		if (!(ddflags & C_CBS)) {
+			fprintf(stderr, "record operations require cbs\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+		cfunc = ddflags & C_BLOCK ? block : unblock;
+	} else if (ddflags & C_CBS) {
+		if (ddflags & (C_ASCII|C_EBCDIC)) {
+			if (ddflags & C_ASCII) {
+				ddflags |= C_UNBLOCK;
+				cfunc = unblock;
+			} else {
+				ddflags |= C_BLOCK;
+				cfunc = block;
+			}
+		} else {
+			fprintf(stderr,
+			    "cbs meaningless if not doing record operations\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+	} else
+		cfunc = def;
+
+	/* Read, write and seek calls take off_t as arguments.
+	 *
+	 * The following check is not done because an off_t is a quad
+	 *  for current NetBSD implementations.
+	 *
+	 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
+	 *	errx(1, "seek offsets cannot be larger than %d", INT_MAX);
+	 */
+}
+
+static int
+c_arg(const void *a, const void *b)
+{
+
+	return (strcmp(((const struct arg *)a)->name,
+	    ((const struct arg *)b)->name));
+}
+
+static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max)
+{
+	long long result;
+	
+	if (sscanf(arg, "%lld", &result) == 0)
+		result = def;
+	return result;
+}
+
+static void
+f_bs(char *arg)
+{
+
+	in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_cbs(char *arg)
+{
+
+	cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
+}
+
+static void
+f_count(char *arg)
+{
+
+	cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
+	if (!cpy_cnt)
+		terminate(0);
+}
+
+static void
+f_files(char *arg)
+{
+
+	files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
+	if (!files_cnt)
+		terminate(0);
+}
+
+static void
+f_ibs(char *arg)
+{
+
+	if (!(ddflags & C_BS))
+		in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_if(char *arg)
+{
+
+	in.name = arg;
+}
+
+static void
+f_obs(char *arg)
+{
+
+	if (!(ddflags & C_BS))
+		out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_of(char *arg)
+{
+
+	out.name = arg;
+}
+
+static void
+f_seek(char *arg)
+{
+
+	out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_skip(char *arg)
+{
+
+	in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_progress(char *arg)
+{
+
+	if (*arg != '0')
+		progress = 1;
+}
+
+#ifdef	NO_CONV
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_conv(char *arg)
+{
+
+	fprintf(stderr, "conv option disabled\n");
+	exit(1);
+	/* NOTREACHED */
+}
+#else	/* NO_CONV */
+
+static const struct conv {
+	const char *name;
+	u_int set, noset;
+	const u_char *ctab;
+} clist[] = {
+	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
+	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
+	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
+	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
+	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
+	{ "noerror",	C_NOERROR,	0,		NULL },
+	{ "notrunc",	C_NOTRUNC,	0,		NULL },
+	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
+	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
+	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
+	{ "osync",	C_OSYNC,	C_BS,		NULL },
+	{ "sparse",	C_SPARSE,	0,		NULL },
+	{ "swab",	C_SWAB,		0,		NULL },
+	{ "sync",	C_SYNC,		0,		NULL },
+	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
+	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
+	/* If you add items to this table, be sure to add the
+	 * conversions to the C_BS check in the jcl routine above.
+	 */
+};
+
+static void
+f_conv(char *arg)
+{
+	struct conv *cp, tmp;
+
+	while (arg != NULL) {
+		tmp.name = strsep(&arg, ",");
+		if (!(cp = (struct conv *)bsearch(&tmp, clist,
+		    sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
+		    c_conv))) {
+			errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
+			/* NOTREACHED */
+		}
+		if (ddflags & cp->noset) {
+			errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name);
+			/* NOTREACHED */
+		}
+		ddflags |= cp->set;
+		if (cp->ctab)
+			ctab = cp->ctab;
+	}
+}
+
+static int
+c_conv(const void *a, const void *b)
+{
+
+	return (strcmp(((const struct conv *)a)->name,
+	    ((const struct conv *)b)->name));
+}
+
+#endif	/* NO_CONV */
+
+
diff --git a/toolbox/dd.h b/toolbox/dd.h
new file mode 100644
index 0000000..794a464
--- /dev/null
+++ b/toolbox/dd.h
@@ -0,0 +1,91 @@
+/*	$NetBSD: dd.h,v 1.12 2004/01/17 20:48:57 dbj Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)dd.h	8.3 (Berkeley) 4/2/94
+ */
+
+/* Input/output stream state. */
+typedef struct {
+	u_char		*db;		/* buffer address */
+	u_char		*dbp;		/* current buffer I/O address */
+	uint64_t	dbcnt;		/* current buffer byte count */
+	int64_t		dbrcnt;		/* last read byte count */
+	uint64_t	dbsz;		/* buffer size */
+
+#define	ISCHR		0x01		/* character device (warn on short) */
+#define	ISPIPE		0x02		/* pipe (not truncatable) */
+#define	ISTAPE		0x04		/* tape (not seekable) */
+#define	NOREAD		0x08		/* not readable */
+	u_int		flags;
+
+	const char  	*name;		/* name */
+	int		fd;		/* file descriptor */
+	uint64_t	offset;		/* # of blocks to skip */
+} IO;
+
+typedef struct {
+	uint64_t	in_full;	/* # of full input blocks */
+	uint64_t	in_part;	/* # of partial input blocks */
+	uint64_t	out_full;	/* # of full output blocks */
+	uint64_t	out_part;	/* # of partial output blocks */
+	uint64_t	trunc;		/* # of truncated records */
+	uint64_t	swab;		/* # of odd-length swab blocks */
+	uint64_t	sparse;		/* # of sparse output blocks */
+	uint64_t	bytes;		/* # of bytes written */
+	struct timeval	start;		/* start time of dd */
+} STAT;
+
+/* Flags (in ddflags). */
+#define	C_ASCII		0x00001
+#define	C_BLOCK		0x00002
+#define	C_BS		0x00004
+#define	C_CBS		0x00008
+#define	C_COUNT		0x00010
+#define	C_EBCDIC	0x00020
+#define	C_FILES		0x00040
+#define	C_IBS		0x00080
+#define	C_IF		0x00100
+#define	C_LCASE		0x00200
+#define	C_NOERROR	0x00400
+#define	C_NOTRUNC	0x00800
+#define	C_OBS		0x01000
+#define	C_OF		0x02000
+#define	C_SEEK		0x04000
+#define	C_SKIP		0x08000
+#define	C_SWAB		0x10000
+#define	C_SYNC		0x20000
+#define	C_UCASE		0x40000
+#define	C_UNBLOCK	0x80000
+#define	C_OSYNC		0x100000
+#define	C_SPARSE	0x200000
diff --git a/toolbox/df.c b/toolbox/df.c
new file mode 100644
index 0000000..90476bd
--- /dev/null
+++ b/toolbox/df.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/statfs.h>
+
+static int ok = EXIT_SUCCESS;
+
+static void df(char *s, int always) {
+    struct statfs st;
+
+    if (statfs(s, &st) < 0) {
+        fprintf(stderr, "%s: %s\n", s, strerror(errno));
+        ok = EXIT_FAILURE;
+    } else {
+        if (st.f_blocks == 0 && !always)
+            return;
+
+        printf("%s: %lldK total, %lldK used, %lldK available (block size %d)\n",
+               s,
+               ((long long)st.f_blocks * (long long)st.f_bsize) / 1024,
+               ((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize) / 1024,
+               ((long long)st.f_bfree * (long long)st.f_bsize) / 1024,
+               (int) st.f_bsize);
+    }
+}
+
+int df_main(int argc, char *argv[]) {
+    if (argc == 1) {
+        char s[2000];
+        FILE *f = fopen("/proc/mounts", "r");
+
+        while (fgets(s, 2000, f)) {
+            char *c, *e = s;
+
+            for (c = s; *c; c++) {
+                if (*c == ' ') {
+                    e = c + 1;
+                    break;
+                }
+            }
+
+            for (c = e; *c; c++) {
+                if (*c == ' ') {
+                    *c = '\0';
+                    break;
+                }
+            }
+
+            df(e, 0);
+        }
+
+        fclose(f);
+    } else {
+        int i;
+
+        for (i = 1; i < argc; i++) {
+            df(argv[i], 1);
+        }
+    }
+
+    exit(ok);
+}
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c
new file mode 100644
index 0000000..e57f607
--- /dev/null
+++ b/toolbox/dmesg.c
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/klog.h>
+#include <string.h>
+
+#define KLOG_BUF_SHIFT	17	/* CONFIG_LOG_BUF_SHIFT from our kernel */
+#define KLOG_BUF_LEN	(1 << KLOG_BUF_SHIFT)
+
+int dmesg_main(int argc, char **argv)
+{
+    char buffer[KLOG_BUF_LEN + 1];
+    char *p = buffer;
+    ssize_t ret;
+    int n, op;
+
+    if((argc == 2) && (!strcmp(argv[1],"-c"))) {
+        op = KLOG_READ_CLEAR;
+    } else {
+        op = KLOG_READ_ALL;
+    }
+
+    n = klogctl(op, buffer, KLOG_BUF_LEN);
+    if (n < 0) {
+        perror("klogctl");
+        return EXIT_FAILURE;
+    }
+    buffer[n] = '\0';
+
+    while((ret = write(STDOUT_FILENO, p, n))) {
+        if (ret == -1) {
+	    if (errno == EINTR)
+                continue;
+	    perror("write");
+	    return EXIT_FAILURE;
+	}
+	p += ret;
+	n -= ret;
+    }
+
+    return 0;
+}
diff --git a/toolbox/exists.c b/toolbox/exists.c
new file mode 100644
index 0000000..e348668
--- /dev/null
+++ b/toolbox/exists.c
@@ -0,0 +1,16 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int exists_main(int argc, char *argv[])
+{
+    struct stat s;
+
+    if(argc < 2) return 1;
+
+    if(stat(argv[1], &s)) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
new file mode 100644
index 0000000..14372cb
--- /dev/null
+++ b/toolbox/getevent.c
@@ -0,0 +1,427 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+#include <linux/input.h> // this does not compile
+#include <errno.h>
+
+static struct pollfd *ufds;
+static char **device_names;
+static int nfds;
+
+enum {
+    PRINT_DEVICE_ERRORS     = 1U << 0,
+    PRINT_DEVICE            = 1U << 1,
+    PRINT_DEVICE_NAME       = 1U << 2,
+    PRINT_DEVICE_INFO       = 1U << 3,
+    PRINT_VERSION           = 1U << 4,
+    PRINT_POSSIBLE_EVENTS   = 1U << 5,
+};
+
+static int print_possible_events(int fd)
+{
+    uint8_t *bits = NULL;
+    ssize_t bits_size = 0;
+    int i, j, k;
+    int res, res2;
+    
+    printf("  events:\n");
+    for(i = 0; i <= EV_MAX; i++) {
+        int count = 0;
+        while(1) {
+            res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
+            if(res < bits_size)
+                break;
+            bits_size = res + 16;
+            bits = realloc(bits, bits_size * 2);
+            if(bits == NULL) {
+                fprintf(stderr, "failed to allocate buffer of size %d\n", bits_size);
+                return 1;
+            }
+        }
+        switch(i) {
+            case EV_KEY:
+                res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
+                break;
+            case EV_LED:
+                res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
+                break;
+            case EV_SND:
+                res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
+                break;
+            case EV_SW:
+                res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
+                break;
+            default:
+                res2 = 0;
+        }
+        for(j = 0; j < res; j++) {
+            for(k = 0; k < 8; k++)
+                if(bits[j] & 1 << k) {
+                    char down;
+                    if(j < res2 && (bits[j + bits_size] & 1 << k))
+                        down = '*';
+                    else
+                        down = ' ';
+                    if(count == 0)
+                        printf("    type %04x:", i);
+                    else if((count & 0x7) == 0 || i == EV_ABS)
+                        printf("\n              ");
+                    printf(" %04x%c", j * 8 + k, down);
+                    if(i == EV_ABS) {
+                        struct input_absinfo abs;
+                        if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
+                            printf(" value %d, min %d, max %d, fuzz %d flat %d", abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat);
+                        }
+                    }
+                    count++;
+                }
+        }
+        if(count)
+            printf("\n");
+    }
+    free(bits);
+    return 0;
+}
+
+static int open_device(const char *device, int print_flags)
+{
+    int version;
+    int fd;
+    struct pollfd *new_ufds;
+    char **new_device_names;
+    char name[80];
+    char location[80];
+    char idstr[80];
+    struct input_id id;
+
+    fd = open(device, O_RDWR);
+    if(fd < 0) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    
+    if(ioctl(fd, EVIOCGVERSION, &version)) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    if(ioctl(fd, EVIOCGID, &id)) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    name[sizeof(name) - 1] = '\0';
+    location[sizeof(location) - 1] = '\0';
+    idstr[sizeof(idstr) - 1] = '\0';
+    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+        //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
+        name[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+        //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
+        location[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+        //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
+        idstr[0] = '\0';
+    }
+
+    new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
+    if(new_ufds == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    ufds = new_ufds;
+    new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
+    if(new_device_names == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    device_names = new_device_names;
+
+    if(print_flags & PRINT_DEVICE)
+        printf("add device %d: %s\n", nfds, device);
+    if(print_flags & PRINT_DEVICE_INFO)
+        printf("  bus:      %04x\n"
+               "  vendor    %04x\n"
+               "  product   %04x\n"
+               "  version   %04x\n",
+               id.bustype, id.vendor, id.product, id.version);
+    if(print_flags & PRINT_DEVICE_NAME)
+        printf("  name:     \"%s\"\n", name);
+    if(print_flags & PRINT_DEVICE_INFO)
+        printf("  location: \"%s\"\n"
+               "  id:       \"%s\"\n", location, idstr);
+    if(print_flags & PRINT_VERSION)
+        printf("  version:  %d.%d.%d\n",
+               version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+    if(print_flags & PRINT_POSSIBLE_EVENTS) {
+        print_possible_events(fd);
+    }
+
+    ufds[nfds].fd = fd;
+    ufds[nfds].events = POLLIN;
+    device_names[nfds] = strdup(device);
+    nfds++;
+
+    return 0;
+}
+
+int close_device(const char *device, int print_flags)
+{
+    int i;
+    for(i = 1; i < nfds; i++) {
+        if(strcmp(device_names[i], device) == 0) {
+            int count = nfds - i - 1;
+            if(print_flags & PRINT_DEVICE)
+                printf("remove device %d: %s\n", i, device);
+            free(device_names[i]);
+            memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+            memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+            nfds--;
+            return 0;
+        }
+    }
+    if(print_flags & PRINT_DEVICE_ERRORS)
+        fprintf(stderr, "remote device: %s not found\n", device);
+    return -1;
+}
+
+static int read_notify(const char *dirname, int nfd, int print_flags)
+{
+    int res;
+    char devname[PATH_MAX];
+    char *filename;
+    char event_buf[512];
+    int event_size;
+    int event_pos = 0;
+    struct inotify_event *event;
+
+    res = read(nfd, event_buf, sizeof(event_buf));
+    if(res < (int)sizeof(*event)) {
+        if(errno == EINTR)
+            return 0;
+        fprintf(stderr, "could not get event, %s\n", strerror(errno));
+        return 1;
+    }
+    //printf("got %d bytes of event information\n", res);
+
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+
+    while(res >= (int)sizeof(*event)) {
+        event = (struct inotify_event *)(event_buf + event_pos);
+        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+        if(event->len) {
+            strcpy(filename, event->name);
+            if(event->mask & IN_CREATE) {
+                open_device(devname, print_flags);
+            }
+            else {
+                close_device(devname, print_flags);
+            }
+        }
+        event_size = sizeof(*event) + event->len;
+        res -= event_size;
+        event_pos += event_size;
+    }
+    return 0;
+}
+
+static int scan_dir(const char *dirname, int print_flags)
+{
+    char devname[PATH_MAX];
+    char *filename;
+    DIR *dir;
+    struct dirent *de;
+    dir = opendir(dirname);
+    if(dir == NULL)
+        return -1;
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+    while((de = readdir(dir))) {
+        if(de->d_name[0] == '.' &&
+           (de->d_name[1] == '\0' ||
+            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+            continue;
+        strcpy(filename, de->d_name);
+        open_device(devname, print_flags);
+    }
+    closedir(dir);
+    return 0;
+}
+
+static void usage(int argc, char *argv[])
+{
+    fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-q] [-c count] [-r] [device]\n", argv[0]);
+}
+
+int getevent_main(int argc, char *argv[])
+{
+    int c;
+    int i;
+    int res;
+    int pollres;
+    int get_time = 0;
+    int print_device = 0;
+    char *newline = "\n";
+    uint16_t get_switch = 0;
+    struct input_event event;
+    int version;
+    int print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
+    int print_flags_set = 0;
+    int dont_block = -1;
+    int event_count = 0;
+    int sync_rate = 0;
+    int64_t last_sync_time = 0;
+    const char *device = NULL;
+    const char *device_path = "/dev/input";
+
+    opterr = 0;
+    do {
+        c = getopt(argc, argv, "tns:Sv::qc:rh");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 't':
+            get_time = 1;
+            break;
+        case 'n':
+            newline = "";
+            break;
+        case 's':
+            get_switch = strtoul(optarg, NULL, 0);
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'S':
+            get_switch = ~0;
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'v':
+            if(optarg)
+                print_flags =  strtoul(optarg, NULL, 0);
+            else
+                print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
+            print_flags_set = 1;
+            break;
+        case 'q':
+            print_flags = 0;
+            print_flags_set = 1;
+            break;
+        case 'c':
+            event_count = atoi(optarg);
+            dont_block = 0;
+            break;
+        case 'r':
+            sync_rate = 1;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+        case 'h':
+            usage(argc, argv);
+            exit(1);
+        }
+    } while (1);
+    if(dont_block == -1)
+        dont_block = 0;
+
+    if (optind + 1 == argc) {
+        device = argv[optind];
+        optind++;
+    }
+    if (optind != argc) {
+        usage(argc, argv);
+        exit(1);
+    }
+    nfds = 1;
+    ufds = calloc(1, sizeof(ufds[0]));
+    ufds[0].fd = inotify_init();
+    ufds[0].events = POLLIN;
+    if(device) {
+        if(!print_flags_set)
+            print_flags = PRINT_DEVICE_ERRORS;
+        res = open_device(device, print_flags);
+        if(res < 0) {
+            return 1;
+        }
+    }
+    else {
+        print_device = 1;
+		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+        if(res < 0) {
+            fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
+            return 1;
+        }
+        res = scan_dir(device_path, print_flags);
+        if(res < 0) {
+            fprintf(stderr, "scan dir failed for %s\n", device_path);
+            return 1;
+        }
+    }
+
+    if(get_switch) {
+        for(i = 1; i < nfds; i++) {
+            uint16_t sw;
+            res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
+            if(res < 0) {
+                fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
+                return 1;
+            }
+            sw &= get_switch;
+            printf("%04x%s", sw, newline);
+        }
+    }
+
+    if(dont_block)
+        return 0;
+
+    while(1) {
+        pollres = poll(ufds, nfds, -1);
+        //printf("poll %d, returned %d\n", nfds, pollres);
+        if(ufds[0].revents & POLLIN) {
+            read_notify(device_path, ufds[0].fd, print_flags);
+        }
+        for(i = 1; i < nfds; i++) {
+            if(ufds[i].revents) {
+                if(ufds[i].revents & POLLIN) {
+                    res = read(ufds[i].fd, &event, sizeof(event));
+                    if(res < (int)sizeof(event)) {
+                        fprintf(stderr, "could not get event\n");
+                        return 1;
+                    }
+                    if(get_time) {
+                        printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec);
+                    }
+                    if(print_device)
+                        printf("%s: ", device_names[i]);
+                    printf("%04x %04x %08x", event.type, event.code, event.value);
+                    if(sync_rate && event.type == 0 && event.code == 0) {
+                        int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
+                        if(last_sync_time)
+                            printf(" rate %lld", 1000000LL / (now - last_sync_time));
+                        last_sync_time = now;
+                    }
+                    printf("%s", newline);
+                    if(event_count && --event_count == 0)
+                        return 0;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/getprop.c b/toolbox/getprop.c
new file mode 100644
index 0000000..fc80a4d
--- /dev/null
+++ b/toolbox/getprop.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+static void proplist(const char *key, const char *name, 
+                     void *user __attribute__((unused)))
+{
+    printf("[%s]: [%s]\n", key, name);
+}
+
+int __system_property_wait(prop_info *pi);
+
+int getprop_main(int argc, char *argv[])
+{
+    int n = 0;
+
+    if (argc == 1) {
+        (void)property_list(proplist, NULL);
+    } else {
+        char value[PROPERTY_VALUE_MAX];
+        char *default_value;
+        if(argc > 2) {
+            default_value = argv[2];
+        } else {
+            default_value = "";
+        }
+
+        property_get(argv[1], value, default_value);
+        printf("%s\n", value);
+    }
+    return 0;
+}
diff --git a/toolbox/hd.c b/toolbox/hd.c
new file mode 100644
index 0000000..1f7d179
--- /dev/null
+++ b/toolbox/hd.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int hd_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+	unsigned char buf[4096];
+    int res;
+	int read_len;
+	int rv = 0;
+	int i;
+	int filepos = 0;
+	int sum;
+	int lsum;
+
+	int base = -1;
+	int count = 0;
+	int repeat = 0;
+
+    do {
+        c = getopt(argc, argv, "b:c:r:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'b':
+            base = strtol(optarg, NULL, 0);
+            break;
+        case 'c':
+            count = strtol(optarg, NULL, 0);
+            break;
+		case 'r':
+			repeat = strtol(optarg, NULL, 0);
+			break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind + 1 != argc) {
+        fprintf(stderr, "Usage: %s [-b base] [-c count] [-r delay] file\n", argv[0]);
+        exit(1);
+    }
+
+    fd = open(argv[optind], O_RDONLY);
+    if(fd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+
+	do {
+		if(base >= 0) {
+			lseek(fd, base, SEEK_SET);
+			filepos = base;
+		}
+		sum = 0;
+		lsum = 0;
+	    while(1) {
+			read_len = sizeof(buf);
+			if(count > 0 && base + count - filepos < read_len)
+				read_len = base + count - filepos;
+	        res = read(fd, &buf, read_len);
+			for(i = 0; i < res; i++) {
+				if((i & 15) == 0) {
+					printf("%08x: ", filepos + i);
+				}
+				lsum += buf[i];
+				sum += buf[i];
+				printf("%02x ", buf[i]);
+				if(((i & 15) == 15) || (i == res - 1)) {
+					printf("s %x\n", lsum);
+					lsum = 0;
+				}
+			}
+			if(res <= 0) {
+				printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno));
+				return 1;
+			}
+			filepos += res;
+			if(filepos == base + count)
+				break;
+	    }
+		printf("sum %x\n", sum);
+		if(repeat)
+			sleep(repeat);
+	} while(repeat);
+	return 0;
+}
diff --git a/toolbox/id.c b/toolbox/id.c
new file mode 100644
index 0000000..bb03cad
--- /dev/null
+++ b/toolbox/id.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+static void print_uid(uid_t uid)
+{
+    struct passwd *pw = getpwuid(uid);
+
+    if (pw) {
+        printf("%d(%s)", uid, pw->pw_name);
+    } else {
+        printf("%d",uid);
+    }
+}
+
+static void print_gid(gid_t gid)
+{
+    struct group *gr = getgrgid(gid);
+    if (gr) {
+        printf("%d(%s)", gid, gr->gr_name);
+    } else {
+        printf("%d",gid);
+    }
+}
+
+int id_main(int argc, char **argv)
+{
+    gid_t list[64];
+    int n, max;
+
+    max = getgroups(64, list);
+    if (max < 0) max = 0;
+
+    printf("uid=");
+    print_uid(getuid());
+    printf(" gid=");
+    print_gid(getgid());
+    if (max) {
+        printf(" groups=");
+        print_gid(list[0]);
+        for(n = 1; n < max; n++) {
+            printf(",");
+            print_gid(list[n]);
+        }
+    }
+    printf("\n");
+    return 0;
+}
diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c
new file mode 100644
index 0000000..e83cd8b
--- /dev/null
+++ b/toolbox/ifconfig.c
@@ -0,0 +1,139 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+
+static void die(const char *s)
+{
+    fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
+    exit(-1);
+}
+
+static void setflags(int s, struct ifreq *ifr, int set, int clr)
+{
+    if(ioctl(s, SIOCGIFFLAGS, ifr) < 0) die("SIOCGIFFLAGS");
+    ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set;
+    if(ioctl(s, SIOCSIFFLAGS, ifr) < 0) die("SIOCSIFFLAGS");
+}
+
+static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
+{
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+static void setnetmask(int s, struct ifreq *ifr, const char *addr)
+{
+	init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr);
+    if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK");
+}
+
+static void setaddr(int s, struct ifreq *ifr, const char *addr)
+{
+	init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr);
+    if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR");
+}
+
+int ifconfig_main(int argc, char *argv[])
+{
+    struct ifreq ifr;
+    int s;
+    unsigned int addr, mask, flags;
+    char astring[20];
+    char mstring[20];
+    char *updown, *brdcst, *loopbk, *ppp, *running, *multi;
+    
+    argc--;
+    argv++;
+
+    if(argc == 0) return 0;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+    ifr.ifr_name[IFNAMSIZ-1] = 0;
+    argc--, argv++;
+
+    if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        die("cannot open control socket\n");
+    }
+
+    if (argc == 0) {
+        if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+        if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+        if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            flags = ifr.ifr_flags;
+
+        sprintf(astring, "%d.%d.%d.%d",
+                addr & 0xff,
+                ((addr >> 8) & 0xff),
+                ((addr >> 16) & 0xff),
+                ((addr >> 24) & 0xff));
+        sprintf(mstring, "%d.%d.%d.%d",
+                mask & 0xff,
+                ((mask >> 8) & 0xff),
+                ((mask >> 16) & 0xff),
+                ((mask >> 24) & 0xff));
+        printf("%s: ip %s mask %s flags [", ifr.ifr_name,
+               astring,
+               mstring
+               );
+
+        updown =  (flags & IFF_UP)           ? "up" : "down";
+        brdcst =  (flags & IFF_BROADCAST)    ? " broadcast" : "";
+        loopbk =  (flags & IFF_LOOPBACK)     ? " loopback" : "";
+        ppp =     (flags & IFF_POINTOPOINT)  ? " point-to-point" : "";
+        running = (flags & IFF_RUNNING)      ? " running" : "";
+        multi =   (flags & IFF_MULTICAST)    ? " multicast" : "";
+        printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi);
+
+
+
+/*    char *updown, *brdcst, *loopbk, *ppp, *running, *multi; */
+
+        return 0;
+    }
+    
+    while(argc > 0){
+        if(!strcmp(argv[0], "up")) {
+            setflags(s, &ifr, IFF_UP, 0);
+        } else if(!strcmp(argv[0], "down")) {
+            setflags(s, &ifr, 0, IFF_UP);
+		} else if(!strcmp(argv[0], "netmask")) {
+			argc--, argv++;
+			if (0 == argc) { 
+				errno = EINVAL;
+				die("expecting an IP address for parameter \"netmask\"");
+			}
+			setnetmask(s, &ifr, argv[0]);
+        } else if(isdigit(argv[0][0])){
+            setaddr(s, &ifr, argv[0]);
+        }
+        argc--, argv++;
+    }
+
+    return 0;
+}
diff --git a/toolbox/iftop.c b/toolbox/iftop.c
new file mode 100644
index 0000000..800c0f0
--- /dev/null
+++ b/toolbox/iftop.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#define PROC_NET_DEV    "/proc/net/dev"
+
+#define MAX_IF           8   /* max interfaces we can handle */
+
+#ifndef PAGE_SIZE
+# define PAGE_SIZE 4096
+#endif
+
+#define _STR(s) #s
+#define STR(s) _STR(s)
+
+struct if_stats {
+    char name[IFNAMSIZ];
+
+    unsigned int mtu;
+
+    unsigned int rx_bytes;
+    unsigned int rx_packets;
+    unsigned int rx_errors;
+    unsigned int rx_dropped;
+
+    unsigned int tx_bytes;
+    unsigned int tx_packets;
+    unsigned int tx_errors;
+    unsigned int tx_dropped;
+};
+
+static int get_mtu(const char *if_name)
+{
+    struct ifreq ifr;
+    int s, ret;
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+        perror("socket");
+        exit(EXIT_FAILURE);
+    }
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    ifr.ifr_addr.sa_family = AF_INET;
+    strcpy(ifr.ifr_name, if_name);
+
+    ret = ioctl(s, SIOCGIFMTU, &ifr);
+    if (ret < 0) {
+        perror("ioctl");
+        exit(EXIT_FAILURE);
+    }
+
+    ret = close(s);
+    if (ret < 0) {
+        perror("close");
+        exit(EXIT_FAILURE);
+    }
+
+    return ifr.ifr_mtu;
+}
+
+static int get_interfaces(struct if_stats *ifs)
+{
+    char buf[PAGE_SIZE];
+    char *p;
+    int ret, nr, fd;
+
+    fd = open(PROC_NET_DEV, O_RDONLY);
+    if (fd < 0) {
+        perror("open");
+        exit(EXIT_FAILURE);
+    }
+
+    ret = read(fd, buf, sizeof(buf) - 1);
+    if (ret < 0) {
+        perror("read");
+        exit(EXIT_FAILURE);
+    } else if (!ret) {
+        fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n");
+        exit(EXIT_FAILURE);
+    }
+    buf[ret] = '\0';
+
+    /* skip down to the third line */
+    p = strchr(buf, '\n');
+    if (!p) {
+        fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+        exit(EXIT_FAILURE);
+    }
+    p = strchr(p + 1, '\n');
+    if (!p) {
+        fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+        exit(EXIT_FAILURE);
+    }
+    p += 1;
+
+    /*
+     * Key:
+     * if: (Rx) bytes packets errs drop fifo frame compressed multicast \
+     *     (Tx) bytes packets errs drop fifo colls carrier compressed
+     */
+    for (nr = 0; nr < MAX_IF; nr++) {
+        char *c;
+
+        ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name);
+        if (ret != 1) {
+            fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+            exit(EXIT_FAILURE);
+        }
+
+        /*
+         * This works around a bug in the proc file where large interface names
+         * or Rx byte counts eat the delimiter, breaking sscanf.
+         */
+        c = strchr(ifs->name, ':');
+        if (c)
+            *c = '\0';
+
+        p = strchr(p, ':') + 1;
+
+        ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u "
+                     "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets,
+                     &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes,
+                     &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped);
+        if (ret != 8) {
+            fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+            exit(EXIT_FAILURE);
+        }
+
+        ifs->mtu = get_mtu(ifs->name);
+
+        p = strchr(p, '\n') + 1;
+        if (*p == '\0') {
+            nr++;
+            break;
+        }
+
+        ifs++;
+    }
+
+    ret = close(fd);
+    if (ret) {
+        perror("close");
+        exit(EXIT_FAILURE);
+    }
+
+    return nr;
+}
+
+static void print_header(void)
+{
+    printf("               Rx                              Tx\n");
+    printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n",
+           "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes",
+           "packets", "errs", "drpd");
+}
+
+static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr)
+{
+    int i = 0;
+
+    while (nr--) {
+        if (old->rx_packets || old->tx_packets) {
+            printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n",
+                   new->name, new->mtu,
+                   new->rx_bytes - old->rx_bytes,
+                   new->rx_packets - old->rx_packets,
+                   new->rx_errors - old->rx_errors,
+                   new->rx_dropped - old->rx_dropped,
+                   new->tx_bytes - old->tx_bytes,
+                   new->tx_packets - old->tx_packets,
+                   new->tx_errors - old->tx_errors,
+                   new->tx_dropped - old->tx_dropped);
+            i++;
+        }
+        old++;
+        new++;
+    }
+
+    return i;
+}
+
+static void usage(const char *cmd)
+{
+    fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
+}
+
+int iftop_main(int argc, char *argv[])
+{
+    struct if_stats ifs[2][MAX_IF];
+    int count = 0, header_interval = 22, delay = 1, i;
+    unsigned int toggle = 0;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-d")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -d requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[i++]);
+            if (!delay)
+                delay = 1;
+            continue;
+        }
+        if (!strcmp(argv[i], "-r")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -r requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            header_interval = atoi(argv[i++]);
+            if (header_interval < MAX_IF)
+                header_interval = MAX_IF;
+            continue;
+        }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    get_interfaces(ifs[!toggle]);
+    if (header_interval)
+        print_header();
+    while (1) {
+        int nr;
+
+        sleep(delay);
+        nr = get_interfaces(ifs[toggle]);
+        if (header_interval && count + nr > header_interval) {
+            print_header();
+            count = 0;
+        }
+        count += print_interfaces(ifs[!toggle], ifs[toggle], nr);
+        toggle = !toggle;
+    }
+
+    return 0;
+}
diff --git a/toolbox/insmod.c b/toolbox/insmod.c
new file mode 100644
index 0000000..44b9847
--- /dev/null
+++ b/toolbox/insmod.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+extern int init_module(void *, unsigned long, const char *);
+
+static void *read_file(const char *filename, ssize_t *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
+
+#define min(x,y) ((x) < (y) ? (x) : (y))
+int insmod_main(int argc, char **argv)
+{
+	void *file;
+	ssize_t size = 0;
+	char opts[1024];
+	int ret;
+
+	/* make sure we've got an argument */
+	if (argc < 2) {
+		fprintf(stderr, "usage: insmod <module.o>\n");
+		return -1;
+	}
+
+	/* read the file into memory */
+	file = read_file(argv[1], &size);
+	if (!file) {
+		fprintf(stderr, "insmod: can't open '%s'\n", argv[1]);
+		return -1;
+	}
+
+	opts[0] = '\0';
+	if (argc > 2) {
+		int i, len;
+		char *end = opts + sizeof(opts) - 1;
+		char *ptr = opts;
+
+		for (i = 2; (i < argc) && (ptr < end); i++) {
+			len = min(strlen(argv[i]), end - ptr);
+			memcpy(ptr, argv[i], len);
+			ptr += len;
+			*ptr++ = ' ';
+			*ptr++ = '\0';
+		}
+		*(ptr - 1) = '\0';
+	}
+
+	/* pass it to the kernel */
+	ret = init_module(file, size, opts);
+	if (ret != 0) {
+		fprintf(stderr,
+                "insmod: init_module '%s' failed (%s)\n",
+                argv[1], strerror(errno));
+	}
+
+	/* free the file buffer */
+	free(file);
+
+	return ret;
+}
+
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
new file mode 100644
index 0000000..e28f2a4
--- /dev/null
+++ b/toolbox/ioctl.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+
+int ioctl_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+    int res;
+
+    int read_only = 0;
+    int length = -1;
+    int arg_size = 4;
+    int direct_arg = 0;
+    uint32_t ioctl_nr;
+    void *ioctl_args;
+    uint8_t *ioctl_argp;
+    uint8_t *ioctl_argp_save;
+    int rem;
+
+    do {
+        c = getopt(argc, argv, "rdl:a:h");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'r':
+            read_only = 1;
+            break;
+        case 'd':
+            direct_arg = 1;
+            break;
+        case 'l':
+            length = strtol(optarg, NULL, 0);
+            break;
+        case 'a':
+            arg_size = strtol(optarg, NULL, 0);
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
+                    "  -l <lenght>   Length of io buffer\n"
+                    "  -a <argsize>  Size of each argument (1-8)\n"
+                    "  -r            Open device in read only mode\n"
+                    "  -d            Direct argument (no iobuffer)\n"
+                    "  -h            Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(optind + 2 > argc) {
+        fprintf(stderr, "%s: too few arguments\n", argv[0]);
+        exit(1);
+    }
+
+    fd = open(argv[optind], O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s\n", argv[optind]);
+        return 1;
+    }
+    optind++;
+    
+    ioctl_nr = strtol(argv[optind], NULL, 0);
+    optind++;
+
+    if(direct_arg) {
+        arg_size = 4;
+        length = 4;
+    }
+
+    if(length < 0) {
+        length = (argc - optind) * arg_size;
+    }
+    if(length) {
+        ioctl_args = calloc(1, length);
+
+        ioctl_argp_save = ioctl_argp = ioctl_args;
+        rem = length;
+        while(optind < argc) {
+            uint64_t tmp = strtoull(argv[optind], NULL, 0);
+            if(rem < arg_size) {
+                fprintf(stderr, "%s: too many arguments\n", argv[0]);
+                exit(1);
+            }
+            memcpy(ioctl_argp, &tmp, arg_size);
+            ioctl_argp += arg_size;
+            rem -= arg_size;
+            optind++;
+        }
+    }
+    printf("sending ioctl 0x%x", ioctl_nr);
+    rem = length;
+    while(rem--) {
+        printf(" 0x%02x", *ioctl_argp_save++);
+    }
+    printf("\n");
+
+    if(direct_arg)
+        res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
+    else if(length)
+        res = ioctl(fd, ioctl_nr, ioctl_args);
+    else
+        res = ioctl(fd, ioctl_nr, 0);
+    if (res < 0) {
+        fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res);
+        return 1;
+    }
+    if(length) {
+        printf("return buf:");
+        ioctl_argp = ioctl_args;
+        rem = length;
+        while(rem--) {
+            printf(" %02x", *ioctl_argp++);
+        }
+        printf("\n");
+    }
+    return 0;
+}
diff --git a/toolbox/kill.c b/toolbox/kill.c
new file mode 100644
index 0000000..4d0e479
--- /dev/null
+++ b/toolbox/kill.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <signal.h>
+
+int kill_main(int argc, char **argv)
+{
+    int sig = SIGTERM;
+    int result = 0;
+    
+    argc--;
+    argv++;
+
+    if(argc >= 2 && argv[0][0] == '-'){
+        sig = atoi(argv[0] + 1);
+        argc--;
+        argv++;
+    }
+
+    while(argc > 0){
+        int pid = atoi(argv[0]);
+        int err = kill(pid, sig);
+        if (err < 0) {
+            result = err;
+            fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno));
+        }
+            
+        argc--;
+        argv++;
+    }
+    
+    return result;
+}
diff --git a/toolbox/ln.c b/toolbox/ln.c
new file mode 100644
index 0000000..dcd5e3a
--- /dev/null
+++ b/toolbox/ln.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+    fprintf(stderr,"ln [-s] <target> <name>\n");
+    return -1;
+}
+
+int ln_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2) return usage();
+    
+    if(!strcmp(argv[1],"-s")) {
+        symbolic = 1;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 3) return usage();
+
+    if(symbolic) {
+        ret = symlink(argv[1], argv[2]);
+    } else {
+        ret = link(argv[1], argv[2]);
+    }
+    if(ret < 0)
+        fprintf(stderr, "link failed %s\n", strerror(errno));
+    return ret;
+}
diff --git a/toolbox/log.c b/toolbox/log.c
new file mode 100644
index 0000000..f30e6a7
--- /dev/null
+++ b/toolbox/log.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <cutils/logd.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <cutils/sockets.h>
+#include <unistd.h>
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+    android_LogPriority pri;
+
+    c = tolower(c);
+
+    if (c >= '0' && c <= '9') {
+        if (c >= ('0'+ANDROID_LOG_SILENT)) {
+            pri = ANDROID_LOG_VERBOSE;
+        } else {
+            pri = (android_LogPriority)(c - '0');
+        }
+    } else if (c == 'v') {
+        pri = ANDROID_LOG_VERBOSE;
+    } else if (c == 'd') {
+        pri = ANDROID_LOG_DEBUG;
+    } else if (c == 'i') {
+        pri = ANDROID_LOG_INFO;
+    } else if (c == 'w') {
+        pri = ANDROID_LOG_WARN;
+    } else if (c == 'e') {
+        pri = ANDROID_LOG_ERROR;
+    } else if (c == 'f') {
+        pri = ANDROID_LOG_FATAL;
+    } else if (c == 's') {
+        pri = ANDROID_LOG_SILENT;
+    } else if (c == '*') {
+        pri = ANDROID_LOG_DEFAULT;
+    } else {
+        pri = ANDROID_LOG_UNKNOWN;
+    }
+
+    return pri;
+}
+
+static int usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s);
+
+    fprintf(stderr, "\tpriorityChar should be one of:\n"
+                        "\t\tv,d,i,w,e\n");
+    exit(-1);
+}
+
+
+int log_main(int argc, char *argv[])
+{
+    android_LogPriority priority; 
+    const char *tag = "log";
+    char buffer[4096];
+    int i;
+
+    priority = ANDROID_LOG_INFO;
+
+    for (;;) {
+        int ret;
+
+        ret = getopt(argc, argv, "t:p:h");
+
+        if (ret < 0) {
+            break;
+        }
+
+        switch(ret) {
+            case 't':
+                tag = optarg;
+            break;
+            
+            case 'p':
+                priority = filterCharToPri(optarg[0]);
+                if (priority == ANDROID_LOG_UNKNOWN) {
+                    usage(argv[0]);                    
+                }
+            break;
+
+            case 'h':
+                usage(argv[0]);
+            break;
+        }
+    }
+
+    if (optind == argc) {
+        usage(argv[0]);
+    }
+
+    buffer[0] = '\0';
+    
+    for (i = optind ; i < argc ; i++) {
+        strncat(buffer, argv[i], sizeof(buffer)-1);
+        strncat(buffer, " ", sizeof(buffer)-1);
+    }
+
+    if(buffer[0] == 0) {
+        usage(argv[0]);
+    }
+
+    __android_log_print(priority, tag, "%s", buffer);
+
+    return 0;
+}
+
diff --git a/toolbox/ls.c b/toolbox/ls.c
new file mode 100644
index 0000000..f609df2
--- /dev/null
+++ b/toolbox/ls.c
@@ -0,0 +1,285 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <pwd.h>
+#include <grp.h>
+
+#include <linux/kdev_t.h>
+
+// bits for flags argument
+#define LIST_LONG       (1 << 0)
+#define LIST_ALL        (1 << 1)
+#define LIST_RECURSIVE  (1 << 2)
+
+// fwd
+static int listpath(const char *name, int flags);
+
+static char mode2kind(unsigned mode)
+{
+    switch(mode & S_IFMT){
+    case S_IFSOCK: return 's';
+    case S_IFLNK: return 'l';
+    case S_IFREG: return '-';
+    case S_IFDIR: return 'd';
+    case S_IFBLK: return 'b';
+    case S_IFCHR: return 'c';
+    case S_IFIFO: return 'p';
+    default: return '?';
+    }
+}
+
+static void mode2str(unsigned mode, char *out)
+{
+    *out++ = mode2kind(mode);
+    
+    *out++ = (mode & 0400) ? 'r' : '-';
+    *out++ = (mode & 0200) ? 'w' : '-';
+    if(mode & 04000) {
+        *out++ = (mode & 0100) ? 's' : 'S';
+    } else {
+        *out++ = (mode & 0100) ? 'x' : '-';
+    }
+    *out++ = (mode & 040) ? 'r' : '-';
+    *out++ = (mode & 020) ? 'w' : '-';
+    if(mode & 02000) {
+        *out++ = (mode & 010) ? 's' : 'S';
+    } else {
+        *out++ = (mode & 010) ? 'x' : '-';
+    }
+    *out++ = (mode & 04) ? 'r' : '-';
+    *out++ = (mode & 02) ? 'w' : '-';
+    if(mode & 01000) {
+        *out++ = (mode & 01) ? 't' : 'T';
+    } else {
+        *out++ = (mode & 01) ? 'x' : '-';
+    }
+    *out = 0;
+}
+
+static void user2str(unsigned uid, char *out)
+{
+    struct passwd *pw = getpwuid(uid);
+    if(pw) {
+        strcpy(out, pw->pw_name);
+    } else {
+        sprintf(out, "%d", uid);
+    }
+}
+
+static void group2str(unsigned gid, char *out)
+{
+    struct group *gr = getgrgid(gid);
+    if(gr) {
+        strcpy(out, gr->gr_name);
+    } else {
+        sprintf(out, "%d", gid);
+    }
+}
+
+static int listfile(const char *path, int flags)
+{
+    struct stat s;
+    char date[32];
+    char mode[16];
+    char user[16];
+    char group[16];
+    const char *name;
+
+    /* name is anything after the final '/', or the whole path if none*/
+    name = strrchr(path, '/');
+    if(name == 0) {
+        name = path;
+    } else {
+        name++;
+    }
+
+    if(lstat(path, &s) < 0) {
+        return -1;
+    }
+
+    mode2str(s.st_mode, mode);
+    user2str(s.st_uid, user);
+    group2str(s.st_gid, group);
+
+    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
+    date[31] = 0;
+    
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
+
+    switch(s.st_mode & S_IFMT) {
+    case S_IFBLK:
+    case S_IFCHR:
+        printf("%s %-8s %-8s %3d, %3d %s %s\n",
+               mode, user, group, 
+               (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
+               date, name);
+        break;
+    case S_IFREG:
+        printf("%s %-8s %-8s %8d %s %s\n",
+               mode, user, group, (int) s.st_size, date, name);
+        break;
+    case S_IFLNK: {
+        char linkto[256];
+        int len;
+
+        len = readlink(path, linkto, 256);
+        if(len < 0) return -1;
+        
+        if(len > 255) {
+            linkto[252] = '.';
+            linkto[253] = '.';
+            linkto[254] = '.';
+            linkto[255] = 0;
+        } else {
+            linkto[len] = 0;
+        }
+        
+        printf("%s %-8s %-8s          %s %s -> %s\n",
+               mode, user, group, date, name, linkto);
+        break;
+    }
+    default:
+        printf("%s %-8s %-8s          %s %s\n",
+               mode, user, group, date, name);
+
+    }
+    return 0;
+}
+
+static int listdir(const char *name, int flags)
+{
+    char tmp[4096];
+    DIR *d;
+    struct dirent *de;
+    
+    d = opendir(name);
+    if(d == 0) {
+        fprintf(stderr, "opendir failed, %s\n", strerror(errno));
+        return -1;
+    }
+
+    while((de = readdir(d)) != 0){
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
+        if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
+        if ((flags & LIST_LONG) != 0) {
+            sprintf(tmp, "%s/%s", name, de->d_name);
+            listfile(tmp, flags);
+        } else {
+            printf("%s\n", de->d_name);
+        }
+    }
+
+    if (flags & LIST_RECURSIVE) {
+        rewinddir(d);
+
+        while ((de = readdir(d)) != 0) {
+            struct stat s;
+            int err;
+
+            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+                continue;
+            if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+                continue;
+
+            if (!strcmp(name, "/")) sprintf(tmp, "/%s", de->d_name);
+            else sprintf(tmp, "%s/%s", name, de->d_name);
+
+            /*
+             * If the name ends in a '/', use stat() so we treat it like a
+             * directory even if it's a symlink.
+             */
+            if (tmp[strlen(tmp)-1] == '/')
+                err = stat(tmp, &s);
+            else
+                err = lstat(tmp, &s);
+
+            if (err < 0) {
+                perror(tmp);
+                closedir(d);
+                return -1;
+            }
+
+            if (S_ISDIR(s.st_mode)) {
+                printf("\n%s:\n", tmp);
+                listdir(tmp, flags);
+            }
+        }
+    }
+
+    closedir(d);
+    return 0;
+}
+
+static int listpath(const char *name, int flags)
+{
+    struct stat s;
+    int err;
+
+    /*
+     * If the name ends in a '/', use stat() so we treat it like a
+     * directory even if it's a symlink.
+     */
+    if (name[strlen(name)-1] == '/')
+        err = stat(name, &s);
+    else
+        err = lstat(name, &s);
+
+    if (err < 0) {
+        perror(name);
+        return -1;
+    }
+
+    if (S_ISDIR(s.st_mode)) {
+        if (flags & LIST_RECURSIVE)
+            printf("\n%s:\n", name);
+        return listdir(name, flags);
+    } else {
+        if ((flags & LIST_LONG) != 0) {
+            /* yeah this calls stat() again*/
+            return listfile(name, flags);
+        } else {
+            printf("%s\n", name);
+            return 0;
+        }
+    }
+}
+
+int ls_main(int argc, char **argv)
+{
+    int flags = 0;
+    int listed = 0;
+    
+    if(argc > 1) {
+        int i;
+        int err = 0;
+
+        for (i = 1; i < argc; i++) {
+            if(!strcmp(argv[i], "-l")) {
+                flags |= LIST_LONG;
+            } else if (!strcmp(argv[i], "-a")) {
+                flags |= LIST_ALL;
+            } else if (!strcmp(argv[i], "-R")) {
+                flags |= LIST_RECURSIVE;
+            } else {
+                listed++;
+                if(listpath(argv[i], flags) != 0) {
+                    err = EXIT_FAILURE;
+                }
+            }
+        }
+
+        if (listed  > 0) return err;
+    }
+    
+    // list working directory if no files or directories were specified    
+    return listpath(".", flags);
+}
diff --git a/toolbox/lsmod.c b/toolbox/lsmod.c
new file mode 100644
index 0000000..8b55ee6
--- /dev/null
+++ b/toolbox/lsmod.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+extern int cat_main(int argc, char **argv);
+
+int lsmod_main(int argc, char **argv)
+{
+	char *cat_argv[] = { "cat", "/proc/modules", NULL };
+	return cat_main(2, cat_argv);
+}
+
diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c
new file mode 100644
index 0000000..121adab
--- /dev/null
+++ b/toolbox/mkdir.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+    fprintf(stderr,"mkdir <target>\n");
+    return -1;
+}
+
+int mkdir_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2) return usage();
+
+    while(argc > 1) {
+        argc--;
+        argv++;
+        ret = mkdir(argv[0], 0777);
+        if(ret < 0) {
+            fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno));
+            return ret;
+        }
+    }
+    
+    return 0;
+}
diff --git a/toolbox/mkdosfs.c b/toolbox/mkdosfs.c
new file mode 100644
index 0000000..744aad1
--- /dev/null
+++ b/toolbox/mkdosfs.c
@@ -0,0 +1,848 @@
+/*	$NetBSD: newfs_msdos.c,v 1.18.2.1 2005/05/01 18:44:02 tron Exp $	*/
+
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __USE_FILE_OFFSET64
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef __FreeBSD__
+#include <sys/diskslice.h>
+#endif
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __NetBSD__
+#include <disktab.h>
+#include <util.h>
+#endif
+
+#define MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
+#define BPN	  4		/* bits per nibble */
+#define NPB	  2		/* nibbles per byte */
+
+#define DOSMAGIC  0xaa55	/* DOS magic number */
+#define MINBPS	  128		/* minimum bytes per sector */
+#define MAXSPC	  128		/* maximum sectors per cluster */
+#define MAXNFT	  16		/* maximum number of FATs */
+#define DEFBLK	  4096		/* default block size */
+#define DEFBLK16  2048		/* default block size FAT16 */
+#define DEFRDE	  512		/* default root directory entries */
+#define RESFTE	  2		/* reserved FAT entries */
+#define MINCLS12  1		/* minimum FAT12 clusters */
+#define MINCLS16  0x1000	/* minimum FAT16 clusters */
+#define MINCLS32  2		/* minimum FAT32 clusters */
+#define MAXCLS12  0xfed 	/* maximum FAT12 clusters */
+#define MAXCLS16  0xfff5	/* maximum FAT16 clusters */
+#define MAXCLS32  0xffffff5	/* maximum FAT32 clusters */
+
+#define mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
+		      (fat) == 16 ? MINCLS16 :	\
+				    MINCLS32)
+
+#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
+		      (fat) == 16 ? MAXCLS16 :	\
+				    MAXCLS32)
+
+#define mk1(p, x)				\
+    (p) = (u_int8_t)(x)
+
+#define mk2(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010),		\
+    (p)[2] = (u_int8_t)((x) >> 020),		\
+    (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
+
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+static int powerof2(int x) {
+    int i;
+    for (i = 0; i < 32; i++) {
+        if (x & 1) {
+            x >>= 1;
+            // if x is zero, then original x was a power of two
+            return (x == 0);
+        }
+        x >>= 1;
+    }
+    
+    return 0;
+}
+
+#ifndef howmany
+#define   howmany(x, y)   (((x)+((y)-1))/(y))
+#endif
+ 
+#pragma pack(push, 1)
+struct bs {
+    u_int8_t jmp[3];		/* bootstrap entry point */
+    u_int8_t oem[8];		/* OEM name and version */
+};
+#define BS_SIZE 11
+
+struct bsbpb {
+    u_int8_t bps[2];		/* bytes per sector */
+    u_int8_t spc;		/* sectors per cluster */
+    u_int8_t res[2];		/* reserved sectors */
+    u_int8_t nft;		/* number of FATs */
+    u_int8_t rde[2];		/* root directory entries */
+    u_int8_t sec[2];		/* total sectors */
+    u_int8_t mid;		/* media descriptor */
+    u_int8_t spf[2];		/* sectors per FAT */
+    u_int8_t spt[2];		/* sectors per track */
+    u_int8_t hds[2];		/* drive heads */
+    u_int8_t hid[4];		/* hidden sectors */
+    u_int8_t bsec[4];		/* big total sectors */
+};
+#define BSBPB_SIZE 25
+
+struct bsxbpb {
+    u_int8_t bspf[4];		/* big sectors per FAT */
+    u_int8_t xflg[2];		/* FAT control flags */
+    u_int8_t vers[2];		/* file system version */
+    u_int8_t rdcl[4];		/* root directory start cluster */
+    u_int8_t infs[2];		/* file system info sector */
+    u_int8_t bkbs[2];		/* backup boot sector */
+    u_int8_t rsvd[12];		/* reserved */
+};
+#define BSXBPB_SIZE 28
+
+struct bsx {
+    u_int8_t drv;		/* drive number */
+    u_int8_t rsvd;		/* reserved */
+    u_int8_t sig;		/* extended boot signature */
+    u_int8_t volid[4];		/* volume ID number */
+    u_int8_t label[11]; 	/* volume label */
+    u_int8_t type[8];		/* file system type */
+};
+#define BSX_SIZE 26
+
+struct de {
+    u_int8_t namext[11];	/* name and extension */
+    u_int8_t attr;		/* attributes */
+    u_int8_t rsvd[10];		/* reserved */
+    u_int8_t time[2];		/* creation time */
+    u_int8_t date[2];		/* creation date */
+    u_int8_t clus[2];		/* starting cluster */
+    u_int8_t size[4];		/* size */
+#define DE_SIZE 32
+};
+#pragma pack(pop)
+
+struct bpb {
+    u_int bps;			/* bytes per sector */
+    u_int spc;			/* sectors per cluster */
+    u_int res;			/* reserved sectors */
+    u_int nft;			/* number of FATs */
+    u_int rde;			/* root directory entries */
+    u_int sec;			/* total sectors */
+    u_int mid;			/* media descriptor */
+    u_int spf;			/* sectors per FAT */
+    u_int spt;			/* sectors per track */
+    u_int hds;			/* drive heads */
+    u_int hid;			/* hidden sectors */
+    u_int bsec; 		/* big total sectors */
+    u_int bspf; 		/* big sectors per FAT */
+    u_int rdcl; 		/* root directory start cluster */
+    u_int infs; 		/* file system info sector */
+    u_int bkbs; 		/* backup boot sector */
+};
+
+static u_int8_t bootcode[] = {
+    0xfa,			/* cli		    */
+    0x31, 0xc0, 		/* xor	   ax,ax    */
+    0x8e, 0xd0, 		/* mov	   ss,ax    */
+    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
+    0xfb,			/* sti		    */
+    0x8e, 0xd8, 		/* mov	   ds,ax    */
+    0xe8, 0x00, 0x00,		/* call    $ + 3    */
+    0x5e,			/* pop	   si	    */
+    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
+    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
+    0xfc,			/* cld		    */
+    0xac,			/* lodsb	    */
+    0x84, 0xc0, 		/* test    al,al    */
+    0x74, 0x06, 		/* jz	   $ + 8    */
+    0xb4, 0x0e, 		/* mov	   ah,0eh   */
+    0xcd, 0x10, 		/* int	   10h	    */
+    0xeb, 0xf5, 		/* jmp	   $ - 9    */
+    0x30, 0xe4, 		/* xor	   ah,ah    */
+    0xcd, 0x16, 		/* int	   16h	    */
+    0xcd, 0x19, 		/* int	   19h	    */
+    0x0d, 0x0a,
+    'N', 'o', 'n', '-', 's', 'y', 's', 't',
+    'e', 'm', ' ', 'd', 'i', 's', 'k',
+    0x0d, 0x0a,
+    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+    ' ', 'r', 'e', 'b', 'o', 'o', 't',
+    0x0d, 0x0a,
+    0
+};
+
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(char* progname);
+
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+mkdosfs_main(int argc, char *argv[])
+{
+    static char opts[] = "NB:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+    static const char *opt_B, *opt_L, *opt_O;
+    static u_int opt_F, opt_I, opt_S, opt_a, opt_b, opt_c, opt_e;
+    static u_int opt_h, opt_i, opt_k, opt_m, opt_n, opt_o, opt_r;
+    static u_int opt_s, opt_u;
+    static int opt_N;
+    static int Iflag, mflag, oflag;
+    char buf[MAXPATHLEN];
+    struct stat sb;
+    struct timeval tv;
+    struct bpb bpb;
+    struct tm *tm;
+    struct bs *bs;
+    struct bsbpb *bsbpb;
+    struct bsxbpb *bsxbpb;
+    struct bsx *bsx;
+    struct de *de;
+    u_int8_t *img;
+    const char *fname, *dtype, *bname;
+    ssize_t n;
+    time_t now;
+    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+    int ch, fd, fd1;
+    char* progname = argv[0];
+
+    while ((ch = getopt(argc, argv, opts)) != -1)
+	switch (ch) {
+	case 'N':
+	    opt_N = 1;
+	    break;
+	case 'B':
+	    opt_B = optarg;
+	    break;
+	case 'F':
+	    if (strcmp(optarg, "12") &&
+		strcmp(optarg, "16") &&
+		strcmp(optarg, "32"))
+		fprintf(stderr, "%s: bad FAT type\n", optarg);
+	    opt_F = atoi(optarg);
+	    break;
+	case 'I':
+	    opt_I = argto4(optarg, 0, "volume ID");
+	    Iflag = 1;
+	    break;
+	case 'L':
+	    if (!oklabel(optarg))
+		fprintf(stderr, "%s: bad volume label\n", optarg);
+	    opt_L = optarg;
+	    break;
+	case 'O':
+	    if (strlen(optarg) > 8)
+		fprintf(stderr, "%s: bad OEM string\n", optarg);
+	    opt_O = optarg;
+	    break;
+	case 'S':
+	    opt_S = argto2(optarg, 1, "bytes/sector");
+	    break;
+	case 'a':
+	    opt_a = argto4(optarg, 1, "sectors/FAT");
+	    break;
+	case 'b':
+	    opt_b = argtox(optarg, 1, "block size");
+	    opt_c = 0;
+	    break;
+	case 'c':
+	    opt_c = argto1(optarg, 1, "sectors/cluster");
+	    opt_b = 0;
+	    break;
+	case 'e':
+	    opt_e = argto2(optarg, 1, "directory entries");
+	    break;
+	case 'h':
+	    opt_h = argto2(optarg, 1, "drive heads");
+	    break;
+	case 'i':
+	    opt_i = argto2(optarg, 1, "info sector");
+	    break;
+	case 'k':
+	    opt_k = argto2(optarg, 1, "backup sector");
+	    break;
+	case 'm':
+	    opt_m = argto1(optarg, 0, "media descriptor");
+	    mflag = 1;
+	    break;
+	case 'n':
+	    opt_n = argto1(optarg, 1, "number of FATs");
+	    break;
+	case 'o':
+	    opt_o = argto4(optarg, 0, "hidden sectors");
+	    oflag = 1;
+	    break;
+	case 'r':
+	    opt_r = argto2(optarg, 1, "reserved sectors");
+	    break;
+	case 's':
+	    opt_s = argto4(optarg, 1, "file system size");
+	    break;
+	case 'u':
+	    opt_u = argto2(optarg, 1, "sectors/track");
+	    break;
+	default:
+	    usage(progname);
+	}
+    argc -= optind;
+    argv += optind;
+    if (argc < 1 || argc > 2)
+	usage(progname);
+    fname = *argv++;
+    if (!strchr(fname, '/')) {
+	snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
+	if (!(fname = strdup(buf)))
+	    fprintf(stderr, NULL);
+    }
+    dtype = *argv;
+    if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1 ||
+	fstat(fd, &sb))
+	fprintf(stderr, "%s\n", fname);
+    memset(&bpb, 0, sizeof(bpb));
+
+    if (opt_h)
+	bpb.hds = opt_h;
+    if (opt_u)
+	bpb.spt = opt_u;
+    if (opt_S)
+	bpb.bps = opt_S;
+    if (opt_s)
+	bpb.bsec = opt_s;
+    if (oflag)
+	bpb.hid = opt_o;
+
+    bpb.bps = 512;  // 512 bytes/sector
+    bpb.spc = 8;    // 4K clusters
+    
+
+	fprintf(stderr, "opening %s\n", fname);
+	if ((fd1 = open(fname, O_RDONLY)) == -1) {
+	    fprintf(stderr, "failed to open %s\n", fname);
+	    exit(1);
+	}
+
+    lseek64(fd1, 0, SEEK_SET);
+    loff_t length = lseek64(fd1, 0, SEEK_END);
+    if (length > 0) {
+        bpb.bsec = length / bpb.bps;
+        bpb.spt = bpb.bsec;
+        // use FAT32 for 2 gig or greater 
+        if (length >= 2 *1024 *1024 *1024) {
+            fat = 32;
+        } else {
+            fat = 16;
+        }
+    }
+    close(fd1);
+    fd1 = -1;
+
+    if (!powerof2(bpb.bps))
+	fprintf(stderr, "bytes/sector (%u) is not a power of 2\n", bpb.bps);
+    if (bpb.bps < MINBPS)
+	fprintf(stderr, "bytes/sector (%u) is too small; minimum is %u\n",
+	     bpb.bps, MINBPS);
+
+    if (!(fat = opt_F)) {
+	if (!opt_e && (opt_i || opt_k))
+	    fat = 32;
+    }
+
+    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+	fprintf(stderr, "-%c is not a legal FAT%s option\n",
+	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+	     fat == 32 ? "32" : "12/16");
+    if (fat == 32)
+	bpb.rde = 0;
+    if (opt_b) {
+	if (!powerof2(opt_b))
+	    fprintf(stderr, "block size (%u) is not a power of 2\n", opt_b);
+	if (opt_b < bpb.bps)
+	    fprintf(stderr, "block size (%u) is too small; minimum is %u\n",
+		 opt_b, bpb.bps);
+	if (opt_b > bpb.bps * MAXSPC)
+	    fprintf(stderr, "block size (%u) is too large; maximum is %u\n",
+		 opt_b, bpb.bps * MAXSPC);
+	bpb.spc = opt_b / bpb.bps;
+    }
+    if (opt_c) {
+	if (!powerof2(opt_c))
+	    fprintf(stderr, "sectors/cluster (%u) is not a power of 2\n", opt_c);
+	bpb.spc = opt_c;
+    }
+    if (opt_r)
+	bpb.res = opt_r;
+    if (opt_n) {
+	if (opt_n > MAXNFT)
+	    fprintf(stderr, "number of FATs (%u) is too large; maximum is %u\n",
+		 opt_n, MAXNFT);
+	bpb.nft = opt_n;
+    }
+    if (opt_e)
+	bpb.rde = opt_e;
+    if (mflag) {
+	if (opt_m < 0xf0)
+	    fprintf(stderr, "illegal media descriptor (%#x)\n", opt_m);
+	bpb.mid = opt_m;
+    }
+    if (opt_a)
+	bpb.bspf = opt_a;
+    if (opt_i)
+	bpb.infs = opt_i;
+    if (opt_k)
+	bpb.bkbs = opt_k;
+    bss = 1;
+    bname = NULL;
+    fd1 = -1;
+    if (opt_B) {
+	bname = opt_B;
+	if (!strchr(bname, '/')) {
+	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
+	    if (!(bname = strdup(buf)))
+		fprintf(stderr, NULL);
+	}
+	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+	    fprintf(stderr, "%s", bname);
+	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+	    sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+	    fprintf(stderr, "%s: inappropriate file type or format\n", bname);
+	bss = sb.st_size / bpb.bps;
+    }
+    if (!bpb.nft)
+	bpb.nft = 2;
+    if (!fat) {
+	if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+	    howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+		    ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+	    bpb.nft +
+	    howmany(bpb.rde ? bpb.rde : DEFRDE,
+		    bpb.bps / DE_SIZE) +
+	    (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+	    (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+	    fat = 12;
+	else if (bpb.rde || bpb.bsec <
+		 (bpb.res ? bpb.res : bss) +
+		 howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+		 howmany(DEFRDE, bpb.bps / DE_SIZE) +
+		 (MAXCLS16 + 1) *
+		 (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+	    fat = 16;
+	else
+	    fat = 32;
+    }
+    x = bss;
+    if (fat == 32) {
+	if (!bpb.infs) {
+	    if (x == MAXU16 || x == bpb.bkbs)
+		fprintf(stderr, "no room for info sector\n");
+	    bpb.infs = x;
+	}
+	if (bpb.infs != MAXU16 && x <= bpb.infs)
+	    x = bpb.infs + 1;
+	if (!bpb.bkbs) {
+	    if (x == MAXU16)
+		fprintf(stderr, "no room for backup sector\n");
+	    bpb.bkbs = x;
+	} else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+	    fprintf(stderr, "backup sector would overwrite info sector\n");
+	if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+	    x = bpb.bkbs + 1;
+    }
+    if (!bpb.res)
+	bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+    else if (bpb.res < x)
+	fprintf(stderr, "too few reserved sectors (need %d have %d)\n", x, bpb.res);
+    if (fat != 32 && !bpb.rde)
+	bpb.rde = DEFRDE;
+    rds = howmany(bpb.rde, bpb.bps / DE_SIZE);
+    if (!bpb.spc)
+	for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+	     bpb.spc < MAXSPC &&
+	     bpb.res +
+	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+		     bpb.bps * NPB) * bpb.nft +
+	     rds +
+	     (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+	     bpb.spc <<= 1);
+    if (fat != 32 && bpb.bspf > MAXU16)
+	fprintf(stderr, "too many sectors/FAT for FAT12/16\n");
+    x1 = bpb.res + rds;
+    x = bpb.bspf ? bpb.bspf : 1;
+    if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+	fprintf(stderr, "meta data exceeds file system size\n");
+    x1 += x * bpb.nft;
+    x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+	(bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
+		 bpb.bps * NPB);
+    if (!bpb.bspf) {
+	bpb.bspf = x2;
+	x1 += (bpb.bspf - 1) * bpb.nft;
+    }
+    cls = (bpb.bsec - x1) / bpb.spc;
+    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+    if (cls > x)
+	cls = x;
+    if (bpb.bspf < x2)
+	fprintf(stderr, "warning: sectors/FAT limits file system to %u clusters\n",
+	      cls);
+    if (cls < mincls(fat))
+	fprintf(stderr, "%u clusters too few clusters for FAT%u, need %u\n", cls, fat,
+	    mincls(fat));
+    if (cls > maxcls(fat)) {
+	cls = maxcls(fat);
+	bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+	fprintf(stderr, "warning: FAT type limits file system to %u sectors\n",
+	      bpb.bsec);
+    }
+    printf("%s: %u sector%s in %u FAT%u cluster%s "
+	   "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+	   cls * bpb.spc == 1 ? "" : "s", cls, fat,
+	   cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+    if (!bpb.mid)
+	bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+    if (fat == 32)
+	bpb.rdcl = RESFTE;
+    if (bpb.hid + bpb.bsec <= MAXU16) {
+	bpb.sec = bpb.bsec;
+	bpb.bsec = 0;
+    }
+    if (fat != 32) {
+	bpb.spf = bpb.bspf;
+	bpb.bspf = 0;
+    }
+    ch = 0;
+    if (fat == 12)
+	ch = 1;			/* 001 Primary DOS with 12 bit FAT */
+    else if (fat == 16) {
+	if (bpb.bsec == 0)
+	    ch = 4;		/* 004 Primary DOS with 16 bit FAT <32M */
+	else
+	    ch = 6;		/* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */
+				/*
+				 * XXX: what about:
+				 * 014 DOS (16-bit FAT) - LBA
+				 *  ?
+				 */
+    } else if (fat == 32) {
+	ch = 11;		/* 011 Primary DOS with 32 bit FAT */
+				/*
+				 * XXX: what about:
+				 * 012 Primary DOS with 32 bit FAT - LBA
+				 *  ?
+				 */
+    }
+    if (ch != 0)
+	printf("MBR type: %d\n", ch);
+    print_bpb(&bpb);
+    if (!opt_N) {
+	gettimeofday(&tv, NULL);
+	now = tv.tv_sec;
+	tm = localtime(&now);
+	if (!(img = malloc(bpb.bps)))
+	    fprintf(stderr, NULL);
+	dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+
+	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+	    x = lsn;
+	    if (opt_B &&
+		fat == 32 && bpb.bkbs != MAXU16 &&
+		bss <= bpb.bkbs && x >= bpb.bkbs) {
+		x -= bpb.bkbs;
+		if (!x && lseek64(fd1, 0, SEEK_SET))
+		    fprintf(stderr, "lseek64 failed for %s\n", bname);
+	    }
+	    if (opt_B && x < bss) {
+		if ((n = read(fd1, img, bpb.bps)) == -1)
+		    fprintf(stderr, "%s\n", bname);
+		if (n != bpb.bps)
+		    fprintf(stderr, "%s: can't read sector %u\n", bname, x);
+	    } else
+		memset(img, 0, bpb.bps);
+	    if (!lsn ||
+	      (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+		x1 = BS_SIZE;
+		bsbpb = (struct bsbpb *)(img + x1);
+		mk2(bsbpb->bps, bpb.bps);
+		mk1(bsbpb->spc, bpb.spc);
+		mk2(bsbpb->res, bpb.res);
+		mk1(bsbpb->nft, bpb.nft);
+		mk2(bsbpb->rde, bpb.rde);
+		mk2(bsbpb->sec, bpb.sec);
+		mk1(bsbpb->mid, bpb.mid);
+		mk2(bsbpb->spf, bpb.spf);
+		mk2(bsbpb->spt, bpb.spt);
+		mk2(bsbpb->hds, bpb.hds);
+		mk4(bsbpb->hid, bpb.hid);
+		mk4(bsbpb->bsec, bpb.bsec);
+		x1 += BSBPB_SIZE;
+		if (fat == 32) {
+		    bsxbpb = (struct bsxbpb *)(img + x1);
+		    mk4(bsxbpb->bspf, bpb.bspf);
+		    mk2(bsxbpb->xflg, 0);
+		    mk2(bsxbpb->vers, 0);
+		    mk4(bsxbpb->rdcl, bpb.rdcl);
+		    mk2(bsxbpb->infs, bpb.infs);
+		    mk2(bsxbpb->bkbs, bpb.bkbs);
+		    x1 += BSXBPB_SIZE;
+		}
+		bsx = (struct bsx *)(img + x1);
+		mk1(bsx->sig, 0x29);
+		if (Iflag)
+		    x = opt_I;
+		else
+		    x = (((u_int)(1 + tm->tm_mon) << 8 |
+			  (u_int)tm->tm_mday) +
+			 ((u_int)tm->tm_sec << 8 |
+			  (u_int)(tv.tv_usec / 10))) << 16 |
+			((u_int)(1900 + tm->tm_year) +
+			 ((u_int)tm->tm_hour << 8 |
+			  (u_int)tm->tm_min));
+		mk4(bsx->volid, x);
+		mklabel(bsx->label, opt_L ? opt_L : "NO_NAME");
+		snprintf(buf, sizeof(buf), "FAT%u", fat);
+		setstr(bsx->type, buf, sizeof(bsx->type));
+		if (!opt_B) {
+		    x1 += BSX_SIZE;
+		    bs = (struct bs *)img;
+		    mk1(bs->jmp[0], 0xeb);
+		    mk1(bs->jmp[1], x1 - 2);
+		    mk1(bs->jmp[2], 0x90);
+		    setstr(bs->oem, opt_O ? opt_O : "NetBSD",
+			   sizeof(bs->oem));
+		    memcpy(img + x1, bootcode, sizeof(bootcode));
+		    mk2(img + bpb.bps - 2, DOSMAGIC);
+		}
+	    } else if (fat == 32 && bpb.infs != MAXU16 &&
+		       (lsn == bpb.infs ||
+			(bpb.bkbs != MAXU16 &&
+			 lsn == bpb.bkbs + bpb.infs))) {
+		mk4(img, 0x41615252);
+		mk4(img + bpb.bps - 28, 0x61417272);
+		mk4(img + bpb.bps - 24, 0xffffffff);
+		mk4(img + bpb.bps - 20, bpb.rdcl);
+		mk2(img + bpb.bps - 2, DOSMAGIC);
+	    } else if (lsn >= bpb.res && lsn < dir &&
+		       !((lsn - bpb.res) %
+			 (bpb.spf ? bpb.spf : bpb.bspf))) {
+		mk1(img[0], bpb.mid);
+		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+	    } else if (lsn == dir && opt_L) {
+		de = (struct de *)img;
+		mklabel(de->namext, opt_L);
+		mk1(de->attr, 050);
+		x = (u_int)tm->tm_hour << 11 |
+		    (u_int)tm->tm_min << 5 |
+		    (u_int)tm->tm_sec >> 1;
+		mk2(de->time, x);
+		x = (u_int)(tm->tm_year - 80) << 9 |
+		    (u_int)(tm->tm_mon + 1) << 5 |
+		    (u_int)tm->tm_mday;
+		mk2(de->date, x);
+	    }
+	    if ((n = write(fd, img, bpb.bps)) == -1)
+		fprintf(stderr, "%s\n", fname);
+	    if (n != bpb.bps)
+		fprintf(stderr, "%s: can't write sector %u\n", fname, lsn);
+	}
+    }
+    return 0;
+}
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+	   bpb->nft);
+    if (bpb->rde)
+	printf(" rde=%u", bpb->rde);
+    if (bpb->sec)
+	printf(" sec=%u", bpb->sec);
+    printf(" mid=%#x", bpb->mid);
+    if (bpb->spf)
+	printf(" spf=%u", bpb->spf);
+    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+    if (bpb->bsec)
+	printf(" bsec=%u", bpb->bsec);
+    if (!bpb->spf) {
+	printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+	printf(" infs=");
+	printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+	printf(" bkbs=");
+	printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+    }
+    printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+    if (!val)
+	fprintf(stderr, "%s: no default %s\n", fname, msg);
+    if (val > MAXU16)
+	fprintf(stderr, "%s: illegal %s\n", fname, msg);
+    return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+    char *s;
+    u_long x;
+
+    errno = 0;
+    x = strtoul(arg, &s, 0);
+    if (errno || !*arg || *s || x < lo || x > hi)
+	fprintf(stderr, "%s: bad %s\n", arg, msg);
+    return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+    int c, i;
+
+    for (i = 0; i <= 11; i++) {
+	c = (u_char)*src++;
+	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+	    break;
+    }
+    return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+    int c, i;
+
+    for (i = 0; i < 11; i++) {
+	c = *src ? toupper((unsigned char)*src++) : ' ';
+	*dest++ = !i && c == '\xe5' ? 5 : c;
+    }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+    while (len--)
+	*dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(char* progname)
+{
+    fprintf(stderr,
+	    "usage: %s [ -options ] special [disktype]\n", progname);
+    fprintf(stderr, "where the options are:\n");
+    fprintf(stderr, "\t-N don't create file system: "
+	    "just print out parameters\n");
+    fprintf(stderr, "\t-B get bootstrap from file\n");
+    fprintf(stderr, "\t-F FAT type (12, 16, or 32)\n");
+    fprintf(stderr, "\t-I volume ID\n");
+    fprintf(stderr, "\t-L volume label\n");
+    fprintf(stderr, "\t-O OEM string\n");
+    fprintf(stderr, "\t-S bytes/sector\n");
+    fprintf(stderr, "\t-a sectors/FAT\n");
+    fprintf(stderr, "\t-b block size\n");
+    fprintf(stderr, "\t-c sectors/cluster\n");
+    fprintf(stderr, "\t-e root directory entries\n");
+    fprintf(stderr, "\t-h drive heads\n");
+    fprintf(stderr, "\t-i file system info sector\n");
+    fprintf(stderr, "\t-k backup boot sector\n");
+    fprintf(stderr, "\t-m media descriptor\n");
+    fprintf(stderr, "\t-n number of FATs\n");
+    fprintf(stderr, "\t-o hidden sectors\n");
+    fprintf(stderr, "\t-r reserved sectors\n");
+    fprintf(stderr, "\t-s file system size (sectors)\n");
+    fprintf(stderr, "\t-u sectors/track\n");
+    exit(1);
+}
+
+
diff --git a/toolbox/mount.c b/toolbox/mount.c
new file mode 100644
index 0000000..ef13e1f
--- /dev/null
+++ b/toolbox/mount.c
@@ -0,0 +1,273 @@
+/*
+ * mount.c, by rmk
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/loop.h>
+
+#define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0]))
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+struct mount_opts {
+	const char str[8];
+	unsigned long rwmask;
+	unsigned long rwset;
+	unsigned long rwnoset;
+};
+
+struct extra_opts {
+	char *str;
+	char *end;
+	int used_size;
+	int alloc_size;
+};
+
+/*
+ * These options define the function of "mount(2)".
+ */
+#define MS_TYPE	(MS_REMOUNT|MS_BIND|MS_MOVE)
+
+
+static const struct mount_opts options[] = {
+	/* name		mask		set		noset		*/
+	{ "async",	MS_SYNCHRONOUS,	0,		MS_SYNCHRONOUS	},
+	{ "atime",	MS_NOATIME,	0,		MS_NOATIME	},
+	{ "bind",	MS_TYPE,	MS_BIND,	0,		},
+	{ "dev",	MS_NODEV,	0,		MS_NODEV	},
+	{ "diratime",	MS_NODIRATIME,	0,		MS_NODIRATIME	},
+	{ "dirsync",	MS_DIRSYNC,	MS_DIRSYNC,	0		},
+	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	},
+	{ "move",	MS_TYPE,	MS_MOVE,	0		},
+	{ "recurse",	MS_REC,		MS_REC,		0		},
+	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		},
+	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		},
+	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	},
+	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	},
+	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		},
+	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		},
+};
+
+static void add_extra_option(struct extra_opts *extra, char *s)
+{
+	int len = strlen(s);
+	int newlen = extra->used_size + len;
+
+	if (extra->str)
+	       len++;			/* +1 for ',' */
+
+	if (newlen >= extra->alloc_size) {
+		char *new;
+
+		new = realloc(extra->str, newlen + 1);	/* +1 for NUL */
+		if (!new)
+			return;
+
+		extra->str = new;
+		extra->end = extra->str + extra->used_size;
+		extra->alloc_size = newlen;
+	}
+
+	if (extra->used_size) {
+		*extra->end = ',';
+		extra->end++;
+	}
+	strcpy(extra->end, s);
+	extra->used_size += len;
+
+}
+
+static unsigned long
+parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop)
+{
+	char *s;
+    
+    *loop = 0;
+	while ((s = strsep(&arg, ",")) != NULL) {
+		char *opt = s;
+		unsigned int i;
+		int res, no = s[0] == 'n' && s[1] == 'o';
+
+		if (no)
+			s += 2;
+
+        if (strcmp(s, "loop") == 0) {
+            *loop = 1;
+            continue;
+        }
+		for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
+			res = strcmp(s, options[i].str);
+
+			if (res == 0) {
+				rwflag &= ~options[i].rwmask;
+				if (no)
+					rwflag |= options[i].rwnoset;
+				else
+					rwflag |= options[i].rwset;
+			}
+			if (res <= 0)
+				break;
+		}
+
+		if (res != 0 && s[0])
+			add_extra_option(extra, opt);
+	}
+
+	return rwflag;
+}
+
+static char *progname;
+
+static struct extra_opts extra;
+static unsigned long rwflag;
+
+static int
+do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop)
+{
+	char *s;
+	int error = 0;
+
+    if (loop) {
+        int file_fd, device_fd;
+        
+        // FIXME - only one loop mount supported at a time
+        file_fd = open(dev, O_RDWR);
+        if (file_fd < -1) {
+            perror("open backing file failed");
+            return 1;
+        }
+        device_fd = open(LOOP_DEVICE, O_RDWR);
+        if (device_fd < -1) {
+            perror("open loop device failed");
+            close(file_fd);
+            return 1;
+        }
+        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
+            perror("ioctl LOOP_SET_FD failed");
+            close(file_fd);
+            close(device_fd);
+            return 1;
+        }
+
+        close(file_fd);
+        close(device_fd);
+        dev = LOOP_DEVICE;
+    }
+
+	while ((s = strsep(&type, ",")) != NULL) {
+retry:
+		if (mount(dev, dir, s, rwflag, data) == -1) {
+			error = errno;
+			/*
+			 * If the filesystem is not found, or the
+			 * superblock is invalid, try the next.
+			 */
+			if (error == ENODEV || error == EINVAL)
+				continue;
+
+			/*
+			 * If we get EACCESS, and we're trying to
+			 * mount readwrite and this isn't a remount,
+			 * try read only.
+			 */
+			if (error == EACCES &&
+			    (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
+				rwflag |= MS_RDONLY;
+				goto retry;
+			}
+			break;
+		}
+	}
+
+	if (error) {
+		errno = error;
+		perror("mount");
+		return 255;
+	}
+
+	return 0;
+}
+
+static int print_mounts()
+{
+    FILE* f;
+    int length;
+    char buffer[100];
+    
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        fprintf(stdout, "could not open /proc/mounts\n");
+        return -1;
+    }
+
+    do {
+        length = fread(buffer, 1, 100, f);
+        if (length > 0)
+            fwrite(buffer, 1, length, stdout);
+    } while (length > 0);
+
+    fclose(f);
+    return 0;
+}
+
+int mount_main(int argc, char *argv[])
+{
+	char *type = NULL;
+	int c;
+	int loop;
+
+	progname = argv[0];
+	rwflag = MS_VERBOSE;
+	
+	// mount with no arguments is equivalent to "cat /proc/mounts"
+	if (argc == 1) return print_mounts();
+
+	do {
+		c = getopt(argc, argv, "o:rt:w");
+		if (c == EOF)
+			break;
+		switch (c) {
+		case 'o':
+			rwflag = parse_mount_options(optarg, rwflag, &extra, &loop);
+			break;
+		case 'r':
+			rwflag |= MS_RDONLY;
+			break;
+		case 't':
+			type = optarg;
+			break;
+		case 'w':
+			rwflag &= ~MS_RDONLY;
+			break;
+		case '?':
+			fprintf(stderr, "%s: invalid option -%c\n",
+				progname, optopt);
+			exit(1);
+		}
+	} while (1);
+
+	/*
+	 * If remount, bind or move was specified, then we don't
+	 * have a "type" as such.  Use the dummy "none" type.
+	 */
+	if (rwflag & MS_TYPE)
+		type = "none";
+
+	if (optind + 2 != argc || type == NULL) {
+		fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
+			"device directory\n", progname);
+		exit(1);
+	}
+
+	return do_mount(argv[optind], argv[optind + 1], type, rwflag,
+		        extra.str, loop);
+}
diff --git a/toolbox/mv.c b/toolbox/mv.c
new file mode 100644
index 0000000..a5bc225
--- /dev/null
+++ b/toolbox/mv.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+int mv_main(int argc, char *argv[])
+{
+    const char* dest;
+    struct stat st;
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr,"USAGE: %s <source...> <destination>\n", argv[0]);
+        return -1;
+    }
+
+    /* check if destination exists */
+    dest = argv[argc - 1];
+    if (stat(dest, &st)) {
+        /* an error, unless the destination was missing */
+        if (errno != ENOENT) {
+            fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno));
+            return -1;
+        }
+        st.st_mode = 0;
+    }
+
+    for (i = 1; i < argc - 1; i++) {
+        const char *source = argv[i];
+        char fullDest[PATH_MAX + 1 + PATH_MAX + 1];
+        /* assume we build "dest/source", and let rename() fail on pathsize */
+        if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) {
+            fprintf(stderr, "path too long\n");
+            return -1;
+        }
+        strcpy(fullDest, dest);
+
+        /* if destination is a directory, concat the source file name */
+        if (S_ISDIR(st.st_mode)) {
+            const char *fileName = strrchr(source, '/');
+            if (fullDest[strlen(fullDest)-1] != '/') {
+                strcat(fullDest, "/");
+            }
+            strcat(fullDest, fileName ? fileName + 1 : source);
+        }
+
+        /* attempt to move it */
+        if (rename(source, fullDest)) {
+            fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno));
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/netstat.c b/toolbox/netstat.c
new file mode 100644
index 0000000..6404309
--- /dev/null
+++ b/toolbox/netstat.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef union iaddr iaddr;
+
+union iaddr {
+    unsigned u;
+    unsigned char b[4];
+};
+    
+static const char *state2str(unsigned state)
+{
+    switch(state){
+    case 0x1: return "ESTABLISHED";
+    case 0x2: return "SYN_SENT";
+    case 0x3: return "SYN_RECV";
+    case 0x4: return "FIN_WAIT1";
+    case 0x5: return "FIN_WAIT2";
+    case 0x6: return "TIME_WAIT";
+    case 0x7: return "CLOSE";
+    case 0x8: return "CLOSE_WAIT";
+    case 0x9: return "LAST_ACK";
+    case 0xA: return "LISTEN";
+    case 0xB: return "CLOSING";
+    default: return "UNKNOWN";
+    }
+}
+
+void addr2str(iaddr addr, unsigned port, char *buf)
+{
+    if(port) {
+        snprintf(buf, 64, "%d.%d.%d.%d:%d",
+                 addr.b[0], addr.b[1], addr.b[2], addr.b[3], port);
+    } else {
+        snprintf(buf, 64, "%d.%d.%d.%d:*",
+                 addr.b[0], addr.b[1], addr.b[2], addr.b[3]);
+    }
+}
+
+int netstat_main(int argc, char *argv[])
+{
+    char buf[512];
+    char lip[64];
+    char rip[64];
+    iaddr laddr, raddr;
+    unsigned lport, rport, state, txq, rxq, num;
+    int n;
+    FILE *fp;
+
+    printf("Proto Recv-Q Send-Q Local Address          Foreign Address        State\n");
+
+    fp = fopen("/proc/net/tcp", "r");
+    if(fp != 0) {
+        fgets(buf, 512, fp);
+        while(fgets(buf, 512, fp)){
+            n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x",
+                       &num, &laddr.u, &lport, &raddr.u, &rport,
+                       &state, &txq, &rxq);
+            if(n == 8) {
+                addr2str(laddr, lport, lip);
+                addr2str(raddr, rport, rip);
+                
+                printf("tcp   %6d %6d %-22s %-22s %s\n", 
+                       txq, rxq, lip, rip,
+                       state2str(state));
+            }
+        }
+        fclose(fp);
+    }
+    fp = fopen("/proc/net/udp", "r");
+    if(fp != 0) {
+        fgets(buf, 512, fp);
+        while(fgets(buf, 512, fp)){
+            n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x",
+                       &num, &laddr.u, &lport, &raddr.u, &rport,
+                       &state, &txq, &rxq);
+            if(n == 8) {
+                addr2str(laddr, lport, lip);
+                addr2str(raddr, rport, rip);
+                
+                printf("udp   %6d %6d %-22s %-22s\n", 
+                       txq, rxq, lip, rip);
+            }
+        }
+        fclose(fp);
+    }
+
+    return 0;
+}
diff --git a/toolbox/notify.c b/toolbox/notify.c
new file mode 100644
index 0000000..b1761d2
--- /dev/null
+++ b/toolbox/notify.c
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <errno.h>
+
+int notify_main(int argc, char *argv[])
+{
+    int c;
+    int nfd, ffd;
+    int res;
+	char event_buf[512];
+    struct inotify_event *event;
+	int event_mask = IN_ALL_EVENTS;
+    int event_count = 1;
+	int print_files = 0;
+	int verbose = 2;
+	int width = 80;
+	char **file_names;
+	int file_count;
+	int id_offset = 0;
+	int i;
+	char *buf;
+
+    do {
+        c = getopt(argc, argv, "m:c:pv:w:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'm':
+            event_mask = strtol(optarg, NULL, 0);
+            break;
+        case 'c':
+            event_count = atoi(optarg);
+            break;
+		case 'p':
+			print_files = 1;
+			break;
+        case 'v':
+            verbose = atoi(optarg);
+            break;
+        case 'w':
+            width = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (argc <= optind) {
+        fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]);
+		return 1;
+    }
+
+    nfd = inotify_init();
+    if(nfd < 0) {
+        fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+        return 1;
+    }
+	file_names = argv + optind;
+	file_count = argc - optind;
+	for(i = 0; i < file_count; i++) {
+		res = inotify_add_watch(nfd, file_names[i], event_mask);
+		if(res < 0) {
+	        fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno));
+			return 1;
+		}
+		if(i == 0)
+			id_offset = -res;
+		if(res + id_offset != i) {
+			fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i);
+			return 1;
+		}
+	}
+
+	buf = malloc(width + 2);
+    
+    while(1) {
+		int event_pos = 0;
+        res = read(nfd, event_buf, sizeof(event_buf));
+        if(res < (int)sizeof(*event)) {
+			if(errno == EINTR)
+				continue;
+            fprintf(stderr, "could not get event, %s\n", strerror(errno));
+            return 1;
+        }
+		//printf("got %d bytes of event information\n", res);
+		while(res >= (int)sizeof(*event)) {
+			int event_size;
+			event = (struct inotify_event *)(event_buf + event_pos);
+			if(verbose >= 2)
+		        printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : "");
+			else if(verbose >= 2)
+		        printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : "");
+			else if(verbose >= 1)
+		        printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+			if(print_files && (event->mask & IN_MODIFY)) {
+				char filename[512];
+				ssize_t read_len;
+				char *display_name;
+				int buflen;
+				strcpy(filename, file_names[event->wd + id_offset]);
+				if(event->len) {
+					strcat(filename, "/");
+					strcat(filename, event->name);
+				}
+				ffd = open(filename, O_RDONLY);
+				display_name = (verbose >= 2 || event->len == 0) ? filename : event->name;
+				buflen = width - strlen(display_name);
+				read_len = read(ffd, buf, buflen);
+				if(read_len > 0) {
+					if(read_len < buflen && buf[read_len-1] != '\n') {
+						buf[read_len] = '\n';
+						read_len++;
+					}
+					if(read_len == buflen) {
+						buf[--read_len] = '\0';
+						buf[--read_len] = '\n';
+						buf[--read_len] = '.';
+						buf[--read_len] = '.';
+						buf[--read_len] = '.';
+					}
+					else {
+						buf[read_len] = '\0';
+					}
+					printf("%s: %s", display_name, buf);
+				}
+				close(ffd);
+			}
+	        if(event_count && --event_count == 0)
+	            return 0;
+			event_size = sizeof(*event) + event->len;
+			res -= event_size;
+			event_pos += event_size;
+		}
+    }
+
+    return 0;
+}
diff --git a/toolbox/powerd.c b/toolbox/powerd.c
new file mode 100644
index 0000000..1f29a8b
--- /dev/null
+++ b/toolbox/powerd.c
@@ -0,0 +1,441 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+//#include <linux/input.h> // this does not compile
+
+// from <linux/input.h>
+
+struct input_event {
+	struct timeval time;
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+#define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */
+#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
+#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
+#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */
+
+#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
+#define EVIOCGPHYS(len)		_IOC(_IOC_READ, 'E', 0x07, len)		/* get physical location */
+#define EVIOCGUNIQ(len)		_IOC(_IOC_READ, 'E', 0x08, len)		/* get unique identifier */
+
+#define EVIOCGKEY(len)		_IOC(_IOC_READ, 'E', 0x18, len)		/* get global keystate */
+#define EVIOCGLED(len)		_IOC(_IOC_READ, 'E', 0x19, len)		/* get all LEDs */
+#define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
+#define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */
+
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
+#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */
+
+#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
+#define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
+#define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
+
+#define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
+
+/*
+ * Event types
+ */
+
+#define EV_SYN			0x00
+#define EV_KEY			0x01
+#define EV_REL			0x02
+#define EV_ABS			0x03
+#define EV_MSC			0x04
+#define EV_SW			0x05
+#define EV_LED			0x11
+#define EV_SND			0x12
+#define EV_REP			0x14
+#define EV_FF			0x15
+#define EV_PWR			0x16
+#define EV_FF_STATUS		0x17
+#define EV_MAX			0x1f
+
+#define KEY_POWER		116
+#define KEY_SLEEP		142
+#define SW_0		0x00
+
+// end <linux/input.h>
+
+struct notify_entry {
+    int id;
+    int (*handler)(struct notify_entry *entry, struct inotify_event *event);
+    const char *filename;
+};
+
+int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event)
+{
+    static int state = -1;
+    int last_state;
+    char buf[40];
+    int read_len;
+    int fd;
+
+    last_state = state;
+    fd = open(entry->filename, O_RDONLY);
+    read_len = read(fd, buf, sizeof(buf));
+    if(read_len > 0) {
+        //printf("charging_state_notify_handler: \"%s\"\n", buf);
+        state = !(strncmp(buf, "Unknown", 7) == 0 
+                  || strncmp(buf, "Discharging", 11) == 0);
+    }
+    close(fd);
+    //printf("charging_state_notify_handler: %d -> %d\n", last_state, state);
+    return state > last_state;
+}
+
+struct notify_entry watched_files[] = {
+    {
+        .filename = "/sys/android_power/charging_state",
+        .handler = charging_state_notify_handler
+    }
+};
+
+int call_notify_handler(struct inotify_event *event)
+{
+    unsigned int start, i;
+    start = event->wd - watched_files[0].id;
+    if(start >= ARRAY_SIZE(watched_files))
+        start = 0;
+    //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+    for(i = start; i < ARRAY_SIZE(watched_files); i++) {
+        if(event->wd == watched_files[i].id) {
+            if(watched_files[i].handler) {
+                return watched_files[i].handler(&watched_files[i], event);
+            }
+            return 1;
+        }
+    }
+    for(i = 0; i < start; i++) {
+        if(event->wd == watched_files[i].id) {
+            if(watched_files[i].handler) {
+                return watched_files[i].handler(&watched_files[i], event);
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int handle_inotify_event(int nfd)
+{
+    int res;
+    int wake_up = 0;
+    struct inotify_event *event;
+    char event_buf[512];
+    int event_pos = 0;
+
+    res = read(nfd, event_buf, sizeof(event_buf));
+    if(res < (int)sizeof(*event)) {
+        if(errno == EINTR)
+            return 0;
+        fprintf(stderr, "could not get event, %s\n", strerror(errno));
+        return 0;
+    }
+    printf("got %d bytes of event information\n", res);
+    while(res >= (int)sizeof(*event)) {
+        int event_size;
+        event = (struct inotify_event *)(event_buf + event_pos);
+        wake_up |= call_notify_handler(event);
+        event_size = sizeof(*event) + event->len;
+        res -= event_size;
+        event_pos += event_size;
+    }
+    return wake_up;
+}
+
+int powerd_main(int argc, char *argv[])
+{
+    int c;
+    unsigned int i;
+    int res;
+    struct timeval tv;
+    int eventfd;
+    int notifyfd;
+    int powerfd;
+    int powerfd_is_sleep;
+    int user_activity_fd;
+    int acquire_partial_wake_lock_fd;
+    int acquire_full_wake_lock_fd;
+    int release_wake_lock_fd;
+    char *eventdev = "/dev/input/event0";
+    const char *android_sleepdev = "/sys/android_power/request_sleep";
+    const char *android_autooff_dev = "/sys/android_power/auto_off_timeout";
+    const char *android_user_activity_dev = "/sys/android_power/last_user_activity";
+    const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock";
+    const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock";
+    const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock";
+    const char *powerdev = "/sys/power/state";
+    const char suspendstring[] = "standby";
+    const char wakelockstring[] = "powerd";
+    fd_set rfds;
+    struct input_event event;
+    struct input_event light_event;
+    struct input_event light_event2;
+    int gotkey = 1;
+    time_t idle_time = 5;
+    const char *idle_time_string = "5";
+    time_t lcd_light_time = 0;
+    time_t key_light_time = 0;
+    int verbose = 1;
+    int event_sleep = 0;
+    int got_power_key_down = 0;
+    struct timeval power_key_down_time = { 0, 0 };
+
+    light_event.type = EV_LED;
+    light_event.code = 4; // bright lcd backlight
+    light_event.value = 0; // light off -- sleep after timeout
+
+    light_event2.type = EV_LED;
+    light_event2.code = 8; // keyboard backlight
+    light_event2.value = 0; // light off -- sleep after timeout
+
+    do {
+        c = getopt(argc, argv, "e:ni:vql:k:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'e':
+            eventdev = optarg;
+            break;
+        case 'n':
+            gotkey = 0;
+            break;
+        case 'i':
+            idle_time = atoi(optarg);
+            idle_time_string = optarg;
+            break;
+        case 'v':
+            verbose = 2;
+            break;
+        case 'q':
+            verbose = 0;
+            break;
+        case 'l':
+            lcd_light_time = atoi(optarg);
+            break;
+        case 'k':
+            key_light_time = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(optind  != argc) {
+        fprintf(stderr,"%s [-e eventdev]\n", argv[0]);
+        return 1;
+    }
+
+    eventfd = open(eventdev, O_RDWR | O_NONBLOCK);
+    if(eventfd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno));
+        return 1;
+    }
+    if(key_light_time >= lcd_light_time) {
+        lcd_light_time = key_light_time + 1;
+        fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n"
+            "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time);
+    }
+
+    user_activity_fd = open(android_user_activity_dev, O_RDWR);
+    if(user_activity_fd >= 0) {
+        int auto_off_fd = open(android_autooff_dev, O_RDWR);
+        write(auto_off_fd, idle_time_string, strlen(idle_time_string));
+        close(auto_off_fd);
+    }
+
+    powerfd = open(android_sleepdev, O_RDWR);
+    if(powerfd >= 0) {
+        powerfd_is_sleep = 1;
+        if(verbose > 0)
+            printf("Using android sleep dev: %s\n", android_sleepdev);
+    }
+    else {
+        powerfd_is_sleep = 0;
+        powerfd = open(powerdev, O_RDWR);
+        if(powerfd >= 0) {
+            if(verbose > 0)
+                printf("Using linux power dev: %s\n", powerdev);
+        }
+    }
+    if(powerfd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno));
+        return 1;
+    }
+
+    notifyfd = inotify_init();
+    if(notifyfd < 0) {
+        fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+        return 1;
+    }
+    fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL));
+    for(i = 0; i < ARRAY_SIZE(watched_files); i++) {
+        watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY);
+        printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id);
+    }
+
+    acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR);
+    acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR);
+    release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR);
+
+    if(user_activity_fd >= 0) {
+        idle_time = 60*60*24; // driver handles real timeout
+    }
+    if(gotkey) {
+        tv.tv_sec = idle_time;
+        tv.tv_usec = 0;
+    }
+    else {
+        tv.tv_sec = 0;
+        tv.tv_usec = 500000;
+    }
+    
+    while(1) {
+        FD_ZERO(&rfds);
+        //FD_SET(0, &rfds);
+        FD_SET(eventfd, &rfds);
+        FD_SET(notifyfd, &rfds);
+        res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv);
+        if(res < 0) {
+            fprintf(stderr, "select failed, %s\n", strerror(errno));
+            return 1;
+        }
+        if(res == 0) {
+            if(light_event2.value == 1)
+                goto light2_off;
+            if(light_event.value == 1)
+                goto light_off;
+            if(user_activity_fd < 0) {
+                if(gotkey && verbose > 0)
+                    printf("Idle - sleep\n");
+                if(!gotkey && verbose > 1)
+                    printf("Reenter sleep\n");
+                goto sleep;
+            }
+            else {
+                tv.tv_sec = 60*60*24;
+                tv.tv_usec = 0;
+            }
+        }
+        if(res > 0) {
+            //if(FD_ISSET(0, &rfds)) {
+            //  printf("goto data on stdin quit\n");
+            //  return 0;
+            //}
+            if(FD_ISSET(notifyfd, &rfds)) {
+                write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                if(handle_inotify_event(notifyfd) > 0) {
+                    write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                }
+                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+            }
+            if(FD_ISSET(eventfd, &rfds)) {
+                write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                res = read(eventfd, &event, sizeof(event));
+                if(res < (int)sizeof(event)) {
+                    fprintf(stderr, "could not get event\n");
+                    write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                    return 1;
+                }
+                if(event.type == EV_PWR && event.code == KEY_SLEEP) {
+                    event_sleep = event.value;
+                }
+                if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) {
+                    gotkey = 1;
+                    if(user_activity_fd >= 0) {
+                        char buf[32];
+                        int len;
+                        len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec);
+                        write(user_activity_fd, buf, len);
+                    }
+                    if(lcd_light_time | key_light_time) {
+                        tv.tv_sec = key_light_time;
+                        light_event.value = 1;
+                        write(eventfd, &light_event, sizeof(light_event));
+                        light_event2.value = 1;
+                        write(eventfd, &light_event2, sizeof(light_event2));
+                    }
+                    else {
+                        tv.tv_sec = idle_time;
+                    }
+                    tv.tv_usec = 0;
+                    if(verbose > 1)
+                        printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : "");
+                    if(event.code == KEY_POWER) {
+                        if(event.value == 0) {
+                            int tmp_got_power_key_down = got_power_key_down;
+                            got_power_key_down = 0;
+                            if(tmp_got_power_key_down) {
+                                // power key released
+                                if(verbose > 0)
+                                    printf("Power key released - sleep\n");
+                                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                                goto sleep;
+                            }
+                        }
+                        else if(event_sleep == 0) {
+                            got_power_key_down = 1;
+                            power_key_down_time = event.time;
+                        }
+                    }
+                }
+                if(event.type == EV_SW && event.code == SW_0 && event.value == 0) {
+                    if(verbose > 0)
+                        printf("Flip closed - sleep\n");
+                    power_key_down_time = event.time;
+                    write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                    goto sleep;
+                }
+                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+            }
+        }
+        if(0) {
+light_off:
+            light_event.value = 0;
+            write(eventfd, &light_event, sizeof(light_event));
+            tv.tv_sec = idle_time - lcd_light_time;
+        }
+        if(0) {
+light2_off:
+            light_event2.value = 0;
+            write(eventfd, &light_event2, sizeof(light_event2));
+            tv.tv_sec = lcd_light_time - key_light_time;
+        }
+        if(0) {
+sleep:
+            if(light_event.value == 1) {
+                light_event.value = 0;
+                write(eventfd, &light_event, sizeof(light_event));
+                light_event2.value = 0;
+                write(eventfd, &light_event2, sizeof(light_event2));
+                tv.tv_sec = idle_time - lcd_light_time;
+            }
+            if(powerfd_is_sleep) {
+                char buf[32];
+                int len;
+                len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec);
+                write(powerfd, buf, len);
+            }
+            else
+                write(powerfd, suspendstring, sizeof(suspendstring) - 1);
+            gotkey = 0;
+            tv.tv_sec = 0;
+            tv.tv_usec = 500000;
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/printenv.c b/toolbox/printenv.c
new file mode 100644
index 0000000..d5ea531
--- /dev/null
+++ b/toolbox/printenv.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char** environ;
+
+int printenv_main (int argc, char **argv)
+{
+    char** e;
+    char* v;
+    int i;
+   
+    if (argc == 1) {
+        e = environ;
+        while (*e) {
+            printf("%s\n", *e);
+            e++;
+        }
+    } else {
+        for (i=1; i<argc; i++) {
+            v = getenv(argv[i]);
+            if (v) {
+                printf("%s\n", v);
+            }
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/ps.c b/toolbox/ps.c
new file mode 100644
index 0000000..3b86fa2
--- /dev/null
+++ b/toolbox/ps.c
@@ -0,0 +1,214 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <pwd.h>
+
+
+static char *nexttoksep(char **strp, char *sep)
+{
+    char *p = strsep(strp,sep);
+    return (p == 0) ? "" : p;
+}
+static char *nexttok(char **strp)
+{
+    return nexttoksep(strp, " ");
+}
+
+#define SHOW_PRIO 1
+#define SHOW_TIME 2
+
+static int display_flags = 0;
+
+static int ps_line(int pid, int tid, char *namefilter)
+{
+    char statline[1024];
+    char cmdline[1024];
+    char user[32];
+    struct stat stats;
+    int fd, r;
+    char *ptr, *name, *state;
+    int ppid, tty;
+    unsigned wchan, rss, vss, eip;
+    unsigned utime, stime;
+    int prio, nice, rtprio, sched;
+    struct passwd *pw;
+    
+    sprintf(statline, "/proc/%d", pid);
+    stat(statline, &stats);
+
+    if(tid) {
+        sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+        cmdline[0] = 0;
+    } else {
+        sprintf(statline, "/proc/%d/stat", pid);
+        sprintf(cmdline, "/proc/%d/cmdline", pid);    
+        fd = open(cmdline, O_RDONLY);
+        if(fd == 0) {
+            r = 0;
+        } else {
+            r = read(fd, cmdline, 1023);
+            close(fd);
+            if(r < 0) r = 0;
+        }
+        cmdline[r] = 0;
+    }
+    
+    fd = open(statline, O_RDONLY);
+    if(fd == 0) return -1;
+    r = read(fd, statline, 1023);
+    close(fd);
+    if(r < 0) return -1;
+    statline[r] = 0;
+
+    ptr = statline;
+    nexttok(&ptr); // skip pid
+    ptr++;          // skip "("
+
+    name = ptr;
+    ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
+    *ptr++ = '\0';           // and null-terminate name.
+
+    ptr++;          // skip " "
+    state = nexttok(&ptr);
+    ppid = atoi(nexttok(&ptr));
+    nexttok(&ptr); // pgrp
+    nexttok(&ptr); // sid
+    tty = atoi(nexttok(&ptr));
+    
+    nexttok(&ptr); // tpgid
+    nexttok(&ptr); // flags
+    nexttok(&ptr); // minflt
+    nexttok(&ptr); // cminflt
+    nexttok(&ptr); // majflt
+    nexttok(&ptr); // cmajflt
+#if 1
+    utime = atoi(nexttok(&ptr));
+    stime = atoi(nexttok(&ptr));
+#else
+    nexttok(&ptr); // utime
+    nexttok(&ptr); // stime
+#endif
+    nexttok(&ptr); // cutime
+    nexttok(&ptr); // cstime
+    prio = atoi(nexttok(&ptr));
+    nice = atoi(nexttok(&ptr));
+    nexttok(&ptr); // threads
+    nexttok(&ptr); // itrealvalue
+    nexttok(&ptr); // starttime
+    vss = strtoul(nexttok(&ptr), 0, 10); // vsize
+    rss = strtoul(nexttok(&ptr), 0, 10); // rss
+    nexttok(&ptr); // rlim
+    nexttok(&ptr); // startcode
+    nexttok(&ptr); // endcode
+    nexttok(&ptr); // startstack
+    nexttok(&ptr); // kstkesp
+    eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
+    nexttok(&ptr); // signal
+    nexttok(&ptr); // blocked
+    nexttok(&ptr); // sigignore
+    nexttok(&ptr); // sigcatch
+    wchan = strtoul(nexttok(&ptr), 0, 10); // wchan
+    nexttok(&ptr); // nswap
+    nexttok(&ptr); // cnswap
+    nexttok(&ptr); // exit signal
+    nexttok(&ptr); // processor
+    rtprio = atoi(nexttok(&ptr)); // rt_priority
+    sched = atoi(nexttok(&ptr)); // scheduling policy
+    
+    tty = atoi(nexttok(&ptr));
+    
+    if(tid != 0) {
+        ppid = pid;
+        pid = tid;
+    }
+
+    pw = getpwuid(stats.st_uid);
+    if(pw == 0) {
+        sprintf(user,"%d",(int)stats.st_uid);
+    } else {
+        strcpy(user,pw->pw_name);
+    }
+    
+    if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) {
+        printf("%-8s %-5d %-5d %-5d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+        if(display_flags&SHOW_PRIO)
+            printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+        printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name);
+        if(display_flags&SHOW_TIME)
+            printf(" (u:%d, s:%d)", utime, stime);
+        printf("\n");
+    }
+    return 0;
+}
+
+
+void ps_threads(int pid, char *namefilter)
+{
+    char tmp[128];
+    DIR *d;
+    struct dirent *de;
+
+    sprintf(tmp,"/proc/%d/task",pid);
+    d = opendir(tmp);
+    if(d == 0) return;
+    
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int tid = atoi(de->d_name);
+            if(tid == pid) continue;
+            ps_line(pid, tid, namefilter);
+        }
+    }
+    closedir(d);    
+}
+
+int ps_main(int argc, char **argv)
+{
+    DIR *d;
+    struct dirent *de;
+    char *namefilter = 0;
+    int pidfilter = 0;
+    int threads = 0;
+    
+    d = opendir("/proc");
+    if(d == 0) return -1;
+
+    while(argc > 1){
+        if(!strcmp(argv[1],"-t")) {
+            threads = 1;
+        } else if(!strcmp(argv[1],"-x")) {
+            display_flags |= SHOW_TIME;
+        } else if(!strcmp(argv[1],"-p")) {
+            display_flags |= SHOW_PRIO;
+        }  else if(isdigit(argv[1][0])){
+            pidfilter = atoi(argv[1]);
+        } else {
+            namefilter = argv[1];
+        }
+        argc--;
+        argv++;
+    }
+
+    printf("USER     PID   PPID  VSIZE RSS   %sWCHAN    PC         NAME\n", 
+           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"");
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int pid = atoi(de->d_name);
+            if(!pidfilter || (pidfilter == pid)) {
+                ps_line(pid, 0, namefilter);
+                if(threads) ps_threads(pid, namefilter);
+            }
+        }
+    }
+    closedir(d);
+    return 0;
+}
+
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..5a82e20
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+
+static int usage()
+{
+    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+    return -1;
+}
+
+int r_main(int argc, char *argv[])
+{
+    int width = 4, set = 0, fd;
+    unsigned addr, value;
+    void *page;
+    
+    if(argc < 2) return usage();
+
+    if(!strcmp(argv[1], "-b")) {
+        width = 1;
+        argc--;
+        argv++;
+    } else if(!strcmp(argv[1], "-s")) {
+        width = 2;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 2) return usage();
+    addr = strtoul(argv[1], 0, 16);
+
+    if(argc > 2) {
+        set = 1;
+        value = strtoul(argv[2], 0, 16);
+    }
+
+    fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if(fd < 0) {
+        fprintf(stderr,"cannot open /dev/mem\n");
+        return -1;
+    }
+    
+    page = mmap(0, 8192, PROT_READ | PROT_WRITE,
+                MAP_SHARED, fd, addr & (~4095));
+
+    if(page == MAP_FAILED){
+        fprintf(stderr,"cannot mmap region\n");
+        return -1;
+    }
+
+    switch(width){
+    case 4: {
+        unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095));
+        if(set) *x = value;
+        fprintf(stderr,"%08x: %08x\n", addr, *x);
+        break;
+    }
+    case 2: {
+        unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095));
+        if(set) *x = value;
+        fprintf(stderr,"%08x: %04x\n", addr, *x);
+        break;
+    }
+    case 1: {
+        unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095));
+        if(set) *x = value;
+        fprintf(stderr,"%08x: %02x\n", addr, *x);
+        break;
+    }
+    }    
+    return 0;
+}
diff --git a/toolbox/readtty.c b/toolbox/readtty.c
new file mode 100644
index 0000000..2b27548
--- /dev/null
+++ b/toolbox/readtty.c
@@ -0,0 +1,183 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+struct {
+    char key;
+    char *chars;
+} map[] = {
+    { '1', "_ -1?!,.:;\"'<=>()_" },
+    { '2', "Cabc2ABC" },
+    { '3', "Fdef3DEF" },
+    { '4', "Ighi4GHI" },
+    { '5', "Ljkl5JKL" },
+    { '6', "Omno6MNO" },
+    { '7', "Spqrs7PQRS" },
+    { '8', "Vtuv8TUV" },
+    { '9', "Zwxyz9WXYZ" },
+    { '0', "*+&0@/#*" },
+};
+
+char next_char(char key, char current)
+{
+    int i;
+    char *next;
+    for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+        if(key == map[i].key) {
+            next = strchr(map[i].chars, current);
+            if(next && next[1])
+                return next[1];
+            return map[i].chars[1];
+        }
+    }
+    return key;
+}
+
+char prev_char(char key, char current)
+{
+    int i;
+    char *next;
+    for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+        if(key == map[i].key) {
+            next = strchr(map[i].chars+1, current);
+            if(next && next[-1])
+                return next[-1];
+            return map[i].chars[1];
+        }
+    }
+    return key;
+}
+
+int readtty_main(int argc, char *argv[])
+{
+    int c;
+    //int flags;
+    char buf[1];
+    int res;
+    struct termios ttyarg;
+    struct termios savedttyarg;
+    int nonblock = 0;
+    int timeout = 0;
+    int flush = 0;
+    int phone = 0;
+    char *accept = NULL;
+    char *rejectstring = NULL;
+    char last_char_in = 0;
+    char current_char = 0;
+    char *exit_string = NULL;
+    int exit_match = 0;
+
+    do {
+        c = getopt(argc, argv, "nt:fa:r:pe:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 't':
+            timeout = atoi(optarg);
+            break;
+        case 'n':
+            nonblock = 1;
+            break;
+        case 'f':
+            flush = 1;
+            break;
+        case 'a':
+            accept = optarg;
+            break;
+        case 'r':
+            rejectstring = optarg;
+            break;
+        case 'p':
+            phone = 1;
+            break;
+        case 'e':
+            exit_string = optarg;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(flush)
+        tcflush(STDIN_FILENO, TCIFLUSH);
+    ioctl(STDIN_FILENO, TCGETS , &savedttyarg) ;       /* set changed tty arguments */
+    ttyarg = savedttyarg;
+    ttyarg.c_cc[VMIN] = (timeout > 0 || nonblock) ? 0 : 1;                /* minimum of 0 chars */
+    ttyarg.c_cc[VTIME] = timeout;              /* wait max 15/10 sec */
+    ttyarg.c_iflag = BRKINT | ICRNL; 
+    ttyarg.c_lflag &= ~(ECHO | ICANON);
+    ioctl(STDIN_FILENO, TCSETS , &ttyarg);
+
+    while (1) {
+        res = read(STDIN_FILENO, buf, 1);
+        if(res <= 0) {
+            if(phone) {
+                if(current_char) {
+                    write(STDERR_FILENO, &current_char, 1);
+                    write(STDOUT_FILENO, &current_char, 1);
+                    if(exit_string && current_char == exit_string[exit_match]) {
+                        exit_match++;
+                        if(exit_string[exit_match] == '\0')
+                            break;
+                    }
+                    else
+                        exit_match = 0;
+                    current_char = 0;
+                }
+                continue;
+            }
+            break;
+        }
+        if(accept && strchr(accept, buf[0]) == NULL) {
+            if(rejectstring) {
+                write(STDOUT_FILENO, rejectstring, strlen(rejectstring));
+                break;
+            }
+            if(flush)
+                tcflush(STDIN_FILENO, TCIFLUSH);
+            continue;
+        }
+        if(phone) {
+            //if(!isprint(buf[0])) {
+            //  fprintf(stderr, "got unprintable character 0x%x\n", buf[0]);
+            //}
+            if(buf[0] == '\0') {
+                if(current_char) {
+                    current_char = prev_char(last_char_in, current_char);
+                    write(STDERR_FILENO, &current_char, 1);
+                    write(STDERR_FILENO, "\b", 1);
+                }
+                continue;
+            }
+            if(current_char && buf[0] != last_char_in) {
+                write(STDERR_FILENO, &current_char, 1);
+                write(STDOUT_FILENO, &current_char, 1);
+                if(exit_string && current_char == exit_string[exit_match]) {
+                    exit_match++;
+                    if(exit_string[exit_match] == '\0')
+                        break;
+                }
+                else
+                    exit_match = 0;
+                current_char = 0;
+            }
+            last_char_in = buf[0];
+            current_char = next_char(last_char_in, current_char);
+            write(STDERR_FILENO, &current_char, 1);
+            write(STDERR_FILENO, "\b", 1);
+            continue;
+        }
+        write(STDOUT_FILENO, buf, 1);
+        break;
+    }
+    ioctl(STDIN_FILENO, TCSETS , &savedttyarg) ;       /* set changed tty arguments */
+
+    return 0;
+}
diff --git a/toolbox/reboot.c b/toolbox/reboot.c
new file mode 100644
index 0000000..aebe185
--- /dev/null
+++ b/toolbox/reboot.c
@@ -0,0 +1,56 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+#include <unistd.h>
+
+int reboot_main(int argc, char *argv[])
+{
+    int ret;
+    int nosync = 0;
+    int poweroff = 0;
+
+    opterr = 0;
+    do {
+        int c;
+
+        c = getopt(argc, argv, "np");
+        
+        if (c == EOF) {
+            break;
+        }
+        
+        switch (c) {
+        case 'n':
+            nosync = 1;
+            break;
+        case 'p':
+            poweroff = 1;
+            break;
+        case '?':
+            fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    } while (1);
+
+    if(argc > optind + 1) {
+        fprintf(stderr, "%s: too many arguments\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if(!nosync)
+        sync();
+
+    if(poweroff)
+        ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
+    else if(argc > optind)
+        ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]);
+    else
+        ret = reboot(RB_AUTOBOOT);
+    if(ret < 0) {
+        perror("reboot");
+        exit(EXIT_FAILURE);
+    }
+    fprintf(stderr, "reboot returned\n");
+    return 0;
+}
diff --git a/toolbox/renice.c b/toolbox/renice.c
new file mode 100644
index 0000000..978b329
--- /dev/null
+++ b/toolbox/renice.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+static void
+usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s [[-r] priority pids ...] [-g pid]\n", s);
+    exit(EXIT_FAILURE);
+}
+
+void print_prio(pid_t pid)
+{
+    int sched;
+    struct sched_param sp;
+
+    printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
+
+    printf("scheduling class: ");
+    sched = sched_getscheduler(pid);
+    switch (sched) {
+    case SCHED_FIFO:
+        printf("FIFO\n");
+        break;
+    case SCHED_RR:
+        printf("RR\n");
+        break;
+    case SCHED_OTHER:
+        printf("Normal\n");
+        break;
+    case -1:
+        perror("sched_getscheduler");
+        break;
+    default:
+        printf("Unknown\n");
+    }
+
+    sched_getparam(pid, &sp);
+    printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
+           sched_get_priority_min(sched), sched_get_priority_max(sched));
+}
+
+int renice_main(int argc, char *argv[])
+{
+    int prio;
+    int realtime = 0;
+    char *cmd = argv[0];
+
+    // consume command name
+    argc--;
+    argv++;
+
+    if (argc < 1)
+        usage(cmd);
+
+    if(strcmp("-r", argv[0]) == 0) {
+        // do realtime priority adjustment
+        realtime = 1;
+        argc--;
+        argv++;
+    }
+
+	if(strcmp("-g", argv[0]) == 0) {
+        if (argc < 2)
+            usage(cmd);
+        print_prio(atoi(argv[1]));
+        return 0;
+    }
+
+    if (argc < 1)
+        usage(cmd);
+
+    prio = atoi(argv[0]);
+    argc--;
+    argv++;
+
+    if (argc < 1)
+        usage(cmd);
+
+    while(argc) {
+        pid_t pid;
+
+        pid = atoi(argv[0]);
+        argc--;
+        argv++;
+
+        if (realtime) {
+            struct sched_param sp = { .sched_priority = prio };
+            int ret;
+
+            ret = sched_setscheduler(pid, SCHED_RR, &sp);
+            if (ret) {
+                perror("sched_set_scheduler");
+                exit(EXIT_FAILURE);
+            }
+        } else {
+            int ret;
+
+            ret = setpriority(PRIO_PROCESS, pid, prio);
+            if (ret) {
+                perror("setpriority");
+                exit(EXIT_FAILURE);
+            }
+        }
+    }
+   
+    return 0;
+}
+
+
diff --git a/toolbox/rm.c b/toolbox/rm.c
new file mode 100644
index 0000000..bd66311
--- /dev/null
+++ b/toolbox/rm.c
@@ -0,0 +1,92 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static int usage()
+{
+    fprintf(stderr,"rm [-rR] <target>\n");
+    return -1;
+}
+
+/* return -1 on failure, with errno set to the first error */
+static int unlink_recursive(const char* name)
+{
+    struct stat st;
+    DIR *dir;
+    struct dirent *de;
+    int fail = 0;
+
+    /* is it a file or directory? */
+    if (lstat(name, &st) < 0)
+        return -1;
+
+    /* a file, so unlink it */
+    if (!S_ISDIR(st.st_mode))
+        return unlink(name);
+
+    /* a directory, so open handle */
+    dir = opendir(name);
+    if (dir == NULL)
+        return -1;
+
+    /* recurse over components */
+    errno = 0;
+    while ((de = readdir(dir)) != NULL) {
+        char dn[PATH_MAX];
+        if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+            continue;
+        sprintf(dn, "%s/%s", name, de->d_name);
+        if (unlink_recursive(dn) < 0) {
+            fail = 1;
+            break;
+        }
+        errno = 0;
+    }
+    /* in case readdir or unlink_recursive failed */
+    if (fail || errno < 0) {
+        int save = errno;
+        closedir(dir);
+        errno = save;
+        return -1;
+    }
+
+    /* close directory handle */
+    if (closedir(dir) < 0)
+        return -1;
+
+    /* delete target directory */
+    return rmdir(name);
+}
+
+int rm_main(int argc, char *argv[])
+{
+    int ret;
+    int i = 1;
+    int recursive = 0;
+
+    if (argc < 2)
+        return usage();
+
+    /* check if recursive */
+    if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) {
+        recursive = 1;
+        i = 2;
+    }
+    
+    /* loop over the file/directory args */
+    for (; i < argc; i++) {
+        int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]);
+        if (ret < 0) {
+            fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno));
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c
new file mode 100644
index 0000000..06f3df2
--- /dev/null
+++ b/toolbox/rmdir.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+    fprintf(stderr,"rmdir <directory>\n");
+    return -1;
+}
+
+int rmdir_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2) return usage();
+
+    while(argc > 1) {
+        argc--;
+        argv++;
+        ret = rmdir(argv[0]);
+        if(ret < 0) {
+            fprintf(stderr, "rmdir failed for %s, %s\n", argv[0], strerror(errno));
+            return ret;
+        }
+    }
+    
+    return 0;
+}
diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c
new file mode 100644
index 0000000..7e10c06
--- /dev/null
+++ b/toolbox/rmmod.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <asm/unistd.h>
+
+extern int delete_module(const char *, unsigned int);
+
+int rmmod_main(int argc, char **argv)
+{
+	int ret;
+	char *modname, *dot;
+
+	/* make sure we've got an argument */
+	if (argc < 2) {
+		fprintf(stderr, "usage: rmmod <module>\n");
+		return -1;
+	}
+
+	/* if given /foo/bar/blah.ko, make a weak attempt
+	 * to convert to "blah", just for convenience
+	 */
+	modname = strrchr(argv[1], '/');
+	if (!modname)
+		modname = argv[1];
+	dot = strchr(argv[1], '.');
+	if (dot)
+		*dot = '\0';
+
+	/* pass it to the kernel */
+	ret = delete_module(modname, O_NONBLOCK | O_EXCL);
+	if (ret != 0) {
+		fprintf(stderr, "rmmod: delete_module '%s' failed (errno %d)\n",
+						modname, errno);
+		return -1;
+	}
+
+	return 0;
+}
+
diff --git a/toolbox/rotatefb.c b/toolbox/rotatefb.c
new file mode 100644
index 0000000..2ff4127
--- /dev/null
+++ b/toolbox/rotatefb.c
@@ -0,0 +1,71 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/fb.h>
+
+
+int rotatefb_main(int argc, char *argv[])
+{
+    int c;
+    char *fbdev = "/dev/graphics/fb0";
+    int rotation = 0;
+    int fd;
+    int res;
+    struct fb_var_screeninfo fbinfo;
+
+    do {
+        c = getopt(argc, argv, "d:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            fbdev = optarg;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(optind + 1 != argc) {
+        fprintf(stderr, "%s: specify rotation\n", argv[0]);
+        exit(1);
+    }
+    rotation = atoi(argv[optind]);
+
+    fd = open(fbdev, O_RDWR);
+    if(fd < 0) {
+        fprintf(stderr, "cannot open %s\n", fbdev);
+        return 1;
+    }
+
+    res = ioctl(fd, FBIOGET_VSCREENINFO, &fbinfo);
+    if(res < 0) {
+        fprintf(stderr, "failed to get fbinfo: %s\n", strerror(errno));
+        return 1;
+    }
+    if((fbinfo.rotate ^ rotation) & 1) {
+        unsigned int xres = fbinfo.yres;
+        fbinfo.yres = fbinfo.xres;
+        fbinfo.xres = xres;
+        fbinfo.xres_virtual = fbinfo.xres;
+        fbinfo.yres_virtual = fbinfo.yres * 2;
+        if(fbinfo.yoffset == xres)
+            fbinfo.yoffset = fbinfo.yres;
+    }
+    fbinfo.rotate = rotation; 
+    res = ioctl(fd, FBIOPUT_VSCREENINFO, &fbinfo);
+    if(res < 0) {
+        fprintf(stderr, "failed to set fbinfo: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/route.c b/toolbox/route.c
new file mode 100644
index 0000000..adf5c69
--- /dev/null
+++ b/toolbox/route.c
@@ -0,0 +1,97 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+#include <linux/route.h>
+
+static void die(const char *s)
+{
+    fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
+    exit(-1);
+}
+
+static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
+{
+	sin->sin_family = AF_INET;
+	sin->sin_port = 0;
+	sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+#define ADVANCE(argc, argv) do { argc--, argv++; } while(0)
+#define EXPECT_NEXT(argc, argv) do {        \
+    ADVANCE(argc, argv);                    \
+	if (0 == argc) {  						\
+		errno = EINVAL;                     \
+		die("expecting one more argument"); \
+	}                                       \
+} while(0)		
+
+/* current support two kinds of usage */
+/* route add default dev wlan0 */
+/* route add default gw 192.168.20.1 dev wlan0 */
+
+int route_main(int argc, char *argv[])
+{
+    struct ifreq ifr;
+    int s,i;
+	struct rtentry rt;
+	struct sockaddr_in ina;
+   
+    if(argc == 0) return 0;
+    
+    strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+    ifr.ifr_name[IFNAMSIZ-1] = 0;
+	ADVANCE(argc, argv);
+
+    if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        die("cannot open control socket\n");
+    }
+
+    while(argc > 0){
+        if(!strcmp(argv[0], "add")) {
+			EXPECT_NEXT(argc, argv);
+			if(!strcmp(argv[0], "default")) {
+				EXPECT_NEXT(argc, argv);
+				memset((char *) &rt, 0, sizeof(struct rtentry));
+				rt.rt_dst.sa_family = AF_INET;	
+				if(!strcmp(argv[0], "dev")) {
+				  EXPECT_NEXT(argc, argv);
+				  rt.rt_flags = RTF_UP | RTF_HOST;
+				  rt.rt_dev = argv[0];
+				  if (ioctl(s, SIOCADDRT, &rt) < 0) die("SIOCADDRT");
+				}else if(!strcmp(argv[0], "gw")) {
+				  EXPECT_NEXT(argc, argv);
+				  rt.rt_flags = RTF_UP | RTF_GATEWAY;
+				  init_sockaddr_in((struct sockaddr_in *)&(rt.rt_genmask), "0.0.0.0");
+				  if(isdigit(argv[0][0])){
+					init_sockaddr_in((struct sockaddr_in *)&(rt.rt_gateway), argv[0]);
+				  }else{
+					die("expecting an IP address for parameter \"gw\"");
+				  }
+				  EXPECT_NEXT(argc, argv);
+				  if(!strcmp(argv[0], "dev")) {
+					EXPECT_NEXT(argc, argv);
+					rt.rt_dev = argv[0];
+					if (ioctl(s, SIOCADDRT, &rt) < 0){
+					  die("SIOCADDRT");
+					}
+				  }
+				}
+			}
+        }
+		ADVANCE(argc, argv);
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c
new file mode 100644
index 0000000..c0e0141
--- /dev/null
+++ b/toolbox/schedtop.c
@@ -0,0 +1,335 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+
+#include <pwd.h>
+
+struct thread_info {
+    int pid;
+    int tid;
+    char name[64];
+    uint64_t exec_time;
+    uint64_t delay_time;
+    uint32_t run_count;
+};
+
+struct thread_table {
+    size_t allocated;
+    size_t active;
+    struct thread_info *data;
+};
+
+enum {
+    FLAG_BATCH = 1U << 0,
+    FLAG_HIDE_IDLE = 1U << 1,
+    FLAG_SHOW_THREADS = 1U << 2,
+    FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
+};
+
+static int time_dp = 9;
+static int time_div = 1;
+#define NS_TO_S_D(ns) \
+    (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
+
+struct thread_table processes;
+struct thread_table last_processes;
+struct thread_table threads;
+struct thread_table last_threads;
+
+static void grow_table(struct thread_table *table)
+{
+    size_t size = table->allocated;
+    struct thread_info *new_table;
+    if (size < 128)
+        size = 128;
+    else
+        size *= 2;
+    
+    new_table = realloc(table->data, size * sizeof(*table->data));
+    if (new_table == NULL) {
+        fprintf(stderr, "out of memory\n");
+        exit(1);
+    }
+    table->data = new_table;
+    table->allocated = size;
+}
+
+static struct thread_info *get_item(struct thread_table *table)
+{
+    if (table->active >= table->allocated)
+        grow_table(table);
+    return table->data + table->active;
+}
+
+static void commit_item(struct thread_table *table)
+{
+    table->active++;
+}
+
+static int read_line(char *line, size_t line_size)
+{
+    int fd;
+    int len;
+    fd = open(line, O_RDONLY);
+    if(fd == 0)
+        return -1;
+    len = read(fd, line, line_size - 1);
+    close(fd);
+    if (len <= 0)
+        return -1;
+    line[len] = '\0';
+    return 0;
+}
+
+static void add_thread(int pid, int tid, struct thread_info *proc_info)
+{
+    char line[1024];
+    char *name, *name_end;
+    size_t name_len;
+    struct thread_info *info;
+    if(tid == 0)
+        info = get_item(&processes);
+    else
+        info = get_item(&threads);
+    info->pid = pid;
+    info->tid = tid;
+
+    if(tid)
+        sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
+    else
+        sprintf(line, "/proc/%d/schedstat", pid);
+    if (read_line(line, sizeof(line)))
+        return;
+    if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
+        return;
+    if (proc_info) {
+        proc_info->exec_time += info->exec_time;
+        proc_info->delay_time += info->delay_time;
+        proc_info->run_count += info->run_count;
+    }
+
+    name = NULL;
+    if (!tid) {
+        sprintf(line, "/proc/%d/cmdline", pid);
+        if (read_line(line, sizeof(line)) == 0 && line[0]) {
+            name = line;
+            name_len = strlen(name);
+        }
+    }
+    if (!name) {
+        if (tid)
+            sprintf(line, "/proc/%d/task/%d/stat", pid, tid);
+        else
+            sprintf(line, "/proc/%d/stat", pid);
+        if (read_line(line, sizeof(line)))
+            return;
+        name = strchr(line, '(');
+        if (name == NULL)
+            return;
+        name_end = strchr(name, ')');
+        if (name_end == NULL)
+            return;
+        name++;
+        name_len = name_end - name;
+    }
+    if (name_len >= sizeof(info->name))
+        name_len = sizeof(info->name) - 1;
+    memcpy(info->name, name, name_len);
+    info->name[name_len] = '\0';
+    if(tid == 0)
+        commit_item(&processes);
+    else
+        commit_item(&threads);
+}
+
+static void add_threads(int pid, struct thread_info *proc_info)
+{
+    char path[1024];
+    DIR *d;
+    struct dirent *de;
+    sprintf(path, "/proc/%d/task", pid);
+    d = opendir(path);
+    if(d == 0) return;
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int tid = atoi(de->d_name);
+            add_thread(pid, tid, proc_info);
+        }
+    }
+    closedir(d);
+}
+
+static void print_threads(int pid, uint32_t flags)
+{
+    size_t i, j;
+    for (i = 0; i < last_threads.active; i++) {
+        int epid = last_threads.data[i].pid;
+        int tid = last_threads.data[i].tid;
+        if (epid != pid)
+            continue;
+        for (j = 0; j < threads.active; j++)
+            if (tid == threads.data[j].tid)
+                break;
+        if (j == threads.active)
+            printf(" %5u died\n", tid);
+        else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
+            printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u  %s\n", tid,
+                NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
+                NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
+                threads.data[j].run_count - last_threads.data[i].run_count,
+                NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time),
+                threads.data[j].run_count, threads.data[j].name);
+    }
+}
+
+static void update_table(DIR *d, uint32_t flags)
+{
+    size_t i, j;
+    struct dirent *de;
+    
+    rewinddir(d);
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int pid = atoi(de->d_name);
+            struct thread_info *proc_info;
+            add_thread(pid, 0, NULL);
+            proc_info = &processes.data[processes.active - 1];
+            proc_info->exec_time = 0;
+            proc_info->delay_time = 0;
+            proc_info->run_count = 0;
+            add_threads(pid, proc_info);
+        }
+    }
+    if (!(flags & FLAG_BATCH))
+        printf("\e[H\e[0J");
+    printf("Processes: %d, Threads %d\n", processes.active, threads.active);
+    switch (time_dp) {
+    case 3:
+        printf("   TID --- SINCE LAST ---- ---------- TOTAL ----------\n");
+        printf("  PID  EXEC_T  DELAY SCHED EXEC_TIME DELAY_TIM   SCHED NAME\n");
+        break;
+    case 6:
+        printf("   TID ------ SINCE LAST -------    ------------ TOTAL -----------\n");
+        printf("  PID  EXEC_TIME DELAY_TIM SCHED    EXEC_TIME   DELAY_TIME   SCHED NAME\n");
+        break;
+    default:
+        printf("   TID    -------- SINCE LAST --------       ------------- TOTAL -------------\n");
+        printf("  PID     EXEC_TIME   DELAY_TIME SCHED       EXEC_TIME      DELAY_TIME   SCHED NAME\n");
+        break;
+    }
+    for (i = 0; i < last_processes.active; i++) {
+        int pid = last_processes.data[i].pid;
+        int tid = last_processes.data[i].tid;
+        for (j = 0; j < processes.active; j++)
+            if (pid == processes.data[j].pid)
+                break;
+        if (j == processes.active)
+            printf("%5u died\n", pid);
+        else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
+            printf("%5u  %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
+                NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
+                NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
+                processes.data[j].run_count - last_processes.data[i].run_count,
+                NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
+                processes.data[j].run_count, processes.data[j].name);
+            if (flags & FLAG_SHOW_THREADS)
+                print_threads(pid, flags);
+        }
+    }
+
+    {
+        struct thread_table tmp;
+        tmp = last_processes;
+        last_processes = processes;
+        processes = tmp;
+        processes.active = 0;
+        tmp = last_threads;
+        last_threads = threads;
+        threads = tmp;
+        threads.active = 0;
+    }
+}
+
+void
+sig_abort(int signum)
+{
+    printf("\e[?47l");
+    exit(0);
+}
+
+
+int schedtop_main(int argc, char **argv)
+{
+    int c;
+    DIR *d;
+    struct dirent *de;
+    char *namefilter = 0;
+    int pidfilter = 0;
+    uint32_t flags = 0;    
+    int delay = 3000000;
+    float delay_f;
+
+    while(1) {
+        c = getopt(argc, argv, "d:ibtamun");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            delay_f = atof(optarg);
+            delay = delay_f * 1000000;
+            break;
+        case 'b':
+            flags |= FLAG_BATCH;
+            break;
+        case 'i':
+            flags |= FLAG_HIDE_IDLE;
+            break;
+        case 't':
+            flags |= FLAG_SHOW_THREADS;
+            break;
+        case 'a':
+            flags |= FLAG_USE_ALTERNATE_SCREEN;
+            break;
+        case 'm':
+            time_dp = 3;
+            time_div = 1000000;
+            break;
+        case 'u':
+            time_dp = 6;
+            time_div = 1000;
+            break;
+        case 'n':
+            time_dp = 9;
+            time_div = 1;
+            break;
+        }
+    }
+
+    d = opendir("/proc");
+    if(d == 0) return -1;
+
+    if (!(flags & FLAG_BATCH)) {
+        if(flags & FLAG_USE_ALTERNATE_SCREEN) {
+            signal(SIGINT, sig_abort);
+            signal(SIGPIPE, sig_abort);
+            signal(SIGTERM, sig_abort);
+            printf("\e7\e[?47h");
+        }
+        printf("\e[2J");
+    }
+    while (1) {
+        update_table(d, flags);
+        usleep(delay);
+    }
+    closedir(d);
+    return 0;
+}
+
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c
new file mode 100644
index 0000000..1608e6c
--- /dev/null
+++ b/toolbox/sendevent.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+//#include <linux/input.h> // this does not compile
+#include <errno.h>
+
+
+// from <linux/input.h>
+
+struct input_event {
+	struct timeval time;
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+#define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */
+#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
+#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
+#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */
+
+#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
+#define EVIOCGPHYS(len)		_IOC(_IOC_READ, 'E', 0x07, len)		/* get physical location */
+#define EVIOCGUNIQ(len)		_IOC(_IOC_READ, 'E', 0x08, len)		/* get unique identifier */
+
+#define EVIOCGKEY(len)		_IOC(_IOC_READ, 'E', 0x18, len)		/* get global keystate */
+#define EVIOCGLED(len)		_IOC(_IOC_READ, 'E', 0x19, len)		/* get all LEDs */
+#define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
+#define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */
+
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
+#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */
+
+#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
+#define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
+#define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
+
+#define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
+
+// end <linux/input.h>
+
+
+
+int sendevent_main(int argc, char *argv[])
+{
+    int i;
+    int fd;
+    int ret;
+    int version;
+    struct input_event event;
+
+    if(argc != 5) {
+        fprintf(stderr, "use: %s device type code value\n", argv[0]);
+        return 1;
+    }
+
+    fd = open(argv[1], O_RDWR);
+    if(fd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+    if (ioctl(fd, EVIOCGVERSION, &version)) {
+        fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+    memset(&event, 0, sizeof(event));
+    event.type = atoi(argv[2]);
+    event.code = atoi(argv[3]);
+    event.value = atoi(argv[4]);
+    ret = write(fd, &event, sizeof(event));
+    if(ret < sizeof(event)) {
+        fprintf(stderr, "write event failed, %s\n", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c
new file mode 100644
index 0000000..b0ce13f
--- /dev/null
+++ b/toolbox/setconsole.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+
+static int activate_thread_switch_vc;
+static void *activate_thread(void *arg)
+{
+    int res;
+    int fd = (int)arg;
+    while(activate_thread_switch_vc >= 0) {
+        do {
+            res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc);
+        } while(res < 0 && errno == EINTR);
+        if (res < 0) {
+            fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc);
+        }
+        if(activate_thread_switch_vc >= 0)
+            sleep(1);
+    }
+    return NULL;
+}
+
+
+int setconsole_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+    int res;
+
+    int mode = -1;
+    int new_vc = 0;
+    int close_vc = 0;
+    int switch_vc = -1;
+    int printvc = 0;
+    char *ttydev = "/dev/tty0";
+
+    do {
+        c = getopt(argc, argv, "d:gtncv:poh");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            ttydev = optarg;
+            break;
+        case 'g':
+            if(mode == KD_TEXT) {
+                fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+                exit(1);
+            }
+            mode = KD_GRAPHICS;
+            break;
+        case 't':
+            if(mode == KD_GRAPHICS) {
+                fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+                exit(1);
+            }
+            mode = KD_TEXT;
+            break;
+        case 'n':
+            new_vc = 1;
+            break;
+        case 'c':
+            close_vc = 1;
+            break;
+        case 'v':
+            switch_vc = atoi(optarg);
+            break;
+        case 'p':
+            printvc |= 1;
+            break;
+        case 'o':
+            printvc |= 2;
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n"
+                    "  -d <dev>   Use <dev> instead of /dev/tty0\n"
+                    "  -v <vc>    Switch to virtual console <vc>\n"
+                    "  -g         Switch to graphics mode\n"
+                    "  -t         Switch to text mode\n"
+                    "  -n         Create and switch to new virtual console\n"
+                    "  -c         Close unused virtual consoles\n"
+                    "  -p         Print new virtual console\n"
+                    "  -o         Print old virtual console\n"
+                    "  -h         Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) {
+        fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]);
+        return -1;
+    }
+
+    fd = open(ttydev, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s\n", ttydev);
+        return -1;
+    }
+
+    if ((printvc && !new_vc) || (printvc & 2)) {
+        struct vt_stat vs;
+
+        res = ioctl(fd, VT_GETSTATE, &vs);
+        if (res < 0) {
+            fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res);
+        }
+        printf("%d\n", vs.v_active);
+    }
+
+    if (new_vc) {
+        int vtnum;
+        res = ioctl(fd, VT_OPENQRY, &vtnum);
+        if (res < 0 || vtnum == -1) {
+            fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum);
+        }
+        switch_vc = vtnum;
+    }
+    if (switch_vc != -1) {
+        pthread_t thread;
+        pthread_attr_t attr;
+        activate_thread_switch_vc = switch_vc;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        pthread_create(&thread, &attr, activate_thread, (void*)fd);
+        
+        do {
+            res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc);
+        } while(res < 0 && errno == EINTR);
+        activate_thread_switch_vc = -1;
+        if (res < 0) {
+            fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc);
+        }
+        if(printvc & 1)
+            printf("%d\n", switch_vc);
+
+        close(fd);
+        fd = open(ttydev, O_RDWR | O_SYNC);
+        if (fd < 0) {
+            fprintf(stderr, "cannot open %s\n", ttydev);
+            return -1;
+        }
+    }
+    if (close_vc) {
+        res = ioctl(fd, VT_DISALLOCATE, 0);
+        if (res < 0) {
+            fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res);
+        }
+    }
+    if (mode != -1) {
+        if (ioctl(fd, KDSETMODE, (void*)mode) < 0) {
+            fprintf(stderr, "KDSETMODE %d failed\n", mode);
+            return -1;
+        }
+    }
+    return 0;
+}
diff --git a/toolbox/setkey.c b/toolbox/setkey.c
new file mode 100644
index 0000000..1ff2774
--- /dev/null
+++ b/toolbox/setkey.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+
+static void setkey_usage(char *argv[])
+{
+    fprintf(stderr, "%s [-t <table>] [-k <index>] [-v value] [-r] [-h]\n"
+            "  -t <table> Select table\n"
+            "  -k <index> Select key\n"
+            "  -v <value> Set entry\n"
+            "  -r         Read current entry\n"
+            "  -h         Print help\n", argv[0]);
+}
+
+#define TTYDEV	"/dev/tty0"
+
+int setkey_main(int argc, char *argv[])
+{
+    int fd;
+    struct kbentry kbe;
+    int did_something = 0;
+
+    kbe.kb_table = 0;
+    kbe.kb_index = -1;
+    kbe.kb_value = 0;
+
+    fd = open(TTYDEV, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "open %s: %s\n", TTYDEV, strerror(errno));
+        return 1;
+    }
+
+    do {
+        int c, ret;
+
+        c = getopt(argc, argv, "t:k:v:hr");
+        if (c == EOF)
+            break;
+
+        switch (c) {
+        case 't':
+            kbe.kb_table = strtol(optarg, NULL, 0);
+            break;
+        case 'k':
+            kbe.kb_index = strtol(optarg, NULL, 0);
+            break;
+        case 'v':
+            kbe.kb_value = strtol(optarg, NULL, 0);
+            ret = ioctl(fd, KDSKBENT, &kbe);
+            if (ret < 0) {
+                fprintf(stderr, "KDSKBENT %d %d %d failed: %s\n",
+                        kbe.kb_table, kbe.kb_index, kbe.kb_value,
+                        strerror(errno));
+                return 1;
+            }
+            did_something = 1;
+            break;
+        case 'r':
+            ret = ioctl(fd, KDGKBENT, &kbe);
+            if (ret < 0) {
+                fprintf(stderr, "KDGKBENT %d %d  failed: %s\n",
+                        kbe.kb_table, kbe.kb_index, strerror(errno));
+                return 1;
+            }
+            printf("0x%x 0x%x 0x%x\n",
+                   kbe.kb_table, kbe.kb_index, kbe.kb_value);
+            did_something = 1;
+            break;
+        case 'h':
+            setkey_usage(argv);
+            return 1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            return 1;
+        }
+    } while (1);
+
+    if(optind != argc || !did_something) {
+        setkey_usage(argv);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/setprop.c b/toolbox/setprop.c
new file mode 100644
index 0000000..63ad2b4
--- /dev/null
+++ b/toolbox/setprop.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+int setprop_main(int argc, char *argv[])
+{
+    if(argc != 3) {
+        fprintf(stderr,"usage: setprop <key> <value>\n");
+        return 1;
+    }
+
+    if(property_set(argv[1], argv[2])){
+        fprintf(stderr,"could not set property\n");
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/sleep.c b/toolbox/sleep.c
new file mode 100644
index 0000000..c09ae03
--- /dev/null
+++ b/toolbox/sleep.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static void
+usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s SECONDS\n", s);
+    exit(-1);
+}
+
+int sleep_main(int argc, char *argv[])
+{
+    unsigned long seconds;
+    char *endptr;
+
+    if (argc != 2) {
+        usage(argv[0]);
+    }
+
+    seconds = strtoul(argv[1], &endptr, 10);
+
+    if (endptr == argv[1]) {
+        usage(argv[0]);
+    }
+
+
+    sleep((unsigned int)seconds);
+   
+    return 0;
+}
+
+
diff --git a/toolbox/smd.c b/toolbox/smd.c
new file mode 100644
index 0000000..65ff994
--- /dev/null
+++ b/toolbox/smd.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int smd_main(int argc, char **argv)
+{
+    int fd, len, r, port = 0;
+    char devname[32];
+    argc--;
+    argv++;
+
+    if((argc > 0) && (argv[0][0] == '-')) {
+        port = atoi(argv[0] + 1);
+        argc--;
+        argv++;
+    }
+
+    sprintf(devname,"/dev/smd%d",port);
+    fd = open(devname, O_WRONLY);
+    if(fd < 0) {
+        fprintf(stderr,"failed to open smd0 - %s\n",
+            strerror(errno));
+        return -1;
+    }
+    while(argc > 0) {
+        len = strlen(argv[0]);
+        r = write(fd, argv[0], len);
+        if(r != len) {
+            fprintf(stderr,"failed to write smd0 (%d) %s\n",
+                r, strerror(errno));
+            return -1;
+        }
+        argc--;
+        argv++;
+        write(fd, argc ? " " : "\r", 1);
+    }
+    close(fd);
+    return 0;       
+}
diff --git a/toolbox/start.c b/toolbox/start.c
new file mode 100644
index 0000000..3bd9fbb
--- /dev/null
+++ b/toolbox/start.c
@@ -0,0 +1,20 @@
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+int start_main(int argc, char *argv[])
+{
+    char buf[1024];
+    if(argc > 1) {
+        property_set("ctl.start", argv[1]);
+    } else {
+        /* default to "start zygote" "start runtime" */
+        property_set("ctl.start", "zygote");
+        property_set("ctl.start", "runtime");
+    }
+    
+    return 0;
+}
diff --git a/toolbox/stop.c b/toolbox/stop.c
new file mode 100644
index 0000000..05baffd
--- /dev/null
+++ b/toolbox/stop.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+int stop_main(int argc, char *argv[])
+{
+    char buf[1024];
+
+    if(argc > 1) {
+        property_set("ctl.stop", argv[1]);
+    } else{
+        /* default to "stop runtime" "stop zygote" */
+        property_set("ctl.stop", "runtime");
+        property_set("ctl.stop", "zygote");
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/sync.c b/toolbox/sync.c
new file mode 100644
index 0000000..8284276
--- /dev/null
+++ b/toolbox/sync.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int sync_main(int argc, char **argv)
+{
+	sync();
+	return 0;
+}
diff --git a/toolbox/syren.c b/toolbox/syren.c
new file mode 100644
index 0000000..06e329e
--- /dev/null
+++ b/toolbox/syren.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+/* ioctl crap */
+#define SYREN_RD		101
+#define SYREN_WR		102
+#define SYREN_OLD_RD	108
+#define SYREN_OLD_WR	109
+
+struct syren_io_args {
+	unsigned long	page;
+	unsigned long	addr;
+	unsigned long	value;
+};
+
+typedef struct {
+	u_char			page;
+	u_char			addr;
+	const char		*name;
+} syren_reg;
+
+static syren_reg registers[] = {
+	{ 0, 0x04, "TOGBR1" },
+	{ 0, 0x05, "TOGBR2" },
+	{ 0, 0x06, "VBDCTRL" },
+	{ 1, 0x07, "VBUCTRL" },
+	{ 1, 0x08, "VBCTRL" },
+	{ 1, 0x09, "PWDNRG" },
+	{ 1, 0x0a, "VBPOP" },
+	{ 1, 0x0b, "VBCTRL2" },
+	{ 1, 0x0f, "VAUDCTRL" },
+	{ 1, 0x10, "VAUSCTRL" },
+	{ 1, 0x11, "VAUOCTRL" },
+	{ 1, 0x12, "VAUDPLL" },
+	{ 1, 0x17, "VRPCSIMR" },
+	{ 0, 0, 0 }
+};
+
+static syren_reg *find_reg(const char *name)
+{
+	int i;
+
+	for (i = 0; registers[i].name != 0; i++) {
+		if (!strcasecmp(registers[i].name, name))
+			return &registers[i];
+	}
+
+	return NULL;
+}
+
+static int usage(void)
+{
+	fprintf(stderr, "usage: syren [r/w] [REGNAME | page:addr] (value)\n");
+	return 1;
+}
+
+int
+syren_main(int argc, char **argv)
+{
+	int cmd = -1;
+	syren_reg *r;
+	struct syren_io_args sio;
+	char name[32];
+	int fd;
+
+	if (argc < 3) {
+		return usage();
+	}
+
+	switch(argv[1][0]) {
+	case 'r':
+		cmd = SYREN_RD;
+		break;
+	case 'w':
+		cmd = SYREN_WR;
+		break;
+	case 'R':
+		cmd = SYREN_OLD_RD;
+		break;
+	case 'W':
+		cmd = SYREN_OLD_WR;
+		break;
+	default:
+		return usage();
+	}
+
+	if (cmd == SYREN_WR || cmd == SYREN_OLD_WR) {
+		if (argc < 4)
+			return usage();
+		sio.value = strtoul(argv[3], 0, 0);
+	}
+
+	fd = open("/dev/eac", O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "can't open /dev/eac\n");
+		return 1;
+	}
+
+	if (strcasecmp(argv[2], "all") == 0) {
+		int i;
+		if (cmd != SYREN_RD && cmd != SYREN_OLD_RD) {
+			fprintf(stderr, "can only read all registers\n");
+			return 1;
+		}
+
+		for (i = 0; registers[i].name; i++) {
+			sio.page = registers[i].page;
+			sio.addr = registers[i].addr;
+			if (ioctl(fd, cmd, &sio) < 0) {
+				fprintf(stderr, "%s: error\n", registers[i].name);
+			} else {
+				fprintf(stderr, "%s: %04x\n", registers[i].name, sio.value);
+			}
+		}
+
+		close(fd);
+		return 0;
+	}
+
+	r = find_reg(argv[2]);
+	if (r == NULL) {
+		strcpy(name, argv[2]);
+		char *addr_str = strchr(argv[2], ':');
+		if (addr_str == NULL)
+			return usage();
+		*addr_str++ = 0;
+		sio.page = strtoul(argv[2], 0, 0);
+		sio.addr = strtoul(addr_str, 0, 0);
+	} else {
+		strcpy(name, r->name);
+		sio.page = r->page;
+		sio.addr = r->addr;
+	}
+
+	if (ioctl(fd, cmd, &sio) < 0) {
+		fprintf(stderr, "ioctl(%d) failed\n", cmd);
+		return 1;
+	}
+
+	if (cmd == SYREN_RD || cmd == SYREN_OLD_RD) {
+		printf("%s: %04x\n", name, sio.value);
+	} else {
+		printf("wrote %04x to %s\n", sio.value, name);
+	}
+
+	close(fd);
+
+	return 0;
+}
+
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
new file mode 100644
index 0000000..0eac390
--- /dev/null
+++ b/toolbox/toolbox.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int, char **);
+
+static int toolbox_main(int argc, char **argv)
+{
+    // "toolbox foo ..." is equivalent to "foo ..."
+    if (argc > 1) {
+        return main(argc - 1, argv + 1);
+    } else {
+        printf("Toolbox!\n");
+        return 0;
+    }
+}
+
+#define TOOL(name) int name##_main(int, char**);
+#include "tools.h"
+#undef TOOL
+
+static struct 
+{
+    const char *name;
+    int (*func)(int, char**);
+} tools[] = {
+    { "toolbox", toolbox_main },
+#define TOOL(name) { #name, name##_main },
+#include "tools.h"
+#undef TOOL
+    { 0, 0 },
+};
+
+int main(int argc, char **argv)
+{
+    int i;
+    char *name = argv[0];
+
+    if((argc > 1) && (argv[1][0] == '@')) {
+        name = argv[1] + 1;
+        argc--;
+        argv++;
+    } else {
+        char *cmd = strrchr(argv[0], '/');
+        if (cmd)
+            name = cmd + 1;
+    }
+
+    for(i = 0; tools[i].name; i++){
+        if(!strcmp(tools[i].name, name)){
+            return tools[i].func(argc, argv);
+        }
+    }
+
+    printf("%s: no such tool\n", argv[0]);
+    return -1;
+}
diff --git a/toolbox/top.c b/toolbox/top.c
new file mode 100644
index 0000000..dcc0843
--- /dev/null
+++ b/toolbox/top.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+struct cpu_info {
+    long unsigned utime, ntime, stime, itime;
+    long unsigned iowtime, irqtime, sirqtime;
+};
+
+#define PROC_NAME_LEN 64
+#define THREAD_NAME_LEN 32
+
+struct proc_info {
+    struct proc_info *next;
+    pid_t pid;
+    pid_t tid;
+    uid_t uid;
+    gid_t gid;
+    char name[PROC_NAME_LEN];
+    char tname[THREAD_NAME_LEN];
+    char state;
+    long unsigned utime;
+    long unsigned stime;
+    long unsigned delta_utime;
+    long unsigned delta_stime;
+    long unsigned delta_time;
+    long vss;
+    long rss;
+    int num_threads;
+};
+
+struct proc_list {
+    struct proc_info **array;
+    int size;
+};
+
+#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
+
+#define INIT_PROCS 50
+#define THREAD_MULT 8
+static struct proc_info **old_procs, **new_procs;
+static int num_old_procs, num_new_procs;
+static struct proc_info *free_procs;
+static int num_used_procs, num_free_procs;
+
+static int max_procs, delay, iterations, threads;
+
+static struct cpu_info old_cpu, new_cpu;
+
+static struct proc_info *alloc_proc(void);
+static void free_proc(struct proc_info *proc);
+static void read_procs(void);
+static int read_stat(char *filename, struct proc_info *proc);
+static void add_proc(int proc_num, struct proc_info *proc);
+static int read_cmdline(char *filename, struct proc_info *proc);
+static int read_status(char *filename, struct proc_info *proc);
+static void print_procs(void);
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
+static void free_old_procs(void);
+static int (*proc_cmp)(const void *a, const void *b);
+static int proc_cpu_cmp(const void *a, const void *b);
+static int proc_vss_cmp(const void *a, const void *b);
+static int proc_rss_cmp(const void *a, const void *b);
+static int proc_thr_cmp(const void *a, const void *b);
+static int numcmp(long long a, long long b);
+static void usage(char *cmd);
+
+int top_main(int argc, char *argv[]) {
+    int i;
+
+    num_used_procs = num_free_procs = 0;
+
+    max_procs = 0;
+    delay = 3;
+    iterations = -1;
+    proc_cmp = &proc_cpu_cmp;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-m")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -m expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            max_procs = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-n")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -n expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            iterations = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-d")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -d expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-s")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -s expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            ++i;
+            if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
+            if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
+            if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
+            if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
+            fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
+            exit(EXIT_FAILURE);
+        }
+        if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (threads && proc_cmp == &proc_thr_cmp) {
+        fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    free_procs = NULL;
+
+    num_new_procs = num_old_procs = 0;
+    new_procs = old_procs = NULL;
+
+    read_procs();
+    while ((iterations == -1) || (iterations-- > 0)) {
+        old_procs = new_procs;
+        num_old_procs = num_new_procs;
+        memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
+        sleep(delay);
+        read_procs();
+        print_procs();
+        free_old_procs();
+    }
+
+    return 0;
+}
+
+static struct proc_info *alloc_proc(void) {
+    struct proc_info *proc;
+
+    if (free_procs) {
+        proc = free_procs;
+        free_procs = free_procs->next;
+        num_free_procs--;
+    } else {
+        proc = malloc(sizeof(*proc));
+        if (!proc) die("Could not allocate struct process_info.\n");
+    }
+
+    num_used_procs++;
+
+    return proc;
+}
+
+static void free_proc(struct proc_info *proc) {
+    proc->next = free_procs;
+    free_procs = proc;
+
+    num_used_procs--;
+    num_free_procs++;
+}
+
+#define MAX_LINE 256
+
+static void read_procs(void) {
+    DIR *proc_dir, *task_dir;
+    struct dirent *pid_dir, *tid_dir;
+    char filename[64];
+    FILE *file;
+    int proc_num;
+    struct proc_info *proc;
+    pid_t pid, tid;
+
+    int i;
+
+    proc_dir = opendir("/proc");
+    if (!proc_dir) die("Could not open /proc.\n");
+
+    new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
+    num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
+
+    file = fopen("/proc/stat", "r");
+    if (!file) die("Could not open /proc/stat.\n");
+    fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
+            &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
+    fclose(file);
+
+    proc_num = 0;
+    while ((pid_dir = readdir(proc_dir))) {
+        if (!isdigit(pid_dir->d_name[0]))
+            continue;
+
+        pid = atoi(pid_dir->d_name);
+        
+        struct proc_info cur_proc;
+        
+        if (!threads) {
+            proc = alloc_proc();
+
+            proc->pid = proc->tid = pid;
+
+            sprintf(filename, "/proc/%d/stat", pid);
+            read_stat(filename, proc);
+
+            sprintf(filename, "/proc/%d/cmdline", pid);
+            read_cmdline(filename, proc);
+
+            sprintf(filename, "/proc/%d/status", pid);
+            read_status(filename, proc);
+
+            proc->num_threads = 0;
+        } else {
+            sprintf(filename, "/proc/%d/cmdline", pid);
+            read_cmdline(filename, &cur_proc);
+
+            sprintf(filename, "/proc/%d/status", pid);
+            read_status(filename, &cur_proc);
+            
+            proc = NULL;
+        }
+
+        sprintf(filename, "/proc/%d/task", pid);
+        task_dir = opendir(filename);
+        if (!task_dir) continue;
+
+        while ((tid_dir = readdir(task_dir))) {
+            if (!isdigit(tid_dir->d_name[0]))
+                continue;
+
+            if (threads) {
+                tid = atoi(tid_dir->d_name);
+
+                proc = alloc_proc();
+
+                proc->pid = pid; proc->tid = tid;
+
+                sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+                read_stat(filename, proc);
+
+                strcpy(proc->name, cur_proc.name);
+                proc->uid = cur_proc.uid;
+                proc->gid = cur_proc.gid;
+
+                add_proc(proc_num++, proc);
+            } else {
+                proc->num_threads++;
+            }
+        }
+
+        closedir(task_dir);
+        
+        if (!threads)
+            add_proc(proc_num++, proc);
+    }
+
+    for (i = proc_num; i < num_new_procs; i++)
+        new_procs[i] = NULL;
+
+    closedir(proc_dir);
+}
+
+static int read_stat(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char buf[MAX_LINE], *open_paren, *close_paren;
+    int res, idx;
+
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    fgets(buf, MAX_LINE, file);
+    fclose(file);
+
+    /* Split at first '(' and last ')' to get process name. */
+    open_paren = strchr(buf, '(');
+    close_paren = strrchr(buf, ')');
+    if (!open_paren || !close_paren) return 1;
+
+    *open_paren = *close_paren = '\0';
+    strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
+    proc->tname[THREAD_NAME_LEN-1] = 0;
+    
+    /* Scan rest of string. */
+    sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+                 "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld",
+                 &proc->state, &proc->utime, &proc->stime, &proc->vss, &proc->rss);
+
+    return 0;
+}
+
+static void add_proc(int proc_num, struct proc_info *proc) {
+    int i;
+
+    if (proc_num >= num_new_procs) {
+        new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
+        if (!new_procs) die("Could not expand procs array.\n");
+        for (i = num_new_procs; i < 2 * num_new_procs; i++)
+            new_procs[i] = NULL;
+        num_new_procs = 2 * num_new_procs;
+    }
+    new_procs[proc_num] = proc;
+}
+
+static int read_cmdline(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char line[MAX_LINE];
+
+    line[0] = '\0';
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    fgets(line, MAX_LINE, file);
+    fclose(file);
+    if (strlen(line) > 0) {
+        strncpy(proc->name, line, PROC_NAME_LEN);
+        proc->name[PROC_NAME_LEN-1] = 0;
+    } else
+        proc->name[0] = 0;
+    return 0;
+}
+
+static int read_status(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char line[MAX_LINE];
+    unsigned int uid, gid;
+
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    while (fgets(line, MAX_LINE, file)) {
+        sscanf(line, "Uid: %u", &uid);
+        sscanf(line, "Gid: %u", &gid);
+    }
+    fclose(file);
+    proc->uid = uid; proc->gid = gid;
+    return 0;
+}
+
+static void print_procs(void) {
+    int i;
+    struct proc_info *old_proc, *proc;
+    long unsigned total_delta_time;
+    struct passwd *user;
+    struct group *group;
+    char *user_str, user_buf[20];
+    char *group_str, group_buf[20];
+
+    for (i = 0; i < num_new_procs; i++) {
+        if (new_procs[i]) {
+            old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
+            if (old_proc) {
+                new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
+                new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
+            } else {
+                new_procs[i]->delta_utime = 0;
+                new_procs[i]->delta_stime = 0;
+            }
+            new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
+        }
+    }
+
+    total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
+                        + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
+                     - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
+                        + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
+
+    qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
+
+    printf("\n\n\n");
+    printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
+            ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100  / total_delta_time,
+            ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
+            ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
+            ((new_cpu.irqtime + new_cpu.sirqtime)
+                    - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
+    printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
+            new_cpu.utime - old_cpu.utime,
+            new_cpu.ntime - old_cpu.ntime,
+            new_cpu.stime - old_cpu.stime,
+            new_cpu.itime - old_cpu.itime,
+            new_cpu.iowtime - old_cpu.iowtime,
+            new_cpu.irqtime - old_cpu.irqtime,
+            new_cpu.sirqtime - old_cpu.sirqtime,
+            total_delta_time);
+    printf("\n");
+    if (!threads) 
+        printf("%5s %4s %1s %5s %7s %7s %-8s %s\n", "PID", "CPU%", "S", "#THR", "VSS", "RSS", "UID", "Name");
+    else
+        printf("%5s %5s %4s %1s %7s %7s %-8s %-15s %s\n", "PID", "TID", "CPU%", "S", "VSS", "RSS", "UID", "Thread", "Proc");
+
+    for (i = 0; i < num_new_procs; i++) {
+        proc = new_procs[i];
+
+        if (!proc || (max_procs && (i >= max_procs)))
+            break;
+        user  = getpwuid(proc->uid);
+        group = getgrgid(proc->gid);
+        if (user && user->pw_name) {
+            user_str = user->pw_name;
+        } else {
+            snprintf(user_buf, 20, "%d", proc->uid);
+            user_str = user_buf;
+        }
+        if (group && group->gr_name) {
+            group_str = group->gr_name;
+        } else {
+            snprintf(group_buf, 20, "%d", proc->gid);
+            group_str = group_buf;
+        }
+        if (!threads) 
+            printf("%5d %3ld%% %c %5d %6ldK %6ldK %-8.8s %s\n", proc->pid, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
+                proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
+        else
+            printf("%5d %5d %3ld%% %c %6ldK %6ldK %-8.8s %-15s %s\n", proc->pid, proc->tid, proc->delta_time * 100 / total_delta_time, proc->state,
+                proc->vss / 1024, proc->rss * getpagesize() / 1024, user_str, proc->tname, proc->name);
+    }
+}
+
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
+    int i;
+
+    for (i = 0; i < num_old_procs; i++)
+        if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
+            return old_procs[i];
+
+    return NULL;
+}
+
+static void free_old_procs(void) {
+    int i;
+
+    for (i = 0; i < num_old_procs; i++)
+        if (old_procs[i])
+            free_proc(old_procs[i]);
+
+    free(old_procs);
+}
+
+static int proc_cpu_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->delta_time, pb->delta_time);
+}
+
+static int proc_vss_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->vss, pb->vss);
+}
+
+static int proc_rss_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->rss, pb->rss);
+}
+
+static int proc_thr_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->num_threads, pb->num_threads);
+}
+
+static int numcmp(long long a, long long b) {
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
+static void usage(char *cmd) {
+    fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]\n"
+                    "    -m num  Maximum number of processes to display.\n"
+                    "    -n num  Updates to show before exiting.\n"
+                    "    -d num  Seconds to wait between updates.\n"
+                    "    -s col  Column to sort by (cpu,vss,rss,thr).\n"
+                    "    -t      Show threads instead of processes.\n"
+                    "    -h      Display this help screen.\n",
+        cmd);
+}
diff --git a/toolbox/umount.c b/toolbox/umount.c
new file mode 100644
index 0000000..92c6076
--- /dev/null
+++ b/toolbox/umount.c
@@ -0,0 +1,74 @@
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/loop.h>
+
+// FIXME - only one loop mount is supported at a time
+#define LOOP_DEVICE "/dev/block/loop0"
+
+static int is_loop_mount(const char* path)
+{
+    FILE* f;
+    int count;
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    int result = 0;
+    int path_length = strlen(path);
+    
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        fprintf(stdout, "could not open /proc/mounts\n");
+        return -1;
+    }
+
+    do {
+        count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+        if (count == 3) {
+            if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) {
+                result = 1;
+                break;
+            }
+        }
+    } while (count == 3);
+
+    fclose(f);
+    return result;
+}
+
+int umount_main(int argc, char *argv[])
+{
+    int loop, loop_fd;
+    
+    if(argc != 2) {
+        fprintf(stderr,"umount <path>\n");
+        return 1;
+    }
+
+    loop = is_loop_mount(argv[1]);
+    if(umount(argv[1])){
+        fprintf(stderr,"failed.\n");
+        return 1;
+    }
+
+    if (loop) {
+        // free the loop device
+        loop_fd = open(LOOP_DEVICE, O_RDONLY);
+        if (loop_fd < -1) {
+            perror("open loop device failed");
+            return 1;
+        }
+        if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+            perror("ioctl LOOP_CLR_FD failed");
+            return 1;
+        }
+
+        close(loop_fd);
+    }
+
+    return 0;
+}
diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c
new file mode 100644
index 0000000..600f136
--- /dev/null
+++ b/toolbox/vmstat.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+struct state {
+    long procs_r;
+    long procs_b;
+
+    long mem_free;
+    long mem_mapped;
+    long mem_anon;
+    long mem_slab;
+
+    long sys_in;
+    long sys_cs;
+    long sys_flt;
+
+    long cpu_us;
+    long cpu_ni;
+    long cpu_sy;
+    long cpu_id;
+    long cpu_wa;
+    long cpu_ir;
+    long cpu_si;
+};
+
+#define MAX_LINE 256
+
+char line[MAX_LINE];
+
+static void read_state(struct state *s);
+static int read_meminfo(struct state *s);
+static int read_stat(struct state *s);
+static int read_vmstat(struct state *s);
+static void print_header(void);
+static void print_line(struct state *old, struct state *new);
+static void usage(char *cmd);
+
+int vmstat_main(int argc, char *argv[]) {
+    struct state s[2];
+    int iterations, delay, header_interval;
+    int toggle, count;
+    int i;
+
+    iterations = 0;
+    delay = 1;
+    header_interval = 20;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-n")) { 
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -n requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            iterations = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-d")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -d requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-r")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -r requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            header_interval = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+        usage(argv[0]);
+	exit(EXIT_FAILURE);
+    }
+
+    toggle = 0;
+    count = 0;
+
+    if (!header_interval)
+        print_header();
+    read_state(&s[1 - toggle]);
+    while ((iterations == 0) || (iterations-- > 0)) {
+        sleep(delay);
+        read_state(&s[toggle]);
+        if (header_interval) {
+            if (count == 0)
+                print_header();
+            count = (count + 1) % header_interval;
+        }
+        print_line(&s[1 - toggle], &s[toggle]);
+        toggle = 1 - toggle;
+    }
+
+    return 0;
+}
+
+static void read_state(struct state *s) {
+    int error;
+
+    error = read_meminfo(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/meminfo: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+
+    error = read_stat(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/stat: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+
+    error = read_vmstat(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/vmstat: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+}
+
+static int read_meminfo(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/meminfo", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        sscanf(line, "MemFree: %ld kB", &s->mem_free);
+        sscanf(line, "AnonPages: %ld kB", &s->mem_anon);
+        sscanf(line, "Mapped: %ld kB", &s->mem_mapped);
+        sscanf(line, "Slab: %ld kB", &s->mem_slab);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static int read_stat(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/stat", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        if (!strncmp(line, "cpu ", 4)) {
+            sscanf(line, "cpu  %ld %ld %ld %ld %ld %ld %ld",
+                &s->cpu_us, &s->cpu_ni, &s->cpu_sy, &s->cpu_id, &s->cpu_wa,
+                &s->cpu_ir, &s->cpu_si);
+        }
+        sscanf(line, "intr %ld", &s->sys_in);
+        sscanf(line, "ctxt %ld", &s->sys_cs);
+        sscanf(line, "procs_running %ld", &s->procs_r);
+        sscanf(line, "procs_blocked %ld", &s->procs_b);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static int read_vmstat(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/vmstat", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        sscanf(line, "pgmajfault %ld", &s->sys_flt);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static void print_header(void) {
+    printf("%-5s  %-27s  %-14s  %-17s\n", "procs", "memory", "system", "cpu");
+    printf("%2s %2s  %6s %6s %6s %6s  %4s %4s %4s  %2s %2s %2s %2s %2s %2s\n", "r", "b", "free", "mapped", "anon", "slab", "in", "cs", "flt", "us", "ni", "sy", "id", "wa", "ir");
+}
+
+/* Jiffies to percent conversion */
+#define JP(jif) ((jif) * 100 / (HZ))
+#define NORM(var) ((var) = (((var) > 99) ? (99) : (var)))
+
+static void print_line(struct state *old, struct state *new) {
+    int us, ni, sy, id, wa, ir;
+    us = JP(new->cpu_us - old->cpu_us); NORM(us);
+    ni = JP(new->cpu_ni - old->cpu_ni); NORM(ni);
+    sy = JP(new->cpu_sy - old->cpu_sy); NORM(sy);
+    id = JP(new->cpu_id - old->cpu_id); NORM(id);
+    wa = JP(new->cpu_wa - old->cpu_wa); NORM(wa);
+    ir = JP(new->cpu_ir - old->cpu_ir); NORM(ir);
+    printf("%2ld %2ld  %6ld %6ld %6ld %6ld  %4ld %4ld %4ld  %2d %2d %2d %2d %2d %2d\n",
+        new->procs_r ? (new->procs_r - 1) : 0, new->procs_b,
+        new->mem_free, new->mem_mapped, new->mem_anon, new->mem_slab,
+        new->sys_in - old->sys_in, new->sys_cs - old->sys_cs, new->sys_flt - old->sys_flt,
+        us, ni, sy, id, wa, ir);
+}
+
+static void usage(char *cmd) {
+    fprintf(stderr, "Usage: %s [ -h ] [ -n iterations ] [ -d delay ] [ -r header_repeat ]\n"
+                    "    -n iterations     How many rows of data to print.\n"
+                    "    -d delay          How long to sleep between rows.\n"
+                    "    -r header_repeat  How many rows to print before repeating\n"
+                    "                      the header.  Zero means never repeat.\n"
+                    "    -h                Displays this help screen.\n",
+        cmd);
+}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
new file mode 100644
index 0000000..d311992
--- /dev/null
+++ b/toolbox/watchprops.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <cutils/properties.h>
+
+#include <sys/atomics.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+
+extern prop_area *__system_property_area__;
+
+typedef struct pwatch pwatch;
+
+struct pwatch
+{
+    const prop_info *pi;
+    unsigned serial;
+};
+
+static pwatch watchlist[1024];
+
+static void announce(const prop_info *pi)
+{
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    char *x;
+    
+    __system_property_read(pi, name, value);
+
+    for(x = value; *x; x++) {
+        if((*x < 32) || (*x > 127)) *x = '.';
+    }
+
+    fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
+}
+
+int watchprops_main(int argc, char *argv[])
+{
+    prop_area *pa = __system_property_area__;
+    unsigned serial = pa->serial;
+    unsigned count = pa->count;
+    unsigned n;
+    
+    if(count >= 1024) exit(1);
+
+    for(n = 0; n < count; n++) {
+        watchlist[n].pi = __system_property_find_nth(n);
+        watchlist[n].serial = watchlist[n].pi->serial;
+    }
+
+    for(;;) {
+        do {
+            __futex_wait(&pa->serial, serial, 0);
+        } while(pa->serial == serial);
+
+        while(count < pa->count){
+            watchlist[count].pi = __system_property_find_nth(count);
+            watchlist[count].serial = watchlist[n].pi->serial;
+            announce(watchlist[count].pi);
+            count++;
+            if(count == 1024) exit(1);
+        }
+
+        for(n = 0; n < count; n++){
+            unsigned tmp = watchlist[n].pi->serial;
+            if(watchlist[n].serial != tmp) {
+                announce(watchlist[n].pi);
+                watchlist[n].serial = tmp;
+            }
+        }
+    }
+    return 0;
+}
diff --git a/toolbox/wipe.c b/toolbox/wipe.c
new file mode 100644
index 0000000..7e263fd
--- /dev/null
+++ b/toolbox/wipe.c
@@ -0,0 +1,176 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+
+/* Directories created by init defined in system/rootdir/init.rc */
+static char *INIT_DIRS[] = {
+    "/system/etc/ppp",
+    "/data/misc",
+    "/data/local",
+    "/data/local/tmp",
+    "/data/data",
+    "/data/app_private",
+    "/data/app",
+    NULL
+};
+
+static void wipe (const char *path);
+
+static int usage()
+{
+    fprintf(stderr, "wipe <system|data|all>\n\n"
+                    "system means '/system'\n"
+                    "data means '/data'\n");
+
+    return -1;
+}
+
+int wipe_main (int argc, char *argv[])
+{
+    char *whatToWipe;
+
+    if (argc != 2) return usage();
+
+    whatToWipe = argv[1];
+
+    if (0 == strcmp (whatToWipe, "system")) {
+        fprintf(stdout, "Wiping /system\n");
+        wipe ("/system");
+        fprintf(stdout, "Done wiping /android\n");
+    } else if (0 == strcmp (whatToWipe, "data")) {
+        fprintf(stdout, "Wiping /data\n");
+        wipe ("/data");
+        fprintf(stdout, "Done wiping /data\n");
+    } else if (0 == strcmp (whatToWipe, "all")) {
+        fprintf(stdout, "Wiping /system and /data\n");
+        wipe ("/system");
+        wipe ("/data");
+        fprintf(stdout, "Done wiping /system and /data\n");
+    } else if (0 == strcmp(whatToWipe, "nuke")) {
+		int ret;
+		fprintf(stdout, "Nuking the device...\n");
+		wipe ("/system");
+        wipe ("/data");
+		fprintf(stdout, "Device nuked! Rebooting...\n");
+		ret = reboot(RB_AUTOBOOT);
+	    if (ret < 0) {
+	        fprintf(stderr, "Reboot failed, %s\n", strerror(errno));
+	        return 1;
+	    }
+	} else {
+        return usage();
+    }
+
+    return 0;
+}
+
+static char nameBuffer[PATH_MAX];
+static struct stat statBuffer;
+
+static void wipe (const char *path) 
+{
+    DIR *dir;
+    struct dirent *de;
+    int ret;
+
+    dir = opendir(path);
+
+    if (dir == NULL) {
+        fprintf (stderr, "Error opendir'ing %s '%s'\n",
+                    path, strerror(errno));
+        return;
+    }
+
+    char *filenameOffset;
+
+    strcpy(nameBuffer, path);
+    strcat(nameBuffer, "/");
+
+    filenameOffset = nameBuffer + strlen(nameBuffer);
+
+    for (;;) {
+        de = readdir(dir);
+
+        if (de == NULL) {
+            break;
+        }
+
+        if (0 == strcmp(de->d_name, ".")
+                || 0 == strcmp(de->d_name, "..")
+                || 0 == strcmp(de->d_name, "lost+found")
+        ) {
+            continue;
+        }
+
+        strcpy(filenameOffset, de->d_name);
+
+        ret = lstat (nameBuffer, &statBuffer);
+
+        if (ret != 0) {
+            fprintf(stderr, "stat() error on '%s' '%s'\n", 
+                    nameBuffer, strerror(errno));
+        }
+
+        if(S_ISDIR(statBuffer.st_mode)) {
+            int i;
+            char *newpath;
+
+#if 0
+            closedir(dir);
+#endif
+
+            newpath = strdup(nameBuffer);
+            wipe(newpath);
+
+            /* Leave directories created by init, they have special permissions. */
+            for (i = 0; INIT_DIRS[i]; i++) {
+                if (strcmp(INIT_DIRS[i], newpath) == 0) {
+                    break;
+                }
+            }
+            if (INIT_DIRS[i] == NULL) {
+                ret = rmdir(newpath);
+                if (ret != 0) {
+                    fprintf(stderr, "rmdir() error on '%s' '%s'\n", 
+                        newpath, strerror(errno));
+                }
+            }
+
+            free(newpath);
+
+#if 0
+            dir = opendir(path);
+            if (dir == NULL) {
+                fprintf (stderr, "Error opendir'ing %s '%s'\n",
+                            path, strerror(errno));
+                return;
+            }
+#endif
+
+            strcpy(nameBuffer, path);
+            strcat(nameBuffer, "/");
+
+        } else {
+            ret = unlink(nameBuffer);
+
+            if (ret != 0) {
+                fprintf(stderr, "unlink() error on '%s' '%s'\n", 
+                    nameBuffer, strerror(errno));
+            }
+        }
+    }
+
+    closedir(dir);
+
+}
diff --git a/vold/Android.mk b/vold/Android.mk
new file mode 100644
index 0000000..3dd9f87
--- /dev/null
+++ b/vold/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                \
+                  vold.c       \
+                  cmd_dispatch.c \
+                  uevent.c       \
+                  mmc.c          \
+		  misc.c         \
+                  blkdev.c       \
+                  ums.c          \
+                  geom_mbr_enc.c \
+                  volmgr.c       \
+                  media.c        \
+                  volmgr_vfat.c  \
+                  volmgr_ext3.c  \
+                  logwrapper.c   \
+                  ProcessKiller.c\
+                  switch.c       \
+                  format.c       \
+                  devmapper.c
+
+LOCAL_MODULE:= vold
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/vold/ProcessKiller.c b/vold/ProcessKiller.c
new file mode 100644
index 0000000..fc3eb37
--- /dev/null
+++ b/vold/ProcessKiller.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+** mountd process killer
+*/
+
+#include "vold.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <sys/stat.h>
+
+
+static boolean ReadSymLink(const char* path, char* link)
+{
+    struct stat s;
+    int length;
+
+    if (lstat(path, &s) < 0)
+        return false;
+    if ((s.st_mode & S_IFMT) != S_IFLNK)
+        return false;
+   
+    // we have a symlink    
+    length = readlink(path, link, PATH_MAX - 1);
+    if (length <= 0) 
+        return false;
+    link[length] = 0;
+    return true;
+}
+
+static boolean PathMatchesMountPoint(const char* path, const char* mountPoint)
+{
+    int length = strlen(mountPoint);
+    if (length > 1 && strncmp(path, mountPoint, length) == 0)
+    {
+        // we need to do extra checking if mountPoint does not end in a '/'
+        if (mountPoint[length - 1] == '/')
+            return true;
+        // if mountPoint does not have a trailing slash, we need to make sure
+        // there is one in the path to avoid partial matches.
+        return (path[length] == 0 || path[length] == '/');
+    }
+    
+    return false;
+}
+
+static void GetProcessName(int pid, char buffer[PATH_MAX])
+{
+    int fd;
+    sprintf(buffer, "/proc/%d/cmdline", pid);
+    fd = open(buffer, O_RDONLY);
+    if (fd < 0) {
+        strcpy(buffer, "???");
+    } else {
+        int length = read(fd, buffer, PATH_MAX - 1);
+        buffer[length] = 0;
+        close(fd);
+    }
+}
+
+static boolean CheckFileDescriptorSymLinks(int pid, const char* mountPoint)
+{
+    DIR*    dir;
+    struct dirent* de;
+    boolean fileOpen = false;
+    char    path[PATH_MAX];
+    char    link[PATH_MAX];
+    int     parent_length;
+
+    // compute path to process's directory of open files
+    sprintf(path, "/proc/%d/fd", pid);
+    dir = opendir(path);
+    if (!dir)
+        return false;
+
+    // remember length of the path
+    parent_length = strlen(path);
+    // append a trailing '/'
+    path[parent_length++] = '/';
+    
+    while ((de = readdir(dir)) != 0 && !fileOpen) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+        
+        // append the file name, after truncating to parent directory
+        path[parent_length] = 0;
+        strcat(path, de->d_name);
+
+        if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint))
+        {
+            char    name[PATH_MAX];
+            GetProcessName(pid, name);
+            LOG_ERROR("process %s (%d) has open file %s", name, pid, link);
+            fileOpen = true;
+        }
+    }
+
+    closedir(dir);
+    return fileOpen;
+}
+
+static boolean CheckFileMaps(int pid, const char* mountPoint)
+{
+    FILE*   file;
+    char    buffer[PATH_MAX + 100];
+    boolean mapOpen = false;
+
+    sprintf(buffer, "/proc/%d/maps", pid);
+    file = fopen(buffer, "r");
+    if (!file)
+        return false;
+    
+    while (!mapOpen && fgets(buffer, sizeof(buffer), file))
+    {
+        // skip to the path
+        const char* path = strchr(buffer, '/');
+        if (path && PathMatchesMountPoint(path, mountPoint))
+        {
+            char    name[PATH_MAX];
+            GetProcessName(pid, name);
+            LOG_ERROR("process %s (%d) has open file map for %s", name, pid, path);
+            mapOpen = true;
+        }
+    }
+    
+    fclose(file);
+    return mapOpen;
+}
+
+static boolean CheckSymLink(int pid, const char* mountPoint, const char* name, const char* message)
+{
+    char    path[PATH_MAX];
+    char    link[PATH_MAX];
+
+    sprintf(path, "/proc/%d/%s", pid, name);
+    if (ReadSymLink(path, link) && PathMatchesMountPoint(link, mountPoint)) 
+    {
+        char    name[PATH_MAX];
+        GetProcessName(pid, name);
+        LOG_ERROR("process %s (%d) has %s in %s", name, pid, message, mountPoint);
+        return true;
+    }
+    else
+        return false;
+}
+
+static int get_pid(const char* s)
+{
+    int result = 0;
+    while (*s) {
+        if (!isdigit(*s)) return -1;
+        result = 10 * result + (*s++ - '0');
+    }
+    return result;
+}
+
+// hunt down and kill processes that have files open on the given mount point
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded)
+{
+    DIR*    dir;
+    struct dirent* de;
+
+    LOG_ERROR("KillProcessesWithOpenFiles %s", mountPoint);
+    dir = opendir("/proc");
+    if (!dir) return;
+
+    while ((de = readdir(dir)) != 0)
+    {
+        boolean killed = false;
+        // does the name look like a process ID?
+        int pid = get_pid(de->d_name);
+        if (pid == -1) continue;
+
+        if (CheckFileDescriptorSymLinks(pid, mountPoint)    // check for open files
+                || CheckFileMaps(pid, mountPoint)           // check for mmap()
+                || CheckSymLink(pid, mountPoint, "cwd", "working directory")    // check working directory
+                || CheckSymLink(pid, mountPoint, "root", "chroot")              // check for chroot()
+                || CheckSymLink(pid, mountPoint, "exe", "executable path")      // check executable path
+            ) 
+        {
+            int i;
+            boolean hit = false;
+
+            for (i = 0; i < num_excluded; i++) {
+                if (pid == excluded[i]) {
+                    LOG_ERROR("I just need a little more TIME captain!");
+                    hit = true;
+                    break;
+                }
+            }
+
+            if (!hit) {
+                LOG_ERROR("Killing process %d", pid);
+                kill(pid, (sigkill ? SIGKILL : SIGTERM));
+            }
+        }
+    }
+
+    closedir(dir);
+}        
diff --git a/vold/blkdev.c b/vold/blkdev.c
new file mode 100644
index 0000000..533bc35
--- /dev/null
+++ b/vold/blkdev.c
@@ -0,0 +1,319 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "diskmbr.h"
+
+#define DEBUG_BLKDEV 0
+
+static blkdev_list_t *list_root = NULL;
+
+static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
+                                int minor, char *type, struct media *media);
+
+static int fat_valid_media(unsigned char media)
+{                       
+        return 0xf8 <= media || media == 0xf0;
+}                               
+
+char *blkdev_get_devpath(blkdev_t *blk)
+{
+    char *dp = malloc(256);
+    sprintf(dp, "%s/vold/%d:%d", DEVPATH, blk->major, blk->minor);
+    return dp;
+}
+
+int blkdev_refresh(blkdev_t *blk)
+{
+    int fd = 0;
+    char *devpath = NULL;
+    unsigned char *block = NULL;
+    int i, rc;
+
+    if (!(block = malloc(512)))
+        goto out;
+
+    /*
+     * Get the device size
+     */
+    devpath = blkdev_get_devpath(blk);
+
+    if ((fd = open(devpath, O_RDONLY)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath, strerror(errno));
+        return -errno;
+    }
+
+    if (ioctl(fd, BLKGETSIZE, &blk->nr_sec)) {
+        LOGE("Unable to get device size (%m)");
+        return -errno;
+    }
+    close(fd);
+    free(devpath);
+
+    /*
+     * Open the disk partition table
+     */
+    devpath = blkdev_get_devpath(blk->disk);
+    if ((fd = open(devpath, O_RDONLY)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath,
+             strerror(errno));
+        free(devpath);
+        return -errno;
+    }
+
+    free(devpath);
+
+    if ((rc = read(fd, block, 512)) != 512) {
+        LOGE("Unable to read device partition table (%d, %s)",
+             rc, strerror(errno));
+        goto out;
+    }
+
+    /*
+     * If we're a disk, then process the partition table. Otherwise we're
+     * a partition so get the partition type
+     */
+
+    if (blk->type == blkdev_disk) {
+        blk->nr_parts = 0;
+
+        if ((block[0x1fe] != 0x55) || (block[0x1ff] != 0xAA)) {
+            LOG_VOL("Disk %d:%d does not contain a partition table",
+                    blk->major, blk->minor);
+            goto out;
+        }
+
+        for (i = 0; i < 4; i++) {
+            struct dos_partition part;
+
+            dos_partition_dec(block + DOSPARTOFF + i * sizeof(struct dos_partition), &part);
+            if (part.dp_flag != 0 && part.dp_flag != 0x80) {
+                struct fat_boot_sector *fb = (struct fat_boot_sector *) &block[0];
+             
+                if (!i && fb->reserved && fb->fats && fat_valid_media(fb->media)) {
+                    LOG_VOL("Detected FAT filesystem in partition table");
+                    break;
+                } else {
+                    LOG_VOL("Partition table looks corrupt");
+                    break;
+                }
+            }
+            if (part.dp_size != 0 && part.dp_typ != 0)
+                blk->nr_parts++;
+        }
+    } else if (blk->type == blkdev_partition) {
+        struct dos_partition part;
+        int part_no = blk->minor -1;
+
+        dos_partition_dec(block + DOSPARTOFF + part_no * sizeof(struct dos_partition), &part);
+        blk->part_type = part.dp_typ;
+    }
+
+ out:
+
+    if (block)
+        free(block);
+
+    char tmp[255];
+    char tmp2[32];
+    sprintf(tmp, "%s (blkdev %d:%d), %u secs (%u MB)",
+                 (blk->type == blkdev_disk ? "Disk" : "Partition"),
+                 blk->major, blk->minor,
+                 blk->nr_sec,
+                 (uint32_t) (((uint64_t) blk->nr_sec * 512) / 1024) / 1024);
+
+    if (blk->type == blkdev_disk) 
+        sprintf(tmp2, " %d partitions", blk->nr_parts);
+    else
+        sprintf(tmp2, " type 0x%x", blk->part_type);
+
+    strcat(tmp, tmp2);
+    LOG_VOL(tmp);
+
+    close(fd);
+
+    return 0;
+}
+
+blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type)
+{
+    return _blkdev_create(disk, devpath, major, minor, type, media);
+}
+
+static blkdev_t *_blkdev_create(blkdev_t *disk, char *devpath, int major,
+                                int minor, char *type, struct media *media)
+{
+    blkdev_t *new;
+    struct blkdev_list *list_entry;
+
+    if (disk && disk->type != blkdev_disk) {
+        LOGE("Non disk parent specified for blkdev!");
+        return NULL;
+    }
+
+    if (!(new = malloc(sizeof(blkdev_t))))
+        return NULL;
+
+    memset(new, 0, sizeof(blkdev_t));
+
+    if (!(list_entry = malloc(sizeof(struct blkdev_list)))) {
+        free (new);
+        return NULL;
+    }
+    list_entry->dev = new;
+    list_entry->next = NULL;
+
+    if (!list_root)
+        list_root = list_entry;
+    else {
+        struct blkdev_list *list_scan = list_root;
+        while (list_scan->next)
+            list_scan = list_scan->next;
+        list_scan->next = list_entry;
+    }
+
+    if (devpath)
+        new->devpath = strdup(devpath);
+    new->major = major;
+    new->minor = minor;
+    new->media = media;
+    new->nr_sec = 0xffffffff;
+
+    if (disk)
+        new->disk = disk;
+    else 
+        new->disk = new; // Note the self disk pointer
+
+    /* Create device nodes */
+    char nodepath[255];
+    mode_t mode = 0666 | S_IFBLK;
+    dev_t dev = (major << 8) | minor;
+
+    sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, major, minor);
+    if (mknod(nodepath, mode, dev) < 0) {
+        LOGE("Error making device nodes for '%s' (%s)",
+             nodepath, strerror(errno));
+    }
+
+    if (!strcmp(type, "disk"))
+        new->type = blkdev_disk;
+    else if (!strcmp(type, "partition"))
+        new->type = blkdev_partition;
+    else {
+        LOGE("Unknown block device type '%s'", type);
+        new->type = blkdev_unknown;
+    }
+
+    return new;
+}
+
+void blkdev_destroy(blkdev_t *blkdev)
+{
+    struct blkdev_list *list_next;
+
+    if (list_root->dev == blkdev) {
+        list_next = list_root->next;
+        free (list_root);
+        list_root = list_next;
+    } else {
+        struct blkdev_list *list_scan = list_root;
+        while (list_scan->next->dev != blkdev)
+            list_scan = list_scan -> next;
+        list_next = list_scan->next->next;
+        free(list_scan->next);
+        list_scan->next = list_next;
+    }
+
+    if (blkdev->devpath)
+        free(blkdev->devpath);
+
+    char nodepath[255];
+    sprintf(nodepath, "%s/vold/%d:%d", DEVPATH, blkdev->major, blkdev->minor);
+    unlink(nodepath);
+
+    free(blkdev);
+}
+
+blkdev_t *blkdev_lookup_by_path(char *devpath)
+{
+    struct blkdev_list *list_scan = list_root;
+
+    while (list_scan) {
+        if (!strcmp(list_scan->dev->devpath, devpath)) 
+            return list_scan->dev;
+        list_scan = list_scan->next;
+    }
+    return NULL;
+}
+
+blkdev_t *blkdev_lookup_by_devno(int maj, int min)
+{
+    struct blkdev_list *list_scan = list_root;
+
+    while (list_scan) {
+        if ((list_scan->dev->major == maj) &&
+            (list_scan->dev->minor == min))
+            return list_scan->dev;
+        list_scan = list_scan->next;
+    }
+    return NULL;
+}
+
+/*
+ * Given a disk device, return the number of partitions which 
+ * have yet to be processed.
+ */
+int blkdev_get_num_pending_partitions(blkdev_t *blk)
+{
+    struct blkdev_list *list_scan = list_root;
+    int num = blk->nr_parts;
+
+    if (blk->type != blkdev_disk)
+        return -EINVAL;
+
+    while (list_scan) {
+        if (list_scan->dev->type != blkdev_partition)
+            goto next;
+
+        if (list_scan->dev->major != blk->major)
+            goto next;
+
+        if (list_scan->dev->nr_sec != 0xffffffff &&
+            list_scan->dev->devpath) {
+            num--;
+        }
+ next:
+        list_scan = list_scan->next;
+    }
+    return num;
+}
+
diff --git a/vold/blkdev.h b/vold/blkdev.h
new file mode 100644
index 0000000..e789739
--- /dev/null
+++ b/vold/blkdev.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BLKDEV_H
+#define _BLKDEV_H
+
+#include <sys/types.h>
+
+struct media;
+
+enum blk_type { blkdev_unknown, blkdev_disk, blkdev_partition };
+
+struct blkdev {
+    char          *devpath;
+    enum blk_type type;
+    struct media  *media;
+
+    // If type == blkdev_disk then nr_parts = number of partitions
+    int           nr_parts;
+
+    // If type == blkdev_partition then part_type = partition type
+    uint8_t       part_type;
+    // If type == blkdev_partition
+    struct blkdev *disk;
+
+    unsigned int  nr_sec;
+
+    int           major;
+    int           minor;
+};
+
+struct blkdev_list {
+    struct blkdev      *dev;
+    struct blkdev_list *next;
+};
+
+typedef struct blkdev blkdev_t;
+typedef struct blkdev_list blkdev_list_t;
+
+blkdev_t *blkdev_create(blkdev_t *disk, char *devpath, int major, int minor, struct media *media, char *type);
+blkdev_t *blkdev_create_pending_partition(blkdev_t *disk, char *dev_fspath, int major, int minor, struct media *media);
+blkdev_t *blkdev_lookup_by_path(char *devpath);
+blkdev_t *blkdev_lookup_by_devno(int maj, int min);
+char *blkdev_get_devpath(blkdev_t *blk);
+
+void blkdev_destroy(blkdev_t *blk);
+
+int blkdev_get_num_pending_partitions(blkdev_t *blk);
+int blkdev_refresh(blkdev_t *blk);
+#endif
diff --git a/vold/cmd_dispatch.c b/vold/cmd_dispatch.c
new file mode 100644
index 0000000..ab65fd9
--- /dev/null
+++ b/vold/cmd_dispatch.c
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "vold.h"
+#include "cmd_dispatch.h"
+#include "ums.h"
+#include "volmgr.h"
+
+struct cmd_dispatch {
+    char *cmd;
+    int (* dispatch) (char *);
+};
+
+static void dispatch_cmd(char *cmd);
+static int do_send_ums_status(char *cmd);
+static int do_set_ums_enable(char *cmd);
+static int do_mount_volume(char *cmd);
+static int do_eject_media(char *cmd);
+static int do_format_media(char *cmd);
+
+static struct cmd_dispatch dispatch_table[] = {
+    { VOLD_CMD_ENABLE_UMS,      do_set_ums_enable },
+    { VOLD_CMD_DISABLE_UMS,     do_set_ums_enable },
+    { VOLD_CMD_SEND_UMS_STATUS, do_send_ums_status },
+    { VOLD_CMD_MOUNT_VOLUME,    do_mount_volume },
+    { VOLD_CMD_EJECT_MEDIA,     do_eject_media },
+    { VOLD_CMD_FORMAT_MEDIA,    do_format_media },
+    { NULL, NULL }
+};
+
+int process_framework_command(int socket)
+{
+    int rc;
+    char buffer[101];
+
+    if ((rc = read(socket, buffer, sizeof(buffer) -1)) < 0) {
+        LOGE("Unable to read framework command (%s)", strerror(errno));
+        return -errno;
+    } else if (!rc)
+        return -ECONNRESET;
+
+    int start = 0;
+    int i;
+
+    buffer[rc] = 0;
+
+    for (i = 0; i < rc; i++) {
+        if (buffer[i] == 0) {
+            dispatch_cmd(buffer + start);
+            start = i + 1;
+        }
+    }
+    return 0;
+}
+
+static void dispatch_cmd(char *cmd)
+{
+    struct cmd_dispatch *c;
+
+    LOG_VOL("dispatch_cmd(%s):", cmd);
+
+    for (c = dispatch_table; c->cmd != NULL; c++) {
+        if (!strncmp(c->cmd, cmd, strlen(c->cmd))) {
+            c->dispatch(cmd);
+            return;
+        }
+    }
+
+    LOGE("No cmd handlers defined for '%s'", cmd);
+}
+
+static int do_send_ums_status(char *cmd)
+{
+    return ums_send_status();
+}
+
+static int do_set_ums_enable(char *cmd)
+{
+    if (!strcmp(cmd, VOLD_CMD_ENABLE_UMS))
+        return volmgr_enable_ums(true);
+
+    return volmgr_enable_ums(false);
+}
+
+static int do_mount_volume(char *cmd)
+{
+    return volmgr_start_volume_by_mountpoint(&cmd[strlen("mount_volume:")]);
+}
+
+static int do_format_media(char *cmd)
+{
+    return volmgr_format_volume(&cmd[strlen("format_media:")]);
+}
+
+static int do_eject_media(char *cmd)
+{
+    return volmgr_stop_volume_by_mountpoint(&cmd[strlen("eject_media:")]);
+}
diff --git a/vold/cmd_dispatch.h b/vold/cmd_dispatch.h
new file mode 100644
index 0000000..f8ba6b3
--- /dev/null
+++ b/vold/cmd_dispatch.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CMD_DISPATCH_H
+#define _CMD_DISPATCH_H
+
+// These must match the strings in java/android/android/os/UsbListener.java
+#define VOLD_CMD_ENABLE_UMS         "enable_ums"
+#define VOLD_CMD_DISABLE_UMS        "disable_ums"
+#define VOLD_CMD_SEND_UMS_STATUS    "send_ums_status"
+
+// these commands should contain a volume mount point after the colon
+#define VOLD_CMD_MOUNT_VOLUME       "mount_volume:"
+#define VOLD_CMD_EJECT_MEDIA        "eject_media:"
+#define VOLD_CMD_FORMAT_MEDIA       "format_media:"
+
+#endif
diff --git a/vold/devmapper.c b/vold/devmapper.c
new file mode 100644
index 0000000..ef44c90
--- /dev/null
+++ b/vold/devmapper.c
@@ -0,0 +1,463 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sched.h>
+#include <fcntl.h>
+
+#include <sys/mount.h>
+
+#include <linux/loop.h>
+#include <linux/dm-ioctl.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/properties.h>
+
+#include "vold.h"
+#include "devmapper.h"
+
+#define DEBUG_DEVMAPPER 1
+
+static void *_align(void *ptr, unsigned int a)
+{
+        register unsigned long agn = --a;
+
+        return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+static struct dm_ioctl *_dm_ioctl_setup(struct devmapping *dm, int flags)
+{
+    void *buffer;
+    void *p;
+    const size_t min_size = 16 * 1024;
+    size_t len = sizeof(struct dm_ioctl);
+    struct dm_ioctl *io;
+    struct dm_target_spec *tgt;
+    int i;
+    char params[1024];
+    char key[80];
+
+    key[0] = '\0';
+    for (i = 0; i < (int) sizeof(dm->key); i++) {
+        char tmp[8];
+
+        sprintf(tmp, "%02x", dm->key[i]);
+        strcat(key, tmp);
+    }
+
+    char srcdev[128];
+
+    // XXX: Handle non crypt targets and non twofish (use param)
+    if (dm->src_type == dmsrc_loopback)
+        strcpy(srcdev, dm->type_data.loop.loop_dev);
+    else if (dm->src_type == dmsrc_partition)
+        strcpy(srcdev, dm->type_data.part.part_dev);
+
+    sprintf(params, "twofish %s 0 %s 0", key, srcdev);
+
+LOG_VOL("Params = '%s'", params);
+
+    if (len < min_size)
+        len = min_size;
+
+    if (!(buffer = malloc(len))) {
+        LOGE("out of memory");
+        return NULL;
+    }
+
+    memset(buffer, 0, len);
+    io = buffer;
+    tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+    io->version[0] = 4;
+    io->version[1] = 0;
+    io->version[2] = 0;
+
+    io->data_size = len;
+    io->data_start = sizeof(struct dm_ioctl);
+
+    io->flags = flags;
+    io->dev = 0;
+
+    io->target_count = 1;
+    io->event_nr = 1;
+    strncpy(io->name, dm->target, sizeof(io->name));
+
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = (dm->size_mb * (1024 * 1024)) / 512;
+    strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type));
+
+    p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+    strcpy((char *) p, params);
+    p+= strlen(params) + 1;
+
+    p = _align(p, 8);
+    tgt->next = p - buffer;
+
+    return io;
+}
+
+static int get_next_available_dm()
+{
+    int i;
+
+    for (i = 0; i < 8; i++) {
+        char path[255];
+        sprintf(path, "/dev/block/dm-%d", i);
+        if ((access(path, F_OK) < 0) && (errno == ENOENT))
+            return i;
+    }
+
+    LOGE("Out of device mapper numbers");
+    return -1;
+}
+
+static int create_devmapping(struct devmapping *dm)
+{
+    struct dm_ioctl *io;
+    int rc, fd;
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("create_devmapping():");
+#endif
+
+    if (dm->dm_no < 0) {
+        LOGE("Invalid dm_no set");
+        return -EINVAL;
+    }
+
+    if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOGE("Error opening device mapper (%d)", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(dm, 0))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        close(fd);
+        return -ENOMEM;
+    }
+
+    if ((rc = ioctl(fd, DM_DEV_CREATE, io)) < 0) {
+        LOGE("device-mapper create ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(dm, DM_STATUS_TABLE_FLAG))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(fd, DM_TABLE_LOAD, io)) < 0) {
+        LOGE("device-mapper load ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+    free(io);
+
+    if (!(io = _dm_ioctl_setup(dm, 0))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(fd, DM_DEV_SUSPEND, io)) < 0) {
+        LOGE("device-mapper resume ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (fd);
+    return rc;
+}
+
+static int loopback_start(struct devmapping *dm)
+{
+    int i;
+    int fd;
+    char filename[255];
+    int rc;
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("loopback_start(%s):", dm->type_data.loop.loop_src);
+#endif
+
+    for (i = 0; i < MAX_LOOP; i++) {
+        struct loop_info info;
+
+        sprintf(filename, "/dev/block/loop%d", i);
+
+        if ((fd = open(filename, O_RDWR)) < 0) {
+            LOGE("Unable to open %s (%s)", filename, strerror(errno));
+            return -errno;
+        }
+
+        rc = ioctl(fd, LOOP_GET_STATUS, &info);
+        if (rc < 0 && errno == ENXIO)
+            break;
+
+        close(fd);
+
+        if (rc < 0) {
+            LOGE("Unable to get loop status for %s (%s)", filename,
+                 strerror(errno));
+            return -errno;
+        }
+    }
+
+    if (i == MAX_LOOP) {
+        LOGE("Out of loop devices");
+        return -ENOSPC;
+    }
+
+    int file_fd;
+
+    if ((file_fd = open(dm->type_data.loop.loop_src, O_RDWR)) < 0) {
+        LOGE("Unable to open %s (%s)", dm->type_data.loop.loop_src,
+             strerror(errno));
+        return -errno;
+    }
+
+    if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
+        LOGE("Error setting up loopback interface (%s)", strerror(errno));
+        return -errno;
+    }
+
+    dm->type_data.loop.loop_dev = strdup(filename);
+    dm->type_data.loop.loop_no  = i;
+
+    close(fd);
+    close(file_fd);
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("Loop setup on %s for %s", dm->type_data.loop.loop_dev,
+            dm->type_data.loop.loop_src);
+#endif
+
+    return 0;
+}
+
+int devmapper_start(struct devmapping *dm)
+{
+    int rc;
+    char src_blkdev_path[255];
+
+#if DEBUG_DEVMAPPER
+    LOG_VOL("devmapper_start()");
+#endif
+
+    if (dm->src_type == dmsrc_loopback) {
+       if ((rc = loopback_start(dm)) < 0)
+           return rc;
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    } else {
+        LOGE("devmapper_start(): Unsupported source type '%d'", dm->src_type);
+        return -ENOENT;
+    }
+
+    /*
+     * Configure the device mapper
+     */
+    if ((rc = create_devmapping(dm)) < 0) {
+        LOGE("Failed to create devmapping (%d)", rc);
+        // XXX: if loopback then tear down
+        return rc;
+    }
+
+    return 0;
+}
+
+struct devmapping *devmapper_init(char *src, char *src_type, uint32_t size_mb,
+                  char *target, char *params, char *tgt_fs, char *mediapath)
+{
+    struct devmapping *dm;
+
+    if (!(dm = malloc(sizeof(struct devmapping)))) {
+        LOGE("devmapper_init(): out of memory");
+        return NULL;
+    }
+
+    memset(dm, 0, sizeof(struct devmapping));
+
+    if (!strcmp(src_type, "loopback_file")) {
+        dm->src_type = dmsrc_loopback;
+        dm->type_data.loop.loop_src = strdup(src);
+    } else if (!strncmp(src_type, "partition ", strlen("partition "))) {
+        dm->src_type = dmsrc_partition;
+        char *p = strtok(src_type, " ");
+        if (!p) {
+            LOGE("Invalid partition specifier");
+            goto out_free;
+        }
+        dm->type_data.part.part_type = strtoul(p, NULL, 0);
+    } else {
+        LOGE("Invalid src_type defined (%s)", src_type);
+        goto out_free;
+    }
+    
+    // XXX: Validate these
+    dm->size_mb = size_mb;
+    dm->target = strdup(target);
+    dm->params = strdup(params);
+    dm->tgt_fs = strdup(tgt_fs);
+    
+    if ((dm->dm_no = get_next_available_dm()) < 0)
+        goto out_free;
+
+    sprintf(mediapath, "/devices/virtual/block/dm-%d", dm->dm_no);
+
+    if (!(dm->media = media_create(mediapath,
+                                   "unknown",
+                                   "unknown",
+                                   media_devmapper))) {
+        LOGE("Unable to create media");
+        goto out_free;
+    }
+
+    return dm;
+ out_free:
+    if (dm->target)
+        free(dm->target);
+    if (dm->params)
+        free(dm->params);
+    if (dm->tgt_fs)
+        free(dm->tgt_fs);
+
+    free(dm);
+    return NULL;
+}
+
+int devmapper_genesis(struct devmapping *dm)
+{
+
+    if (dm->src_type == dmsrc_loopback) {
+        int fd;
+
+        LOG_VOL("devmapper_genesis(): Working on %s", 
+                dm->type_data.loop.loop_src);
+
+        unlink(dm->type_data.loop.loop_src);
+
+        LOG_VOL("devmapper_genesis(): Creating imagefile (%u MB)",
+                dm->size_mb);
+
+        if ((fd = creat(dm->type_data.loop.loop_src, 0600)) < 0) {
+            LOGE("Error creating imagefile (%s)", strerror(errno));
+            return -errno;
+        }
+
+        if (ftruncate(fd, (dm->size_mb * (1024 * 1024))) < 0) {
+            LOGE("Error truncating imagefile (%s)", strerror(errno));
+            close(fd);
+            return -errno;
+        }
+        close(fd);
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    }
+
+    return devmapper_start(dm);
+}
+
+static int destroy_devmapping(struct devmapping *dm)
+{
+    struct dm_ioctl       *io;
+    int                   dmFd;
+    int                   rc = 0;
+
+    LOG_VOL("destroy_devmapping():");
+
+    if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
+        LOGE("Error opening device mapper (%d)", errno);
+        return -errno;
+    }
+
+    if (!(io = _dm_ioctl_setup(dm, DM_PERSISTENT_DEV_FLAG))) {
+        LOGE("Unable to setup ioctl (out of memory)");
+        rc = -ENOMEM;
+        goto out_nofree;
+    }
+
+    if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
+        LOGE("device-mapper remove ioctl failed (%d)", errno);
+        rc = -errno;
+        goto out_free;
+    }
+
+out_free:
+    free (io);
+out_nofree:
+    close (dmFd);
+    return rc;
+}
+
+static int loopback_stop(struct devmapping *dm)
+{
+    char devname[255];
+    int device_fd;
+    int rc = 0;
+
+    LOG_VOL("loopback_stop():");
+
+    device_fd = open(dm->type_data.loop.loop_dev, O_RDONLY);
+    if (device_fd < 0) {
+        LOG_ERROR("Failed to open loop (%d)", errno);
+        return -errno;
+    }
+
+    if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
+        LOG_ERROR("Failed to destroy loop (%d)", errno);
+        rc = -errno;
+    }
+
+    close(device_fd);
+    return rc;
+}
+
+int devmapper_stop(struct devmapping *dm)
+{
+    int rc;
+
+    LOG_VOL("devmapper_stop():");
+
+    if ((rc = destroy_devmapping(dm)))
+        return rc;
+
+    if (dm->src_type == dmsrc_loopback) {
+        if ((rc = loopback_stop(dm)))
+            return rc;
+    } else if (dm->src_type == dmsrc_partition) {
+        LOGE("partition maps not yet supported");
+        return -ENOSYS;
+    }
+    return 0;
+}
diff --git a/vold/devmapper.h b/vold/devmapper.h
new file mode 100644
index 0000000..3d8cab3
--- /dev/null
+++ b/vold/devmapper.h
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEVMAPPER_H
+#define _DEVMAPPER_H
+
+#include <pthread.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "media.h"
+
+#define MAX_LOOP 8
+
+enum dm_src_type {
+    dmsrc_unknown,
+    dmsrc_loopback,
+    dmsrc_partition,
+};
+
+struct loop_data {
+    char *loop_src;
+
+    char *loop_dev;
+    int  loop_no;
+};
+
+struct part_data {
+    char part_type;
+    
+    char *part_dev;
+};
+
+struct devmapping {
+        enum dm_src_type src_type;
+        union {
+            struct loop_data loop;
+            struct part_data part;
+        } type_data;
+        
+        uint32_t         size_mb;
+	char             *target;
+        char             *params;
+        char             *tgt_fs;
+
+        unsigned char key[16];
+        int           dm_no;
+
+        media_t *media;
+};
+
+struct devmapping *devmapper_init(char *, char *, unsigned int, char *, char *, char *, char *);
+int devmapper_start(struct devmapping *);
+int devmapper_stop(struct devmapping *);
+int devmapper_genesis(struct devmapping *);
+#endif
diff --git a/vold/diskmbr.h b/vold/diskmbr.h
new file mode 100644
index 0000000..417e7ca
--- /dev/null
+++ b/vold/diskmbr.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1987, 1988, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      @(#)disklabel.h 8.2 (Berkeley) 7/10/94
+ * $FreeBSD: src/sys/sys/diskmbr.h,v 1.99.2.1 2005/01/31 23:26:56 imp Exp $
+ */
+
+#ifndef _SYS_DISKMBR_H_
+#define _SYS_DISKMBR_H_
+
+#define DOSBBSECTOR     0       /* DOS boot block relative sector number */
+#define DOSPARTOFF      446
+#define DOSPARTSIZE     16
+#define NDOSPART        4
+#define NEXTDOSPART     32
+#define DOSMAGICOFFSET  510
+#define DOSMAGIC        0xAA55
+
+#define DOSPTYP_386BSD  0xa5    /* 386BSD partition type */
+#define DOSPTYP_LINSWP  0x82    /* Linux swap partition */
+#define DOSPTYP_LINUX   0x83    /* Linux partition */
+#define DOSPTYP_PMBR    0xee    /* GPT Protective MBR */
+#define DOSPTYP_EXT     5       /* DOS extended partition */
+#define DOSPTYP_EXTLBA  15      /* DOS extended partition */
+
+struct dos_partition {
+        unsigned char   dp_flag;        /* bootstrap flags */
+        unsigned char   dp_shd;         /* starting head */
+        unsigned char   dp_ssect;       /* starting sector */
+        unsigned char   dp_scyl;        /* starting cylinder */
+        unsigned char   dp_typ;         /* partition type */
+        unsigned char   dp_ehd;         /* end head */
+        unsigned char   dp_esect;       /* end sector */
+        unsigned char   dp_ecyl;        /* end cylinder */
+        u_int32_t       dp_start;       /* absolute starting sector number */
+        u_int32_t       dp_size;        /* partition size in sectors */
+};
+#ifdef CTASSERT
+CTASSERT(sizeof (struct dos_partition) == DOSPARTSIZE);
+#endif
+
+void dos_partition_dec(void const *pp, struct dos_partition *d);
+void dos_partition_enc(void *pp, struct dos_partition *d);
+
+#define DPSECT(s) ((s) & 0x3f)          /* isolate relevant bits of sector */
+#define DPCYL(c, s) ((c) + (((s) & 0xc0)<<2)) /* and those that are cylinder */
+
+#define DIOCSMBR        _IOW('M', 129, u_char[512])
+
+#endif /* !_SYS_DISKMBR_H_ */
diff --git a/vold/format.c b/vold/format.c
new file mode 100755
index 0000000..dd0515c
--- /dev/null
+++ b/vold/format.c
@@ -0,0 +1,113 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include <linux/fs.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "format.h"
+#include "diskmbr.h"
+#include "logwrapper.h"
+
+static char MKDOSFS_PATH[] = "/system/bin/mkdosfs";
+static char MKE2FS_PATH[] = "/system/bin/mke2fs";
+
+int format_partition(blkdev_t *part, char *type)
+{
+    char *devpath;
+    int rc = -EINVAL;
+
+    devpath = blkdev_get_devpath(part);
+
+    if (!strcmp(type, FORMAT_TYPE_FAT32)) {
+        char *args[7];
+        args[0] = MKDOSFS_PATH;
+        args[1] = "-F 32";
+        args[2] = "-c 32";
+        args[3] = "-n 2";
+        args[4] = "-O android";
+        args[5] = devpath;
+        args[6] = NULL;
+        rc = logwrap(6, args);
+    } else {
+        char *args[7];
+        args[0] = MKE2FS_PATH;
+        args[1] = "-b 4096";
+        args[2] = "-m 1";
+        args[3] = "-L android";
+        args[4] = "-v";
+        args[5] = devpath;
+        args[6] = NULL;
+        rc = logwrap(6, args);
+    }
+ 
+    free(devpath);
+
+    if (rc == 0) {
+        LOG_VOL("Filesystem formatted OK");
+        return 0;
+    } else {
+        LOGE("Format failed (unknokwn exit code %d)", rc);
+        return -EIO;
+    }
+    return 0;
+}
+
+int initialize_mbr(blkdev_t *disk)
+{
+    int fd, rc;
+    unsigned char block[512];
+    struct dos_partition part;
+    char *devpath;
+
+    devpath = blkdev_get_devpath(disk);
+
+    memset(&part, 0, sizeof(part));
+    part.dp_flag = 0x80;
+    part.dp_typ = 0xc;
+    part.dp_start = ((1024 * 64) / 512) + 1;
+    part.dp_size = disk->nr_sec - part.dp_start;
+
+    memset(block, 0, sizeof(block));
+    block[0x1fe] = 0x55;
+    block[0x1ff] = 0xaa;
+
+    dos_partition_enc(block + DOSPARTOFF, &part);
+
+    if ((fd = open(devpath, O_RDWR)) < 0) {
+        LOGE("Error opening disk file (%s)", strerror(errno));
+        return -errno;
+    }
+    free(devpath);
+
+    if (write(fd, block, sizeof(block)) < 0) {
+        LOGE("Error writing MBR (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+
+    if (ioctl(fd, BLKRRPART, NULL) < 0) {
+        LOGE("Error re-reading partition table (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    close(fd);
+    return 0;
+}
diff --git a/vold/format.h b/vold/format.h
new file mode 100644
index 0000000..73cc012
--- /dev/null
+++ b/vold/format.h
@@ -0,0 +1,26 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FORMAT_H
+#define _FORMAT_H
+
+#define FORMAT_TYPE_EXT2  "ext2"
+#define FORMAT_TYPE_FAT32 "fat32"
+
+int format_partition(blkdev_t *part, char *type);
+int initialize_mbr(blkdev_t *disk);
+#endif
diff --git a/vold/geom_mbr_enc.c b/vold/geom_mbr_enc.c
new file mode 100644
index 0000000..f1f8339
--- /dev/null
+++ b/vold/geom_mbr_enc.c
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Functions to encode or decode struct dos_partition into a bytestream
+ * of correct endianess and packing.  These functions do no validation
+ * or sanity checking, they only pack/unpack the fields correctly.
+ *
+ * NB!  This file must be usable both in kernel and userland.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+
+#include "diskmbr.h"
+
+static __inline uint32_t __unused
+le32dec(const void *buf)
+{
+        const uint8_t *p = (const uint8_t *)buf;
+
+        return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+static __inline void
+le32enc(void *pp, uint32_t u)
+{
+        unsigned char *p = (unsigned char *)pp;
+
+        p[0] = u & 0xff;
+        p[1] = (u >> 8) & 0xff;
+        p[2] = (u >> 16) & 0xff;
+        p[3] = (u >> 24) & 0xff;
+}
+
+void
+dos_partition_dec(void const *pp, struct dos_partition *d)
+{
+        unsigned char const *p = pp;
+
+        d->dp_flag = p[0];
+        d->dp_shd = p[1];
+        d->dp_ssect = p[2];
+        d->dp_scyl = p[3];
+        d->dp_typ = p[4];
+        d->dp_ehd = p[5];
+        d->dp_esect = p[6];
+        d->dp_ecyl = p[7];
+        d->dp_start = le32dec(p + 8);
+        d->dp_size = le32dec(p + 12);
+}
+
+void
+dos_partition_enc(void *pp, struct dos_partition *d)
+{
+        unsigned char *p = pp;
+
+        p[0] = d->dp_flag;
+        p[1] = d->dp_shd;
+        p[2] = d->dp_ssect;
+        p[3] = d->dp_scyl;
+        p[4] = d->dp_typ;
+        p[5] = d->dp_ehd;
+        p[6] = d->dp_esect;
+        p[7] = d->dp_ecyl;
+        le32enc(p + 8, d->dp_start);
+        le32enc(p + 12, d->dp_size);
+}
diff --git a/vold/logwrapper.c b/vold/logwrapper.c
new file mode 100644
index 0000000..25d2281
--- /dev/null
+++ b/vold/logwrapper.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "private/android_filesystem_config.h"
+#include "cutils/log.h"
+
+int parent(const char *tag, int parent_read) {
+    int status;
+    char buffer[4096];
+
+    int a = 0;  // start index of unprocessed data
+    int b = 0;  // end index of unprocessed data
+    int sz;
+    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
+
+        sz += b;
+        // Log one line at a time
+        for (b = 0; b < sz; b++) {
+            if (buffer[b] == '\r') {
+                buffer[b] = '\0';
+            } else if (buffer[b] == '\n') {
+                buffer[b] = '\0';
+                LOG(LOG_INFO, tag, &buffer[a]);
+                a = b + 1;
+            }
+        }
+
+        if (a == 0 && b == sizeof(buffer) - 1) {
+            // buffer is full, flush
+            buffer[b] = '\0';
+            LOG(LOG_INFO, tag, &buffer[a]);
+            b = 0;
+        } else if (a != b) {
+            // Keep left-overs
+            b -= a;
+            memmove(buffer, &buffer[a], b);
+            a = 0;
+        } else {
+            a = 0;
+            b = 0;
+        }
+
+    }
+    // Flush remaining data
+    if (a != b) {
+        buffer[b] = '\0';
+        LOG(LOG_INFO, tag, &buffer[a]);
+    }
+    status = 0xAAAA;
+    if (wait(&status) != -1) {  // Wait for child
+        if (WIFEXITED(status)) {
+            LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+                    WEXITSTATUS(status));
+            return WEXITSTATUS(status);
+        } else if (WIFSIGNALED(status))
+            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+                    WTERMSIG(status));
+        else if (WIFSTOPPED(status))
+            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+                    WSTOPSIG(status));
+    } else
+        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+                strerror(errno), errno);
+    return -EAGAIN;
+}
+
+void child(int argc, char* argv[]) {
+    // create null terminated argv_child array
+    char* argv_child[argc + 1];
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
+
+    // XXX: PROTECT FROM VIKING KILLER
+    if (execvp(argv_child[0], argv_child)) {
+        LOG(LOG_ERROR, "logwrapper",
+            "executing %s failed: %s", argv_child[0], strerror(errno));
+        exit(-1);
+    }
+}
+
+int logwrap(int argc, char* argv[], pid_t *childPid)
+{
+    pid_t pid;
+
+    int parent_ptty;
+    int child_ptty;
+    char *child_devname = NULL;
+
+    /* Use ptty instead of socketpair so that STDOUT is not buffered */
+    parent_ptty = open("/dev/ptmx", O_RDWR);
+    if (parent_ptty < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty");
+	return -errno;
+    }
+
+    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
+            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
+	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx");
+	return -1;
+    }
+
+    pid = fork();
+    if (pid < 0) {
+	LOG(LOG_ERROR, "logwrapper", "Failed to fork");
+        return -errno;
+    } else if (pid == 0) {
+        child_ptty = open(child_devname, O_RDWR);
+        if (child_ptty < 0) {
+	    LOG(LOG_ERROR, "logwrapper", "Problem with child ptty");
+            return -errno;
+        }
+
+        // redirect stdout and stderr
+        close(parent_ptty);
+        dup2(child_ptty, 1);
+        dup2(child_ptty, 2);
+        close(child_ptty);
+
+        child(argc, argv);
+    } else {
+        return parent(argv[0], parent_ptty);
+    }
+
+    return 0;
+}
diff --git a/vold/logwrapper.h b/vold/logwrapper.h
new file mode 100644
index 0000000..602e24c
--- /dev/null
+++ b/vold/logwrapper.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGWRAPPER_H
+#define _LOGWRAPPER_H
+
+#include <stdlib.h>
+int logwrap(int argc, char* argv[]);
+#endif
diff --git a/vold/media.c b/vold/media.c
new file mode 100644
index 0000000..40637ff
--- /dev/null
+++ b/vold/media.c
@@ -0,0 +1,165 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "media.h"
+
+static media_list_t *list_root = NULL;
+
+media_t *media_create(char *devpath, char *name, char *serial, media_type_t type)
+{
+    media_list_t *list_entry;
+    media_t *new;
+
+    if (!(new = malloc(sizeof(media_t))))
+        return NULL;
+
+    memset(new, 0, sizeof(media_t));
+
+    if (!(list_entry = malloc(sizeof(media_list_t)))) {
+        free(new);
+        return NULL;
+    }
+    list_entry->media = new;
+    list_entry->next = NULL;
+
+    if (!list_root)
+        list_root = list_entry;
+    else {
+        media_list_t *list_scan = list_root;
+        while(list_scan->next)
+            list_scan = list_scan->next;
+        list_scan->next = list_entry;
+    }
+     
+    new->devpath = strdup(devpath);
+    new->name = strdup(name);
+    if (!serial)
+        new->serial = 0;
+    else
+        new->serial = strtoul(serial, NULL, 0);
+
+    new->media_type = type;
+
+    return new;
+}
+
+void media_destroy(media_t *media)
+{
+    media_list_t *list_next;
+
+    if (list_root->media == media) {
+        list_next = list_root->next;
+        free(list_root);
+        list_root = list_next;
+    } else {
+        media_list_t *list_scan = list_root;
+        while (list_scan->next->media != media)
+            list_scan = list_scan -> next;
+        list_next = list_scan->next->next;
+        free(list_scan->next);
+        list_scan->next = list_next;
+    }
+
+    free(media->devpath);
+    free(media->name);
+
+    while(media->devs)
+        media_remove_blkdev(media, media->devs->dev);
+    free(media);
+}
+
+media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match)
+{
+    media_list_t *list_scan = list_root;
+
+    while (list_scan) {
+        if (fuzzy_match) {
+            if (!strncmp(list_scan->media->devpath, devpath, strlen(devpath)))
+                return list_scan->media;
+        } else {
+            if (!strcmp(list_scan->media->devpath, devpath))
+                return list_scan->media;
+        }
+        list_scan = list_scan->next;
+    }
+#if DEBUG_MEDIA
+    LOG_VOL("media_lookup_by_path(): No media found @ %s", devpath);
+#endif
+    return NULL;
+}
+
+int media_add_blkdev(media_t *card, blkdev_t *dev)
+{
+    blkdev_list_t *list_entry;
+
+    if (!(list_entry = malloc(sizeof(blkdev_list_t)))) {
+        LOGE("Out of memory");
+        return -ENOMEM;
+    }
+    
+    list_entry->next = NULL;
+    list_entry->dev = dev;
+    if (!card->devs)
+        card->devs = list_entry;
+    else {
+        blkdev_list_t *scan = card->devs;
+
+        while(scan->next)
+            scan = scan->next;
+
+        scan->next = list_entry;
+    }
+    return 0;
+}
+
+void media_remove_blkdev(media_t *card, blkdev_t *dev)
+{
+    if (card->devs->dev == dev)
+        card->devs = card->devs->next;
+    else {
+        blkdev_list_t *scan = card->devs;
+        while (scan->next->dev != dev)
+            scan = scan -> next;
+        blkdev_list_t *next = scan->next->next;
+        free(scan->next);
+        scan->next = next;
+    }
+}
+
+media_t *media_lookup_by_dev(blkdev_t *dev)
+{
+    media_list_t *media_scan = list_root;
+
+    while (media_scan) {
+        blkdev_list_t *blk_scan = media_scan->media->devs;
+        while (blk_scan) {
+            if (blk_scan->dev == dev)
+                return media_scan->media;
+            blk_scan = blk_scan->next;
+        }
+        media_scan = media_scan->next;
+    }
+    return NULL;
+}
diff --git a/vold/media.h b/vold/media.h
new file mode 100644
index 0000000..567ce04
--- /dev/null
+++ b/vold/media.h
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MEDIA_H
+#define _MEDIA_H
+
+#include <sys/types.h>
+
+#include "blkdev.h"
+
+typedef enum media_type {
+    media_unknown,
+    media_mmc,
+    media_devmapper,
+} media_type_t;
+
+typedef struct media {
+    char           *devpath;
+    char           *name;
+    uint32_t       serial;
+    media_type_t   media_type;
+
+    blkdev_list_t  *devs;
+} media_t;
+
+typedef struct media_list {
+    media_t           *media;
+    struct media_list *next;
+} media_list_t;
+
+media_t *media_create(char *devpath, char *name, char *serial, enum media_type);
+media_t *media_lookup_by_path(char *devpath, boolean fuzzy_match);
+media_t *media_lookup_by_dev(blkdev_t *dev);
+void media_destroy(media_t *media);
+int media_add_blkdev(media_t *media, blkdev_t *dev);
+void media_remove_blkdev(media_t *media, blkdev_t *dev);
+#endif
diff --git a/vold/misc.c b/vold/misc.c
new file mode 100644
index 0000000..951414c
--- /dev/null
+++ b/vold/misc.c
@@ -0,0 +1,91 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void *read_file(char *filename, ssize_t *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
+char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer)
+{
+    int i;
+
+    strcpy(buffer, path);
+
+    for (i = 0; i < num_elements_to_remove; i++) {
+        char *p = &buffer[strlen(buffer)-1];
+
+        for (p = &buffer[strlen(buffer) -1]; *p != '/'; p--);
+        *p = '\0';
+    }
+
+    return buffer;
+}
+
+char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var)
+{
+    char filename[255];
+    char *p;
+    ssize_t sz;
+
+    sprintf(filename, "/sys%s/%s", devpath, var);
+    p = read_file(filename, &sz);
+    p[(strlen(p) - 1)] = '\0';
+    strncpy(buffer, p, maxlen);
+    free(p);
+    return buffer;
+}
+
diff --git a/vold/mmc.c b/vold/mmc.c
new file mode 100644
index 0000000..0f08964
--- /dev/null
+++ b/vold/mmc.c
@@ -0,0 +1,293 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "mmc.h"
+#include "media.h"
+
+#define DEBUG_BOOTSTRAP 0
+
+static int mmc_bootstrap_controller(char *sysfs_path);
+static int mmc_bootstrap_card(char *sysfs_path);
+static int mmc_bootstrap_block(char *devpath);
+static int mmc_bootstrap_mmcblk(char *devpath);
+static int mmc_bootstrap_mmcblk_partition(char *devpath);
+
+/*
+ * Bootstrap our mmc information.
+ */
+int mmc_bootstrap()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SYSFS_CLASS_MMC_PATH))) {
+        LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_MMC_PATH);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        sprintf(tmp, "%s/%s", SYSFS_CLASS_MMC_PATH, de->d_name);
+        if (mmc_bootstrap_controller(tmp))
+            LOG_ERROR("Error bootstrapping controller '%s' (%m)", tmp);
+    }
+
+    closedir(d);
+
+    return 0;
+}
+
+static int mmc_bootstrap_controller(char *sysfs_path)
+{
+    DIR *d;
+    struct dirent *de;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_controller(%s):", sysfs_path);
+#endif
+    if (!(d = opendir(sysfs_path))) {
+        LOG_ERROR("Unable to open '%s' (%m)", sysfs_path);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        if ((!strcmp(de->d_name, "uevent")) ||
+            (!strcmp(de->d_name, "subsystem")) ||
+            (!strcmp(de->d_name, "device")) ||
+            (!strcmp(de->d_name, "power"))) {
+            continue;
+        }
+
+        sprintf(tmp, "%s/%s", sysfs_path, de->d_name);
+
+        if (mmc_bootstrap_card(tmp) < 0)
+            LOG_ERROR("Error bootstrapping card '%s' (%m)", tmp);
+    } // while
+
+    closedir(d);
+    return 0;   
+}
+
+static int mmc_bootstrap_card(char *sysfs_path)
+{
+    char saved_cwd[255];
+    char new_cwd[255];
+    char *devpath;
+    char *uevent_params[4];
+    char *p;
+    char filename[255];
+    char tmp[255];
+    ssize_t sz;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_card(%s):", sysfs_path);
+#endif
+
+    /*
+     * sysfs_path is based on /sys/class, but we want the actual device class
+     */
+    if (!getcwd(saved_cwd, sizeof(saved_cwd))) {
+        LOGE("Error getting working dir path");
+        return -errno;
+    }
+    
+    if (chdir(sysfs_path) < 0) {
+        LOGE("Unable to chdir to %s (%m)", sysfs_path);
+        return -errno;
+    }
+
+    if (!getcwd(new_cwd, sizeof(new_cwd))) {
+        LOGE("Buffer too small for device path");
+        return -errno;
+    }
+
+    if (chdir(saved_cwd) < 0) {
+        LOGE("Unable to restore working dir");
+        return -errno;
+    }
+
+    devpath = &new_cwd[4]; // Skip over '/sys'
+
+    /*
+     * Collect parameters so we can simulate a UEVENT
+     */
+    sprintf(tmp, "DEVPATH=%s", devpath);
+    uevent_params[0] = (char *) strdup(tmp);
+
+    sprintf(filename, "/sys%s/type", devpath);
+    p = read_file(filename, &sz);
+    p[strlen(p) - 1] = '\0';
+    sprintf(tmp, "MMC_TYPE=%s", p);
+    free(p);
+    uevent_params[1] = (char *) strdup(tmp);
+
+    sprintf(filename, "/sys%s/name", devpath);
+    p = read_file(filename, &sz);
+    p[strlen(p) - 1] = '\0';
+    sprintf(tmp, "MMC_NAME=%s", p);
+    free(p);
+    uevent_params[2] = (char *) strdup(tmp);
+
+    uevent_params[3] = (char *) NULL;
+
+    if (simulate_uevent("mmc", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%m)");
+        return -errno;
+    }
+
+    /*
+     *  Check for block drivers
+     */
+    char block_devpath[255];
+    sprintf(tmp, "%s/block", devpath);
+    sprintf(filename, "/sys%s/block", devpath);
+    if (!access(filename, F_OK)) {
+        if (mmc_bootstrap_block(tmp)) {
+            LOGE("Error bootstrapping block @ %s", tmp);
+        }
+    }
+
+    return 0;
+}
+
+static int mmc_bootstrap_block(char *devpath)
+{
+    char blockdir_path[255];
+    DIR *d;
+    struct dirent *de;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_block(%s):", devpath);
+#endif
+
+    sprintf(blockdir_path, "/sys%s", devpath);
+
+    if (!(d = opendir(blockdir_path))) {
+        LOGE("Failed to opendir %s", devpath);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+        sprintf(tmp, "%s/%s", devpath, de->d_name);
+        if (mmc_bootstrap_mmcblk(tmp))
+            LOGE("Error bootstraping mmcblk @ %s", tmp);
+    }
+    closedir(d);
+    return 0;
+}
+
+static int mmc_bootstrap_mmcblk(char *devpath)
+{
+    char *mmcblk_devname;
+    int part_no;
+    int rc;
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_mmcblk(%s):", devpath);
+#endif
+
+    if ((rc = mmc_bootstrap_mmcblk_partition(devpath))) {
+        LOGE("Error bootstrapping mmcblk partition '%s'", devpath);
+        return rc;
+    }
+
+    for (mmcblk_devname = &devpath[strlen(devpath)];
+         *mmcblk_devname != '/'; mmcblk_devname--);
+    mmcblk_devname++;
+
+    for (part_no = 0; part_no < 4; part_no++) {
+        char part_file[255];
+        sprintf(part_file, "/sys%s/%sp%d", devpath, mmcblk_devname, part_no);
+        if (!access(part_file, F_OK)) {
+            char part_devpath[255];
+
+            sprintf(part_devpath, "%s/%sp%d", devpath, mmcblk_devname, part_no);
+            if (mmc_bootstrap_mmcblk_partition(part_devpath)) 
+                LOGE("Error bootstrapping mmcblk partition '%s'", part_devpath);
+        }
+    }
+
+    return 0;
+}
+
+static int mmc_bootstrap_mmcblk_partition(char *devpath)
+{
+    char filename[255];
+    char *uevent_buffer;
+    ssize_t sz;
+    char *uevent_params[4];
+    char tmp[255];
+    FILE *fp;
+    char line[255];
+
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("mmc_bootstrap_mmcblk_partition(%s):", devpath);
+#endif
+
+    sprintf(tmp, "DEVPATH=%s", devpath);
+    uevent_params[0] = strdup(tmp);
+
+    sprintf(filename, "/sys%s/uevent", devpath);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Unable to open '%s' (%m)", filename);
+        return -errno;
+    }
+
+    while (fgets(line, sizeof(line), fp)) {
+        line[strlen(line)-1] = 0;
+        if (!strncmp(line, "DEVTYPE=", 8))
+            uevent_params[1] = strdup(line);
+        else if (!strncmp(line, "MAJOR=",6)) 
+            uevent_params[2] = strdup(line);
+        else if (!strncmp(line, "MINOR=",6)) 
+            uevent_params[3] = strdup(line);
+    }
+    fclose(fp);
+
+    if (!uevent_params[1] || !uevent_params[2] || !uevent_params[3]) {
+        LOGE("mmcblk uevent missing required params");
+        return -1;
+    }
+    uevent_params[4] = '\0';
+    
+    if (simulate_uevent("block", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%m)");
+        return -errno;
+    }
+    return 0;
+}
diff --git a/vold/mmc.h b/vold/mmc.h
new file mode 100644
index 0000000..5a5d184
--- /dev/null
+++ b/vold/mmc.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MMC_H
+#define _MMC_H
+
+#define SYSFS_CLASS_MMC_PATH "/sys/class/mmc_host"
+
+#endif
diff --git a/vold/switch.c b/vold/switch.c
new file mode 100644
index 0000000..ba9ddb3
--- /dev/null
+++ b/vold/switch.c
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "vold.h"
+#include "switch.h"
+
+#define DEBUG_BOOTSTRAP 0
+
+static int mmc_bootstrap_switch(char *sysfs_path);
+
+int switch_bootstrap()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (!(d = opendir(SYSFS_CLASS_SWITCH_PATH))) {
+        LOG_ERROR("Unable to open '%s' (%m)", SYSFS_CLASS_SWITCH_PATH);
+        return -errno;
+    }
+
+    while ((de = readdir(d))) {
+        char tmp[255];
+
+        if (de->d_name[0] == '.')
+            continue;
+
+        sprintf(tmp, "%s/%s", SYSFS_CLASS_SWITCH_PATH, de->d_name);
+        if (mmc_bootstrap_switch(tmp))
+            LOG_ERROR("Error bootstrapping switch '%s' (%m)", tmp);
+    }
+
+    closedir(d);
+
+    return 0;
+}
+
+static int mmc_bootstrap_switch(char *sysfs_path)
+{
+#if DEBUG_BOOTSTRAP
+    LOG_VOL("bootstrap_switch(%s):", sysfs_path);
+#endif
+
+    char filename[255];
+    char name[255];
+    char state[255];
+    char tmp[255];
+    char *uevent_params[3];
+    char devpath[255];
+    FILE *fp;
+
+    /*
+     * Read switch name
+     */
+    sprintf(filename, "%s/name", sysfs_path);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Error opening switch name path '%s' (%s)",
+             sysfs_path, strerror(errno));
+       return -errno;
+    }
+    if (!fgets(name, sizeof(name), fp)) {
+        LOGE("Unable to read switch name");
+        fclose(fp);
+        return -EIO;
+    }
+    fclose(fp);
+
+    name[strlen(name) -1] = '\0';
+    sprintf(devpath, "/devices/virtual/switch/%s", name);
+    sprintf(tmp, "SWITCH_NAME=%s", name);
+    uevent_params[0] = (char *) strdup(tmp);
+
+    /*
+     * Read switch state
+     */
+    sprintf(filename, "%s/state", sysfs_path);
+    if (!(fp = fopen(filename, "r"))) {
+        LOGE("Error opening switch state path '%s' (%s)",
+             sysfs_path, strerror(errno));
+       return -errno;
+    }
+    if (!fgets(state, sizeof(state), fp)) {
+        LOGE("Unable to read switch state");
+        fclose(fp);
+        return -EIO;
+    }
+    fclose(fp);
+
+    state[strlen(state) -1] = '\0';
+    sprintf(tmp, "SWITCH_STATE=%s", state);
+    uevent_params[1] = (char *) strdup(tmp);
+
+    uevent_params[2] = (char *) NULL;
+
+    if (simulate_uevent("switch", devpath, "add", uevent_params) < 0) {
+        LOGE("Error simulating uevent (%s)", strerror(errno));
+        return -errno;
+    }
+
+    return 0;   
+}
diff --git a/vold/switch.h b/vold/switch.h
new file mode 100644
index 0000000..6729f2d
--- /dev/null
+++ b/vold/switch.h
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SWITCH_H
+#define _SWITCH_H
+
+#include "vold.h"
+
+#define SYSFS_CLASS_SWITCH_PATH "/sys/class/switch"
+
+#endif
diff --git a/vold/uevent.c b/vold/uevent.c
new file mode 100644
index 0000000..b1a6944
--- /dev/null
+++ b/vold/uevent.c
@@ -0,0 +1,443 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "vold.h"
+#include "uevent.h"
+#include "mmc.h"
+#include "blkdev.h"
+#include "volmgr.h"
+#include "media.h"
+
+#define DEBUG_UEVENT 0
+
+#define UEVENT_PARAMS_MAX 32
+
+enum uevent_action { action_add, action_remove, action_change };
+
+struct uevent {
+    char *path;
+    enum uevent_action action;
+    char *subsystem;
+    char *param[UEVENT_PARAMS_MAX];
+    unsigned int seqnum;
+};
+
+struct uevent_dispatch {
+    char *subsystem;
+    int (* dispatch) (struct uevent *);
+};
+
+static void dump_uevent(struct uevent *);
+static int dispatch_uevent(struct uevent *event);
+static void free_uevent(struct uevent *event);
+static char *get_uevent_param(struct uevent *event, char *param_name);
+
+static int handle_powersupply_event(struct uevent *event);
+static int handle_switch_event(struct uevent *);
+static int handle_battery_event(struct uevent *);
+static int handle_mmc_event(struct uevent *);
+static int handle_block_event(struct uevent *);
+static int handle_bdi_event(struct uevent *);
+static void _cb_blkdev_ok_to_destroy(blkdev_t *dev);
+
+static struct uevent_dispatch dispatch_table[] = {
+    { "switch", handle_switch_event }, 
+    { "battery", handle_battery_event }, 
+    { "mmc", handle_mmc_event },
+    { "block", handle_block_event },
+    { "bdi", handle_bdi_event },
+    { "power_supply", handle_powersupply_event },
+    { NULL, NULL }
+};
+
+static boolean low_batt = false;
+static boolean door_open = true;
+
+int process_uevent_message(int socket)
+{
+    char buffer[64 * 1024]; // Thank god we're not in the kernel :)
+    int count;
+    char *s = buffer;
+    char *end;
+    struct uevent *event;
+    int param_idx = 0;
+    int i;
+    int first = 1;
+    int rc = 0;
+
+    if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) {
+        LOGE("Error receiving uevent (%s)", strerror(errno));
+        return -errno;
+    }
+
+    if (!(event = malloc(sizeof(struct uevent)))) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        return -errno;
+    }
+
+    memset(event, 0, sizeof(struct uevent));
+
+    end = s + count;
+    while (s < end) {
+        if (first) {
+            char *p;
+            for (p = s; *p != '@'; p++);
+            p++;
+            event->path = strdup(p);
+            first = 0;
+        } else {
+            if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
+                char *a = s + strlen("ACTION=");
+               
+                if (!strcmp(a, "add"))
+                    event->action = action_add;
+                else if (!strcmp(a, "change"))
+                    event->action = action_change;
+                else if (!strcmp(a, "remove"))
+                    event->action = action_remove;
+            } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
+                event->seqnum = atoi(s + strlen("SEQNUM="));
+            else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
+                event->subsystem = strdup(s + strlen("SUBSYSTEM="));
+            else
+                event->param[param_idx++] = strdup(s);
+        }
+        s+= strlen(s) + 1;
+    }
+
+    rc = dispatch_uevent(event);
+    
+    free_uevent(event);
+    return rc;
+}
+
+int simulate_uevent(char *subsys, char *path, char *action, char **params)
+{
+    struct uevent *event;
+    char tmp[255];
+    int i, rc;
+
+    if (!(event = malloc(sizeof(struct uevent)))) {
+        LOGE("Error allocating memory (%s)", strerror(errno));
+        return -errno;
+    }
+
+    memset(event, 0, sizeof(struct uevent));
+
+    event->subsystem = strdup(subsys);
+
+    if (!strcmp(action, "add"))
+        event->action = action_add;
+    else if (!strcmp(action, "change"))
+        event->action = action_change;
+    else if (!strcmp(action, "remove"))
+        event->action = action_remove;
+    else {
+        LOGE("Invalid action '%s'", action);
+        return -1;
+    }
+
+    event->path = strdup(path);
+
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!params[i])
+            break;
+        event->param[i] = strdup(params[i]);
+    }
+
+    rc = dispatch_uevent(event);
+    free_uevent(event);
+    return rc;
+}
+
+static int dispatch_uevent(struct uevent *event)
+{
+    int i;
+
+#if DEBUG_UEVENT
+    dump_uevent(event);
+#endif
+    for (i = 0; dispatch_table[i].subsystem != NULL; i++) {
+        if (!strcmp(dispatch_table[i].subsystem, event->subsystem))
+            return dispatch_table[i].dispatch(event);
+    }
+
+#if DEBUG_UEVENT
+    LOG_VOL("No uevent handlers registered for '%s' subsystem", event->subsystem);
+#endif
+    return 0;
+}
+
+static void dump_uevent(struct uevent *event)
+{
+    int i;
+
+    LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s",
+              event->seqnum, event->subsystem, event->action, event->path);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        LOG_VOL("%s", event->param[i]);
+    }
+}
+
+static void free_uevent(struct uevent *event)
+{
+    int i;
+    free(event->path);
+    free(event->subsystem);
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        free(event->param[i]);
+    }
+    free(event);
+}
+
+static char *get_uevent_param(struct uevent *event, char *param_name)
+{
+    int i;
+
+    for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
+        if (!event->param[i])
+            break;
+        if (!strncmp(event->param[i], param_name, strlen(param_name)))
+            return &event->param[i][strlen(param_name) + 1];
+    }
+
+    LOGE("get_uevent_param(): No parameter '%s' found", param_name);
+    return NULL;
+}
+
+/*
+ * ---------------
+ * Uevent Handlers
+ * ---------------
+ */
+
+static int handle_powersupply_event(struct uevent *event)
+{
+    char *ps_type = get_uevent_param(event, "POWER_SUPPLY_TYPE");
+    char *ps_cap = get_uevent_param(event, "POWER_SUPPLY_CAPACITY");
+
+    if (!strcasecmp(ps_type, "battery")) {
+        int capacity = atoi(ps_cap);
+  
+        if (capacity < 5)
+            low_batt = true;
+        else
+            low_batt = false;
+LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
+        volmgr_safe_mode(low_batt || door_open);
+    }
+    return 0;
+}
+
+static int handle_switch_event(struct uevent *event)
+{
+    char *name = get_uevent_param(event, "SWITCH_NAME");
+    char *state = get_uevent_param(event, "SWITCH_STATE");
+
+
+    if (!strcmp(name, "usb_mass_storage")) {
+        if (!strcmp(state, "online")) {
+            ums_hostconnected_set(true);
+        } else {
+            ums_hostconnected_set(false);
+            volmgr_enable_ums(false);
+        }
+    } else if (!strcmp(name, "sd-door")) {
+        if (!strcmp(state, "open"))
+            door_open = true;
+        else
+            door_open = false;
+LOG_VOL("handle_powersupply_event(): low_batt = %d, door_open = %d", low_batt, door_open);
+        volmgr_safe_mode(low_batt || door_open);
+    } else
+        LOG_VOL("handle_switch_event(): Ignoring switch '%s'", name);
+
+    return 0;
+}
+
+static int handle_battery_event(struct uevent *event)
+{
+    return 0;
+}
+
+static int handle_block_event(struct uevent *event)
+{
+    char mediapath[255];
+    media_t *media;
+    int n;
+    int maj, min;
+    blkdev_t *blkdev;
+
+    /*
+     * Look for backing media for this block device
+     */
+    if (!strncmp(get_uevent_param(event, "DEVPATH"),
+                 "/devices/virtual/",
+                 strlen("/devices/virtual/"))) {
+        n = 0;
+    } else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
+        n = 2;
+    else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
+        n = 3;
+    else {
+        LOGE("Bad blockdev type '%s'", get_uevent_param(event, "DEVTYPE"));
+        return -EINVAL;
+    }
+
+    truncate_sysfs_path(event->path, n, mediapath);
+
+    if (!(media = media_lookup_by_path(mediapath, false))) {
+#if DEBUG_UEVENT
+        LOG_VOL("No backend media found @ device path '%s'", mediapath);
+#endif
+        return 0;
+    }
+
+    maj = atoi(get_uevent_param(event, "MAJOR"));
+    min = atoi(get_uevent_param(event, "MINOR"));
+
+    if (event->action == action_add) {
+        blkdev_t *disk;
+
+        /*
+         * If there isn't a disk already its because *we*
+         * are the disk
+         */
+        disk = blkdev_lookup_by_devno(maj, 0);
+
+        if (!(blkdev = blkdev_create(disk,
+                                     event->path,
+                                     maj,
+                                     min,
+                                     media,
+                                     get_uevent_param(event, "DEVTYPE")))) {
+            LOGE("Unable to allocate new blkdev (%m)");
+            return -1;
+        }
+
+        blkdev_refresh(blkdev);
+
+        /*
+         * Add the blkdev to media
+         */
+        int rc;
+        if ((rc = media_add_blkdev(media, blkdev)) < 0) {
+            LOGE("Unable to add blkdev to card (%d)", rc);
+            return rc;
+        }
+
+        LOG_VOL("New blkdev %d.%d on media %s, media path %s, Dpp %d",
+                blkdev->major, blkdev->minor, media->name, mediapath,
+                blkdev_get_num_pending_partitions(blkdev->disk));
+
+        if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
+            if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
+                LOGE("Volmgr failed to handle device (%d)", rc);
+                return rc;
+            }
+        }
+    } else if (event->action == action_remove) {
+        if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
+            return 0;
+
+        LOG_VOL("Destroying blkdev %d.%d @ %s on media %s", blkdev->major,
+                blkdev->minor, blkdev->devpath, media->name);
+        volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy);
+
+    } else if (event->action == action_change) {
+        if (!(blkdev = blkdev_lookup_by_devno(maj, min)))
+            return 0;
+
+        LOG_VOL("Modified blkdev %d.%d @ %s on media %s", blkdev->major,
+                blkdev->minor, blkdev->devpath, media->name);
+        
+        blkdev_refresh(blkdev);
+    } else  {
+#if DEBUG_UEVENT
+        LOG_VOL("No handler implemented for action %d", event->action);
+#endif
+    }
+    return 0;
+}
+
+static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
+{
+    media_t *media = media_lookup_by_dev(dev);
+    if (media)
+        media_remove_blkdev(media, dev);
+    blkdev_destroy(dev);
+}
+
+static int handle_bdi_event(struct uevent *event)
+{
+    return 0;
+}
+
+static int handle_mmc_event(struct uevent *event)
+{
+    if (event->action == action_add) {
+        media_t *media;
+        char serial[80];
+        char *type;
+
+        /*
+         * Pull card information from sysfs
+         */
+        type = get_uevent_param(event, "MMC_TYPE");
+        if (strcmp(type, "SD") && strcmp(type, "MMC"))
+            return 0;
+        
+        read_sysfs_var(serial, sizeof(serial), event->path, "serial");
+        if (!(media = media_create(event->path,
+                                   get_uevent_param(event, "MMC_NAME"),
+                                   serial,
+                                   media_mmc))) {
+            LOGE("Unable to allocate new media (%m)");
+            return -1;
+        }
+        LOG_VOL("New MMC card '%s' (serial %u) added @ %s", media->name,
+                  media->serial, media->devpath);
+    } else if (event->action == action_remove) {
+        media_t *media;
+
+        if (!(media = media_lookup_by_path(event->path, false))) {
+            LOGE("Unable to lookup media '%s'", event->path);
+            return -1;
+        }
+
+        LOG_VOL("MMC card '%s' (serial %u) @ %s removed", media->name, 
+                  media->serial, media->devpath);
+        media_destroy(media);
+    } else {
+#if DEBUG_UEVENT
+        LOG_VOL("No handler implemented for action %d", event->action);
+#endif
+    }
+
+    return 0;
+}
diff --git a/vold/uevent.h b/vold/uevent.h
new file mode 100644
index 0000000..0e9e671
--- /dev/null
+++ b/vold/uevent.h
@@ -0,0 +1,21 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UEVENT_MSG_H
+#define _UEVENT_MSG_H
+
+#endif
diff --git a/vold/ums.c b/vold/ums.c
new file mode 100644
index 0000000..4d0fc25
--- /dev/null
+++ b/vold/ums.c
@@ -0,0 +1,129 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include "vold.h"
+#include "ums.h"
+
+#define DEBUG_UMS 0
+
+static boolean host_connected = false;
+static boolean ums_enabled = false;
+
+int ums_bootstrap(void)
+{
+    return 0;
+}
+
+void ums_enabled_set(boolean enabled)
+{
+    ums_enabled = enabled;
+    send_msg(enabled ? VOLD_EVT_UMS_ENABLED : VOLD_EVT_UMS_DISABLED);
+}
+
+boolean ums_enabled_get()
+{
+    return ums_enabled;
+}
+
+void ums_hostconnected_set(boolean connected)
+{
+#if DEBUG_UMS
+    LOG_VOL("ums_hostconnected_set(%d):", connected);
+#endif
+    host_connected = connected;
+
+    if (!connected)
+        ums_enabled_set(false);
+    send_msg(connected ? VOLD_EVT_UMS_CONNECTED : VOLD_EVT_UMS_DISCONNECTED);
+}
+
+int ums_enable(char *dev_fspath, char *lun_syspath)
+{
+    LOG_VOL("ums_enable(%s, %s):", dev_fspath, lun_syspath);
+
+    int fd;
+    char filename[255];
+
+    sprintf(filename, "/sys/%s/file", lun_syspath);
+    if ((fd = open(filename, O_WRONLY)) < 0) {
+        LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    if (write(fd, dev_fspath, strlen(dev_fspath)) < 0) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    
+    close(fd);
+    return 0;
+}
+
+int ums_disable(char *lun_syspath)
+{
+#if DEBUG_UMS
+    LOG_VOL("ums_disable(%s):", lun_syspath);
+#endif
+
+    int fd;
+    char filename[255];
+
+    sprintf(filename, "/sys/%s/file", lun_syspath);
+    if ((fd = open(filename, O_WRONLY)) < 0) {
+        LOGE("Unable to open '%s' (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    char ch = 0;
+
+    if (write(fd, &ch, 1) < 0) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -errno;
+    }
+    
+    close(fd);
+    return 0;
+}
+
+boolean ums_hostconnected_get(void)
+{
+    return host_connected;
+}
+
+int ums_send_status(void)
+{
+    int rc;
+
+#if DEBUG_UMS
+    LOG_VOL("ums_send_status():");
+#endif
+
+    rc = send_msg(ums_enabled_get() ? VOLD_EVT_UMS_ENABLED :
+                                      VOLD_EVT_UMS_DISABLED);
+    if (rc < 0)
+        return rc;
+
+    rc = send_msg(ums_hostconnected_get() ? VOLD_EVT_UMS_CONNECTED :
+                                            VOLD_EVT_UMS_DISCONNECTED);
+
+    return rc;
+}
diff --git a/vold/ums.h b/vold/ums.h
new file mode 100644
index 0000000..02cdec3
--- /dev/null
+++ b/vold/ums.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UMS_H
+#define _UMS_H
+
+// these must match the corresponding strings in java/android/android/os/UsbListener.java
+#define VOLD_EVT_UMS_ENABLED              "ums_enabled"
+#define VOLD_EVT_UMS_DISABLED             "ums_disabled"
+#define VOLD_EVT_UMS_CONNECTED            "ums_connected"
+#define VOLD_EVT_UMS_DISCONNECTED         "ums_disconnected"
+
+
+int ums_send_status(void);
+int ums_enable(char *device_file, char *lun_syspath);
+int ums_disable(char *lun_syspath);
+#endif
diff --git a/vold/vold.c b/vold/vold.c
new file mode 100644
index 0000000..17331ac
--- /dev/null
+++ b/vold/vold.c
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include <linux/netlink.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "vold.h"
+#include "volmgr.h"
+
+
+#define VOLD_SOCKET "vold"
+
+/*
+ * Globals
+ */
+
+static int ver_major = 2;
+static int ver_minor = 0;
+static pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int fw_sock = -1;
+
+int main(int argc, char **argv)
+{
+    int door_sock = -1;
+    int uevent_sock = -1;
+    struct sockaddr_nl nladdr;
+    int uevent_sz = 64 * 1024;
+
+    LOG_VOL("Android Volume Daemon version %d.%d", ver_major, ver_minor);
+
+    /*
+     * Create all the various sockets we'll need
+     */
+
+    // Socket to listen on for incomming framework connections
+    if ((door_sock = android_get_control_socket(VOLD_SOCKET)) < 0) {
+        LOGE("Obtaining file descriptor socket '%s' failed: %s",
+             VOLD_SOCKET, strerror(errno));
+        exit(1);
+    }
+
+    if (listen(door_sock, 4) < 0) {
+        LOGE("Unable to listen on fd '%d' for socket '%s': %s", 
+             door_sock, VOLD_SOCKET, strerror(errno));
+        exit(1);
+    }
+
+    mkdir("/dev/block/vold", 0755);
+
+    // Socket to listen on for uevent changes
+    memset(&nladdr, 0, sizeof(nladdr));
+    nladdr.nl_family = AF_NETLINK;
+    nladdr.nl_pid = getpid();
+    nladdr.nl_groups = 0xffffffff;
+
+    if ((uevent_sock = socket(PF_NETLINK,
+                             SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
+        LOGE("Unable to create uevent socket: %s", strerror(errno));
+        exit(1);
+    }
+
+    if (setsockopt(uevent_sock, SOL_SOCKET, SO_RCVBUFFORCE, &uevent_sz,
+                   sizeof(uevent_sz)) < 0) {
+        LOGE("Unable to set uevent socket options: %s", strerror(errno));
+        exit(1);
+    }
+
+    if (bind(uevent_sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
+        LOGE("Unable to bind uevent socket: %s", strerror(errno));
+        exit(1);
+    }
+
+    /*
+     * Bootstrap 
+     */
+
+    // Volume Manager
+    volmgr_bootstrap();
+
+    // SD Card system
+    mmc_bootstrap();
+
+    // USB Mass Storage
+    ums_bootstrap();
+
+    // Switch
+    switch_bootstrap();
+
+    /*
+     * Main loop
+     */
+    LOG_VOL("Bootstrapping complete");
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int max = 0;
+        int rc = 0;
+
+        to.tv_sec = (60 * 60);
+        to.tv_usec = 0;
+
+        FD_ZERO(&read_fds);
+        FD_SET(door_sock, &read_fds);
+        if (door_sock > max)
+            max = door_sock;
+        FD_SET(uevent_sock, &read_fds);
+        if (uevent_sock > max)
+            max = uevent_sock;
+
+        if (fw_sock != -1) {
+            FD_SET(fw_sock, &read_fds);
+            if (fw_sock > max)
+                max = fw_sock;
+        }
+
+        if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
+            LOGE("select() failed (%s)", strerror(errno));
+            sleep(1);
+            continue;
+        }
+
+        if (!rc) {
+            continue;
+        }
+
+        if (FD_ISSET(door_sock, &read_fds)) {
+            struct sockaddr addr;
+            socklen_t alen;
+
+            alen = sizeof(addr);
+
+            if (fw_sock != -1) {
+                LOGE("Dropping duplicate framework connection");
+                int tmp = accept(door_sock, &addr, &alen);
+                close(tmp);
+                continue;
+            }
+
+            if ((fw_sock = accept(door_sock, &addr, &alen)) < 0) {
+                LOGE("Unable to accept framework connection (%s)",
+                     strerror(errno));
+            }
+            LOG_VOL("Accepted connection from framework");
+            if ((rc = volmgr_send_states()) < 0) {
+                LOGE("Unable to send volmgr status to framework (%d)", rc);
+            }
+        }
+
+        if (FD_ISSET(fw_sock, &read_fds)) {
+            if ((rc = process_framework_command(fw_sock)) < 0) {
+                if (rc == -ECONNRESET) {
+                    LOGE("Framework disconnected");
+                    close(fw_sock);
+                    fw_sock = -1;
+                } else {
+                    LOGE("Error processing framework command (%s)",
+                         strerror(errno));
+                }
+            }
+        }
+
+        if (FD_ISSET(uevent_sock, &read_fds)) {
+            if ((rc = process_uevent_message(uevent_sock)) < 0) {
+                LOGE("Error processing uevent msg (%s)", strerror(errno));
+            }
+        }
+    } // while
+
+}
+
+int send_msg(char* message)
+{
+    int result = -1;
+
+    pthread_mutex_lock(&write_mutex);
+
+    LOG_VOL("send_msg(%s):", message);
+
+    if (fw_sock >= 0)
+        result = write(fw_sock, message, strlen(message) + 1);
+
+    pthread_mutex_unlock(&write_mutex);
+
+    return result;
+}
+
+int send_msg_with_data(char *message, char *data)
+{
+    int result = -1;
+
+    char* buffer = (char *)alloca(strlen(message) + strlen(data) + 1);
+    if (!buffer) {
+        LOGE("alloca failed in send_msg_with_data");
+        return -1;
+    }
+
+    strcpy(buffer, message);
+    strcat(buffer, data);
+    return send_msg(buffer);
+}
diff --git a/vold/vold.h b/vold/vold.h
new file mode 100644
index 0000000..0876bec
--- /dev/null
+++ b/vold/vold.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOLD_H__
+#define VOLD_H__
+
+#define LOG_TAG "vold"
+#include "cutils/log.h"
+
+typedef int boolean;
+enum {
+    false = 0,
+    true = 1
+};
+
+#define DEVPATH "/dev/block/"
+#define DEVPATHLENGTH 11
+
+#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
+
+// Set this for logging error messages
+#define ENABLE_LOG_ERROR
+
+// set this to log vold events
+#define ENABLE_LOG_VOL
+
+#ifdef ENABLE_LOG_ERROR
+#define LOG_ERROR(fmt, args...) \
+    { LOGE(fmt , ## args); }
+#else
+#define LOG_ERROR(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ERROR */
+
+#ifdef ENABLE_LOG_VOL
+#define LOG_VOL(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_VOL(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_VOL */
+
+#ifdef ENABLE_LOG_SERVER
+#define LOG_SERVER(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_SERVER(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_SERVER */
+
+#ifdef ENABLE_LOG_ASEC
+#define LOG_ASEC(fmt, args...) \
+    { LOGD(fmt , ## args); }
+#else
+#define LOG_ASEC(fmt, args...) \
+    do { } while (0)
+#endif /* ENABLE_LOG_ASEC */
+
+/*
+ * Prototypes
+ */
+
+int process_framework_command(int socket);
+
+int process_inotify_event(int fd);
+int inotify_bootstrap(void);
+
+int process_uevent_message(int socket);
+int simulate_uevent(char *subsystem, char *path, char *action, char **params);
+
+int mmc_bootstrap(void);
+int ums_bootstrap(void);
+
+int volmgr_bootstrap(void);
+
+int switch_bootstrap(void);
+
+void *read_file(char *filename, ssize_t *_size);
+char *truncate_sysfs_path(char *path, int num_elements_to_remove, char *buffer);
+char *read_sysfs_var(char *buffer, size_t maxlen, char *devpath, char *var);
+
+void ums_hostconnected_set(boolean connected);
+boolean ums_hostconnected_get(void);
+
+int send_msg(char *msg);
+int send_msg_with_data(char *msg, char *data);
+#endif
diff --git a/vold/volmgr.c b/vold/volmgr.c
new file mode 100644
index 0000000..c3ce8d1
--- /dev/null
+++ b/vold/volmgr.c
@@ -0,0 +1,1229 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include <sys/mount.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/properties.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "blkdev.h"
+#include "ums.h"
+#include "format.h"
+#include "devmapper.h"
+
+#include "volmgr_ext3.h"
+#include "volmgr_vfat.h"
+
+#define DEBUG_VOLMGR 0
+
+static volume_t *vol_root = NULL;
+static boolean safe_mode = true;
+
+static struct volmgr_fstable_entry fs_table[] = {
+    { "ext3", ext_identify, ext_check, ext_mount , true },
+    { "vfat", vfat_identify, vfat_check, vfat_mount , false },
+    { NULL, NULL, NULL, NULL , false}
+};
+
+struct _volume_state_event_map {
+    volume_state_t state;
+    char           *event;
+    char           *property_val;
+};
+
+static struct _volume_state_event_map volume_state_strings[] = {
+    { volstate_unknown,     "volstate_unknown:",  "unknown" },
+    { volstate_nomedia,     VOLD_EVT_NOMEDIA,     VOLD_ES_PVAL_NOMEDIA },
+    { volstate_unmounted,   VOLD_EVT_UNMOUNTED,   VOLD_ES_PVAL_UNMOUNTED },
+    { volstate_checking,    VOLD_EVT_CHECKING,    VOLD_ES_PVAL_CHECKING },
+    { volstate_mounted,     VOLD_EVT_MOUNTED,     VOLD_ES_PVAL_MOUNTED },
+    { volstate_mounted_ro,  VOLD_EVT_MOUNTED_RO,  VOLD_ES_PVAL_MOUNTED_RO },
+    { volstate_badremoval,  VOLD_EVT_BADREMOVAL,  VOLD_ES_PVAL_BADREMOVAL },
+    { volstate_damaged,     VOLD_EVT_DAMAGED,     VOLD_ES_PVAL_DAMAGED },
+    { volstate_nofs,        VOLD_EVT_NOFS,        VOLD_ES_PVAL_NOFS },
+    { volstate_ums,         VOLD_EVT_UMS,         VOLD_ES_PVAL_UMS },
+    { 0, NULL, NULL }
+};
+
+
+static int volmgr_readconfig(char *cfg_path);
+static int volmgr_config_volume(cnode *node);
+static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy);
+static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev);
+static int _volmgr_start(volume_t *vol, blkdev_t *dev);
+static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev);
+static void *volmgr_start_fs_thread(void *arg);
+static void volmgr_start_fs_thread_sighandler(int signo);
+static void volume_setstate(volume_t *vol, volume_state_t state);
+static char *conv_volstate_to_eventstr(volume_state_t state);
+static char *conv_volstate_to_propstr(volume_state_t state);
+static int volume_send_state(volume_t *vol);
+static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg);
+static int _volmgr_enable_ums(volume_t *);
+static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg), boolean emit_statechange);
+static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange);
+static void _cb_volume_stopped_for_eject(volume_t *v, void *arg);
+static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg);
+static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev);
+static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg);
+static void volmgr_reaper_thread_sighandler(int signo);
+static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path);
+static int volmgr_send_eject_request(volume_t *v);
+static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked);
+
+static boolean _mountpoint_mounted(char *mp)
+{
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    FILE *fp;
+    char line[1024];
+
+    if (!(fp = fopen("/proc/mounts", "r"))) {
+        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
+        return false;
+    }
+
+    while(fgets(line, sizeof(line), fp)) {
+        line[strlen(line)-1] = '\0';
+        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
+        if (!strcmp(mount_path, mp)) {
+            fclose(fp);
+            return true;
+        }
+        
+    }
+
+    fclose(fp);
+    return false;
+}
+
+/*
+ * Public functions
+ */
+
+int volmgr_set_volume_key(char *mount_point, unsigned char *key)
+{
+    volume_t *v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+ 
+    if (!v)
+        return -ENOENT;
+
+    if (v->media_type != media_devmapper) {
+        LOGE("Cannot set key on a non devmapper volume");
+        pthread_mutex_unlock(&v->lock);
+        return -EINVAL;
+    }
+
+    memcpy(v->dm->key, key, sizeof(v->dm->key));
+    pthread_mutex_unlock(&v->lock);
+    return 0;
+}
+
+int volmgr_format_volume(char *mount_point)
+{
+    int rc;
+    volume_t *v;
+
+    LOG_VOL("volmgr_format_volume(%s):", mount_point);
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+
+    if (!v)
+        return -ENOENT;
+
+    if (v->state == volstate_mounted ||
+        v->state == volstate_mounted_ro ||
+        v->state == volstate_ums ||
+        v->state == volstate_checking) {
+            LOGE("Can't format '%s', currently in state %d", mount_point, v->state);
+            pthread_mutex_unlock(&v->lock);
+            return -EBUSY;
+        } else if (v->state == volstate_nomedia &&
+                   v->media_type != media_devmapper) {
+            LOGE("Can't format '%s', (no media)", mount_point);
+            pthread_mutex_unlock(&v->lock);
+            return -ENOMEDIUM;
+        }
+
+    // XXX:Reject if the underlying source media is not present
+
+    if (v->media_type == media_devmapper) {
+        if ((rc = devmapper_genesis(v->dm)) < 0) {
+            LOGE("devmapper genesis failed for %s (%d)", mount_point, rc);
+            pthread_mutex_unlock(&v->lock);
+            return rc;
+        }
+    } else {
+        if ((rc = initialize_mbr(v->dev->disk)) < 0) {
+            LOGE("MBR init failed for %s (%d)", mount_point, rc);
+            pthread_mutex_unlock(&v->lock);
+            return rc;
+        }
+    }
+
+    volume_setstate(v, volstate_formatting);
+    pthread_mutex_unlock(&v->lock);
+    return rc;
+}
+
+int volmgr_bootstrap(void)
+{
+    int rc;
+
+    if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {
+        LOGE("Unable to process config");
+        return rc;
+    }
+
+    /*
+     * Check to see if any of our volumes is mounted
+     */
+    volume_t *v = vol_root;
+    while (v) {
+        if (_mountpoint_mounted(v->mount_point)) {
+            LOG_VOL("Volume '%s' already mounted at startup", v->mount_point);
+            v->state = volstate_mounted;
+        }
+        v = v->next;
+    }
+
+    return 0;
+}
+
+int volmgr_safe_mode(boolean enable)
+{
+    if (enable == safe_mode)
+        return 0;
+
+    safe_mode = enable;
+
+    volume_t *v = vol_root;
+    int rc;
+
+    while (v) {
+        pthread_mutex_lock(&v->lock);
+        if (v->state == volstate_mounted && v->fs) {
+            rc = v->fs->mount_fn(v->dev, v, safe_mode);
+            if (!rc) {
+                LOG_VOL("Safe mode %s on %s", (enable ? "enabled" : "disabled"), v->mount_point);
+            } else {
+                LOGE("Failed to %s safe-mode on %s (%s)",
+                     (enable ? "enable" : "disable" ), v->mount_point, strerror(-rc));
+            }
+        }
+
+        pthread_mutex_unlock(&v->lock);
+        v = v->next;
+    }
+        
+    return 0;
+}
+
+int volmgr_send_states(void)
+{
+    volume_t *vol_scan = vol_root;
+    int rc;
+
+    while (vol_scan) {
+        pthread_mutex_lock(&vol_scan->lock);
+        if ((rc = volume_send_state(vol_scan)) < 0) {
+            LOGE("Error sending state to framework (%d)", rc);
+        }
+        pthread_mutex_unlock(&vol_scan->lock);
+        vol_scan = vol_scan->next;
+        break; // XXX:
+    }
+
+    return 0;
+}
+
+/*
+ * Called when a block device is ready to be
+ * evaluated by the volume manager.
+ */
+int volmgr_consider_disk(blkdev_t *dev)
+{
+    volume_t *vol;
+
+    if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true)))
+        return 0;
+
+    pthread_mutex_lock(&vol->lock);
+
+    if (vol->state == volstate_mounted) {
+        LOGE("Volume %s already mounted (did we just crash?)", vol->mount_point);
+        pthread_mutex_unlock(&vol->lock);
+        return 0;
+    }
+
+    int rc =  _volmgr_consider_disk_and_vol(vol, dev);
+    pthread_mutex_unlock(&vol->lock);
+    return rc;
+}
+
+int volmgr_start_volume_by_mountpoint(char *mount_point)
+{ 
+    volume_t *v;
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+    if (!v)
+        return -ENOENT;
+
+    if (v->media_type == media_devmapper) {
+        if (devmapper_start(v->dm) < 0)  {
+            LOGE("volmgr failed to start devmapper volume '%s'",
+                 v->mount_point);
+        }
+    } else if (v->media_type == media_mmc) {
+        if (!v->dev) {
+            LOGE("Cannot start volume '%s' (volume is not bound)", mount_point);
+            pthread_mutex_unlock(&v->lock);
+            return -ENOENT;
+        }
+
+        if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) {
+            LOGE("volmgr failed to start volume '%s'", v->mount_point);
+        }
+    }
+
+    pthread_mutex_unlock(&v->lock);
+    return 0;
+}
+
+static void _cb_volstopped_for_devmapper_teardown(volume_t *v, void *arg)
+{
+    devmapper_stop(v->dm);
+    volume_setstate(v, volstate_nomedia);
+    pthread_mutex_unlock(&v->lock);
+}
+
+int volmgr_stop_volume_by_mountpoint(char *mount_point)
+{
+    int rc;
+    volume_t *v;
+
+    v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
+    if (!v)
+        return -ENOENT;
+
+    if (v->state == volstate_mounted)
+        volmgr_send_eject_request(v);
+
+    if (v->media_type == media_devmapper)
+        rc = volmgr_shutdown_volume(v, _cb_volstopped_for_devmapper_teardown, false);
+    else
+        rc = volmgr_shutdown_volume(v, NULL, true);
+
+    /*
+     * If shutdown returns -EINPROGRESS,
+     * do *not* release the lock as
+     * it is now owned by the reaper thread
+     */
+    if (rc != -EINPROGRESS) {
+        if (rc)
+            LOGE("unable to shutdown volume '%s'", v->mount_point);
+        pthread_mutex_unlock(&v->lock);
+    }
+    return 0;
+}
+
+int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *))
+{
+    LOG_VOL("Volmgr notified of %d:%d eject", dev->major, dev->minor);
+
+    volume_t *v;
+    int rc;
+
+    // XXX: Partitioning support is going to need us to stop *all*
+    // devices in this volume
+    if (!(v = volmgr_lookup_volume_by_dev(dev))) {
+        if (cb)
+            cb(dev);
+        return 0;
+    }
+    
+    pthread_mutex_lock(&v->lock);
+
+    volume_state_t old_state = v->state;
+
+    if (v->state == volstate_mounted ||
+        v->state == volstate_ums ||
+        v->state == volstate_checking) {
+
+        volume_setstate(v, volstate_badremoval);
+
+        /*
+         * Stop any devmapper volumes which
+         * are using us as a source
+         * XXX: We may need to enforce stricter
+         * order here
+         */
+        volume_t *dmvol = vol_root;
+        while (dmvol) {
+            if ((dmvol->media_type == media_devmapper) &&
+                (dmvol->dm->src_type == dmsrc_loopback) &&
+                (!strncmp(dmvol->dm->type_data.loop.loop_src, 
+                          v->mount_point, strlen(v->mount_point)))) {
+
+                pthread_mutex_lock(&dmvol->lock);
+                if (dmvol->state != volstate_nomedia) {
+                    rc = volmgr_shutdown_volume(dmvol, _cb_volstopped_for_devmapper_teardown, false);
+                    if (rc != -EINPROGRESS) {
+                        if (rc)
+                            LOGE("unable to shutdown volume '%s'", v->mount_point);
+                        pthread_mutex_unlock(&dmvol->lock);
+                    }
+                } else 
+                    pthread_mutex_unlock(&dmvol->lock);
+            }
+            dmvol = dmvol->next;
+        }
+
+    } else if (v->state == volstate_formatting) {
+        /*
+         * The device is being ejected due to
+         * kernel disk revalidation.
+         */
+        LOG_VOL("Volmgr ignoring eject of %d:%d (volume formatting)",
+                dev->major, dev->minor);
+        if (cb)
+            cb(dev);
+        pthread_mutex_unlock(&v->lock);
+        return 0;
+    } else
+        volume_setstate(v, volstate_nomedia);
+    
+    if (old_state == volstate_ums) {
+        ums_disable(v->ums_path);
+        pthread_mutex_unlock(&v->lock);
+    } else {
+        int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false);
+        if (rc != -EINPROGRESS) {
+            if (rc)
+                LOGE("unable to shutdown volume '%s'", v->mount_point);
+            pthread_mutex_unlock(&v->lock);
+        }
+    }
+    return 0; 
+}
+
+static void _cb_volume_stopped_for_eject(volume_t *v, void *arg)
+{
+    void (* eject_cb) (blkdev_t *) = arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s has been stopped for eject", v->mount_point);
+#endif
+
+    if (eject_cb)
+        eject_cb(v->dev);
+    v->dev = NULL; // Clear dev because its being ejected
+}
+
+/*
+ * Instructs the volume manager to enable or disable USB mass storage
+ * on any volumes configured to use it.
+ */
+int volmgr_enable_ums(boolean enable)
+{
+    volume_t *v = vol_root;
+
+    while(v) {
+        if (v->ums_path) {
+            int rc;
+
+            if (enable) {
+                pthread_mutex_lock(&v->lock);
+                if (v->state == volstate_mounted)
+                    volmgr_send_eject_request(v);
+                else if (v->state == volstate_ums) {
+                    pthread_mutex_unlock(&v->lock);
+                    goto next_vol;
+                }
+
+                // Stop the volume, and enable UMS in the callback
+                rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false);
+                if (rc != -EINPROGRESS) {
+                    if (rc)
+                        LOGE("unable to shutdown volume '%s'", v->mount_point);
+                    pthread_mutex_unlock(&v->lock);
+                }
+            } else {
+                // Disable UMS
+                pthread_mutex_lock(&v->lock);
+                if (v->state != volstate_ums) {
+                    pthread_mutex_unlock(&v->lock);
+                    goto next_vol;
+                }
+
+                if ((rc = ums_disable(v->ums_path)) < 0) {
+                    LOGE("unable to disable ums on '%s'", v->mount_point);
+                    pthread_mutex_unlock(&v->lock);
+                    continue;
+                }
+
+                LOG_VOL("Kick-starting volume %d:%d after UMS disable",
+                        v->dev->disk->major, v->dev->disk->minor);
+                // Start volume
+                if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) {
+                    LOGE("volmgr failed to consider disk %d:%d",
+                         v->dev->disk->major, v->dev->disk->minor);
+                }
+                pthread_mutex_unlock(&v->lock);
+            }
+        }
+ next_vol:
+        v = v->next;
+    }
+    return 0;
+}
+
+/*
+ * Static functions
+ */
+
+static int volmgr_send_eject_request(volume_t *v)
+{
+    return send_msg_with_data(VOLD_EVT_EJECTING, v->mount_point);
+}
+
+// vol->lock must be held!
+static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev)
+{
+    int rc = 0;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_consider_disk_and_vol(%s, %d:%d):", vol->mount_point,
+            dev->major, dev->minor); 
+#endif
+
+    if (vol->state == volstate_unknown ||
+        vol->state == volstate_mounted ||
+        vol->state == volstate_mounted_ro ||
+        vol->state == volstate_damaged) {
+        LOGE("Cannot consider volume '%s' because it is in state '%d", 
+             vol->mount_point, vol->state);
+        return -EADDRINUSE;
+    }
+
+    if (vol->state == volstate_formatting) {
+        LOG_VOL("Evaluating dev '%s' for formattable filesystems for '%s'",
+                dev->devpath, vol->mount_point);
+        /*
+         * Since we only support creating 1 partition (right now),
+         * we can just lookup the target by devno
+         */
+        blkdev_t *part = blkdev_lookup_by_devno(dev->major, 1);
+        if (!part) {
+            part = blkdev_lookup_by_devno(dev->major, 0);
+            if (!part) {
+                LOGE("Unable to find device to format");
+                return -ENODEV;
+            }
+        }
+
+        if ((rc = format_partition(part,
+                                   vol->media_type == media_devmapper ?
+                                   FORMAT_TYPE_EXT2 : FORMAT_TYPE_FAT32)) < 0) {
+            LOGE("format failed (%d)", rc);
+            return rc;
+        }
+        
+    }
+
+    LOG_VOL("Evaluating dev '%s' for mountable filesystems for '%s'",
+            dev->devpath, vol->mount_point);
+
+    if (dev->nr_parts == 0) {
+        rc = _volmgr_start(vol, dev);
+#if DEBUG_VOLMGR
+        LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", vol->mount_point,
+                dev->major, dev->minor, rc);
+#endif
+    } else {
+        /*
+         * Device has multiple partitions
+         * This is where interesting partition policies could be implemented.
+         * For now just try them in sequence until one succeeds
+         */
+   
+        rc = -ENODEV;
+        int i;
+        for (i = 0; i < dev->nr_parts; i++) {
+            blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1));
+            if (!part) {
+                LOGE("Error - unable to lookup partition for blkdev %d:%d", dev->major, (i+1));
+                continue;
+            }
+            rc = _volmgr_start(vol, part);
+#if DEBUG_VOLMGR
+            LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d",
+                    vol->mount_point, part->major, part->minor, rc);
+#endif
+            if (!rc) 
+                break;
+        }
+
+        if (rc == -ENODEV) {
+            // Assert to make sure each partition had a backing blkdev
+            LOGE("Internal consistency error");
+            return 0;
+        }
+    }
+
+    if (rc == -ENODATA) {
+        LOGE("Device %d:%d contains no usable filesystems",
+             dev->major, dev->minor);
+        rc = 0;
+    }
+
+    return rc;
+}
+
+static void volmgr_reaper_thread_sighandler(int signo)
+{
+    LOGE("Volume reaper thread got signal %d", signo);
+}
+
+static void __reaper_cleanup(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    if (vol->worker_args.reaper_args.cb)
+        vol->worker_args.reaper_args.cb(vol, vol->worker_args.reaper_args.cb_arg);
+
+    vol->worker_running = false;
+
+    // Wake up anyone that was waiting on this thread
+    pthread_mutex_unlock(&vol->worker_sem);
+
+    // Unlock the volume
+    pthread_mutex_unlock(&vol->lock);
+}
+
+static void *volmgr_reaper_thread(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    pthread_cleanup_push(__reaper_cleanup, arg);
+
+    vol->worker_running = true;
+    vol->worker_pid = getpid();
+
+    struct sigaction actions;
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = volmgr_reaper_thread_sighandler;
+    sigaction(SIGUSR1, &actions, NULL);
+
+    LOG_VOL("Reaper here - working on %s", vol->mount_point);
+
+    boolean send_sig_kill = false;
+    int i, rc;
+
+    for (i = 0; i < 10; i++) {
+        errno = 0;
+        rc = umount(vol->mount_point);
+        LOG_VOL("volmngr reaper umount(%s) attempt %d (%s)",
+                vol->mount_point, i + 1, strerror(errno));
+        if (!rc)
+            break;
+        if (rc && (errno == EINVAL || errno == ENOENT)) {
+            rc = 0;
+            break;
+        }
+        sleep(1);
+        if (i >= 4) {
+            KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0);
+            if (!send_sig_kill)
+                send_sig_kill = true;
+        }
+    }
+
+    if (!rc) {
+        LOG_VOL("Reaper sucessfully unmounted %s", vol->mount_point);
+        vol->fs = NULL;
+        volume_setstate(vol, volstate_unmounted);
+    } else {
+        LOGE("Unable to unmount!! (%d)", rc);
+    }
+
+ out:
+    pthread_cleanup_pop(1);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+// vol->lock must be held!
+static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg)
+{
+
+    if (vol->worker_running) {
+        LOGE("Worker thread is currently running.. waiting..");
+        pthread_mutex_lock(&vol->worker_sem);
+        LOG_VOL("Worker thread now available");
+    }
+
+    vol->worker_args.reaper_args.cb = cb;
+    vol->worker_args.reaper_args.cb_arg = arg;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol);
+}
+
+static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange)
+{
+    int i, rc;
+
+    if (v->state == volstate_mounted || v->state == volstate_badremoval) {
+        // Try to unmount right away (5 retries)
+        for (i = 0; i < 5; i++) {
+            rc = umount(v->mount_point);
+            if (!rc)
+                break;
+
+            if (rc && (errno == EINVAL || errno == ENOENT)) {
+                rc = 0;
+                break;
+            }
+
+            LOG_VOL("volmngr quick stop umount(%s) attempt %d (%s)",
+                    v->mount_point, i + 1, strerror(errno));
+
+            if (i == 0)
+                usleep(1000 * 250); // First failure, sleep for 250 ms 
+            else
+                sched_yield();
+        }
+
+        if (!rc) {
+            LOG_VOL("volmgr_stop_volume(%s): Volume unmounted sucessfully",
+                    v->mount_point);
+            if (emit_statechange)
+                volume_setstate(v, volstate_unmounted);
+            v->fs = NULL;
+            goto out_cb_immed;
+        }
+
+        /*
+         * Since the volume is still in use, dispatch the stopping to
+         * a thread
+         */
+        LOG_VOL("Volume %s is busy (%d) - uncaging the reaper", v->mount_point, rc);
+        volmgr_uncage_reaper(v, cb, arg);
+        return -EINPROGRESS;
+    } else if (v->state == volstate_checking) {
+        volume_setstate(v, volstate_unmounted);
+        if (v->worker_running) {
+            LOG_VOL("Cancelling worker thread");
+            pthread_kill(v->worker_thread, SIGUSR1);
+        } else
+            LOGE("Strange... we were in checking state but worker thread wasn't running..");
+        goto out_cb_immed;
+    }
+
+ out_cb_immed:
+    if (cb)
+        cb(v, arg);
+    return 0;
+}
+
+
+/*
+ * Gracefully stop a volume
+ * v->lock must be held!
+ * if we return -EINPROGRESS, do NOT release the lock as the reaper
+ * is using the volume
+ */
+static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *), boolean emit_statechange)
+{
+    return volmgr_stop_volume(v, cb, NULL, emit_statechange);
+}
+
+static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg)
+{
+    void (* shutdown_cb) (volume_t *) = arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s has been stopped for shutdown", v->mount_point);
+#endif
+    shutdown_cb(v);
+}
+
+
+/*
+ * Called when a volume is sucessfully unmounted for UMS enable
+ */
+static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
+{
+    int rc;
+    char *devdir_path;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point);
+#endif
+    devdir_path = blkdev_get_devpath(v->dev->disk);
+
+    if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) {
+        free(devdir_path);
+        LOGE("Error enabling ums (%d)", rc);
+        return;
+    }
+    free(devdir_path);
+    volume_setstate(v, volstate_ums);
+    pthread_mutex_unlock(&v->lock);
+}
+
+static int volmgr_readconfig(char *cfg_path)
+{
+    cnode *root = config_node("", "");
+    cnode *node;
+
+    config_load_file(root, cfg_path);
+    node = root->first_child;
+
+    while (node) {
+        if (!strncmp(node->name, "volume_", 7))
+            volmgr_config_volume(node);
+        else
+            LOGE("Skipping unknown configuration node '%s'", node->name);
+        node = node->next;
+    }
+    return 0;
+}
+
+static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path)
+{
+    int i;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_add_mediapath_to_volume(%p, %s):", v, media_path);
+#endif
+    for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+        if (!v->media_paths[i]) {
+            v->media_paths[i] = strdup(media_path);
+            return;
+        }
+    }
+    LOGE("Unable to add media path '%s' to volume (out of media slots)", media_path);
+}
+
+static int volmgr_config_volume(cnode *node)
+{
+    volume_t *new;
+    int rc = 0, i;
+
+    char *dm_src, *dm_src_type, *dm_tgt, *dm_param, *dm_tgtfs;
+    uint32_t dm_size_mb = 0;
+
+    dm_src = dm_src_type = dm_tgt = dm_param = dm_tgtfs = NULL;
+#if DEBUG_VOLMGR
+    LOG_VOL("volmgr_configure_volume(%s):", node->name);
+#endif
+    if (!(new = malloc(sizeof(volume_t))))
+        return -ENOMEM;
+    memset(new, 0, sizeof(volume_t));
+
+    new->state = volstate_nomedia;
+    pthread_mutex_init(&new->lock, NULL);
+    pthread_mutex_init(&new->worker_sem, NULL);
+
+    cnode *child = node->first_child;
+
+    while (child) {
+        if (!strcmp(child->name, "media_path"))
+            volmgr_add_mediapath_to_volume(new, child->value);
+        else if (!strcmp(child->name, "emu_media_path"))
+            volmgr_add_mediapath_to_volume(new, child->value);
+        else if (!strcmp(child->name, "media_type")) {
+            if (!strcmp(child->value, "mmc"))
+                new->media_type = media_mmc;
+            else if (!strcmp(child->value, "devmapper"))
+                new->media_type = media_devmapper;
+            else {
+                LOGE("Invalid media type '%s'", child->value);
+                rc = -EINVAL;
+                goto out_free;
+            }
+        } else if (!strcmp(child->name, "mount_point"))
+            new->mount_point = strdup(child->value);
+        else if (!strcmp(child->name, "ums_path"))
+            new->ums_path = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src")) 
+            dm_src = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src_type")) 
+            dm_src_type = strdup(child->value);
+        else if (!strcmp(child->name, "dm_src_size_mb")) 
+            dm_size_mb = atoi(child->value);
+        else if (!strcmp(child->name, "dm_target")) 
+            dm_tgt = strdup(child->value);
+        else if (!strcmp(child->name, "dm_target_params")) 
+            dm_param = strdup(child->value);
+        else if (!strcmp(child->name, "dm_target_fs")) 
+            dm_tgtfs = strdup(child->value);
+        else
+            LOGE("Ignoring unknown config entry '%s'", child->name);
+        child = child->next;
+    }
+
+    if (new->media_type == media_mmc) {
+        if (!new->media_paths[0] || !new->mount_point || new->media_type == media_unknown) {
+            LOGE("Required configuration parameter missing for mmc volume");
+            rc = -EINVAL;
+            goto out_free;
+        }
+    } else if (new->media_type == media_devmapper) {
+        if (!dm_src || !dm_src_type || !dm_tgt ||
+            !dm_param || !dm_tgtfs || !dm_size_mb) {
+            LOGE("Required configuration parameter missing for devmapper volume");
+            rc = -EINVAL;
+            goto out_free;
+        }
+
+        char dm_mediapath[255];
+        if (!(new->dm = devmapper_init(dm_src, dm_src_type, dm_size_mb,
+                                       dm_tgt, dm_param, dm_tgtfs, dm_mediapath))) {
+            LOGE("Unable to initialize devmapping");
+            goto out_free;
+        }
+        LOG_VOL("media path for devmapper volume = '%s'", dm_mediapath);
+        volmgr_add_mediapath_to_volume(new, dm_mediapath);
+    }
+
+    if (!vol_root)
+        vol_root = new;
+    else {
+        volume_t *scan = vol_root;
+        while (scan->next)
+            scan = scan->next;
+        scan->next = new;
+    }
+
+    if (dm_src)
+        free(dm_src);
+    if (dm_src_type)
+        free(dm_src_type);
+    if (dm_tgt)
+        free(dm_tgt);
+    if (dm_param)
+        free(dm_param);
+    if (dm_tgtfs)
+        free(dm_tgtfs);
+
+    return rc;
+
+ out_free:
+
+    if (dm_src)
+        free(dm_src);
+    if (dm_src_type)
+        free(dm_src_type);
+    if (dm_tgt)
+        free(dm_tgt);
+    if (dm_param)
+        free(dm_param);
+    if (dm_tgtfs)
+        free(dm_tgtfs);
+
+
+    for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+        if (new->media_paths[i])
+            free(new->media_paths[i]);
+    }
+    if (new->mount_point)
+        free(new->mount_point);
+    if (new->ums_path)
+        free(new->ums_path);
+    return rc;
+}
+
+static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev)
+{
+    volume_t *scan = vol_root;
+    while(scan) {
+        if (scan->dev == dev)
+            return scan;
+        scan = scan->next;
+    }
+    return NULL;
+}
+
+static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked)
+{
+    volume_t *v = vol_root;
+
+    while(v) {
+        pthread_mutex_lock(&v->lock);
+        if (!strcmp(v->mount_point, mount_point)) {
+            if (!leave_locked)
+                pthread_mutex_unlock(&v->lock);
+            return v;
+        }
+        pthread_mutex_unlock(&v->lock);
+        v = v->next;
+    }
+    return NULL;
+}
+
+static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy)
+{
+    volume_t *scan = vol_root;
+    int i;
+
+    while (scan) {
+
+        for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
+            if (!scan->media_paths[i])
+                continue;
+
+            if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i])))
+                return scan;
+            else if (!fuzzy && !strcmp(media_path, scan->media_paths[i]))
+                return scan;
+        }
+
+        scan = scan->next;
+    }
+    return NULL;
+}
+
+/*
+ * Attempt to bring a volume online
+ * Returns: 0 on success, errno on failure, with the following exceptions:
+ *     - ENODATA - Unsupported filesystem type / blank
+ * vol->lock MUST be held!
+ */
+static int _volmgr_start(volume_t *vol, blkdev_t *dev)
+{
+    struct volmgr_fstable_entry *fs;
+    int rc = ENODATA;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("_volmgr_start(%s, %d:%d):", vol->mount_point,
+            dev->major, dev->minor);
+#endif
+
+    if (vol->state == volstate_mounted) {
+        LOGE("Unable to start volume '%s' (already mounted)", vol->mount_point);
+        return -EBUSY;
+    }
+
+    for (fs = fs_table; fs->name; fs++) {
+        if (!fs->identify_fn(dev))
+            break;
+    }
+
+    if (!fs) {
+        LOGE("No supported filesystems on %d:%d", dev->major, dev->minor);
+        volume_setstate(vol, volstate_nofs);
+        return -ENODATA;
+    }
+
+    return volmgr_start_fs(fs, vol, dev);
+}
+
+// vol->lock MUST be held!
+static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev)
+{
+    /*
+     * Spawn a thread to do the actual checking / mounting in
+     */
+
+    if (vol->worker_running) {
+        LOGE("Worker thread is currently running.. waiting..");
+        pthread_mutex_lock(&vol->worker_sem);
+        LOG_VOL("Worker thread now available");
+    }
+
+    vol->dev = dev; 
+
+    vol->worker_args.start_args.fs = fs;
+    vol->worker_args.start_args.dev = dev;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol);
+
+    return 0;
+}
+
+static void __start_fs_thread_lock_cleanup(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("__start_fs_thread_lock_cleanup(%s):", vol->mount_point);
+#endif
+
+    vol->worker_running = false;
+
+    // Wake up anyone that was waiting on this thread
+    pthread_mutex_unlock(&vol->worker_sem);
+
+    // Unlock the volume
+    pthread_mutex_unlock(&vol->lock);
+}
+
+static void *volmgr_start_fs_thread(void *arg)
+{
+    volume_t *vol = (volume_t *) arg;
+
+    pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg);
+    pthread_mutex_lock(&vol->lock);
+
+    vol->worker_running = true;
+    vol->worker_pid = getpid();
+
+    struct sigaction actions;
+
+    memset(&actions, 0, sizeof(actions));
+    sigemptyset(&actions.sa_mask);
+    actions.sa_flags = 0;
+    actions.sa_handler = volmgr_start_fs_thread_sighandler;
+    sigaction(SIGUSR1, &actions, NULL);
+
+    struct volmgr_fstable_entry *fs = vol->worker_args.start_args.fs;
+    blkdev_t                    *dev = vol->worker_args.start_args.dev;
+    int                          rc;
+  
+#if DEBUG_VOLMGR
+    LOG_VOL("Worker thread pid %d starting %s fs %d:%d on %s", getpid(),
+             fs->name, dev->major, dev->minor, vol->mount_point);
+#endif
+
+    if (fs->check_fn) {
+#if DEBUG_VOLMGR
+        LOG_VOL("Starting %s filesystem check on %d:%d", fs->name,
+                dev->major, dev->minor);
+#endif
+        volume_setstate(vol, volstate_checking);
+        pthread_mutex_unlock(&vol->lock);
+        rc = fs->check_fn(dev);
+        pthread_mutex_lock(&vol->lock);
+        if (vol->state != volstate_checking) {
+            LOG_VOL("filesystem check aborted");
+            goto out;
+        }
+        
+        if (rc < 0) {
+            LOGE("%s filesystem check failed on %d:%d (%s)", fs->name,
+                    dev->major, dev->minor, strerror(-rc));
+            if (rc == -ENODATA) {
+               volume_setstate(vol, volstate_nofs);
+               goto out;
+            }
+            goto out_unmountable;
+        }
+#if DEBUG_VOLMGR
+        LOG_VOL("%s filesystem check of %d:%d OK", fs->name,
+                dev->major, dev->minor);
+#endif
+    }
+
+    rc = fs->mount_fn(dev, vol, safe_mode);
+    if (!rc) {
+        LOG_VOL("Sucessfully mounted %s filesystem %d:%d on %s (safe-mode %s)",
+                fs->name, dev->major, dev->minor, vol->mount_point,
+                (safe_mode ? "on" : "off"));
+        vol->fs = fs;
+        volume_setstate(vol, volstate_mounted);
+        goto out;
+    }
+
+    LOGE("%s filesystem mount of %d:%d failed (%d)", fs->name, dev->major,
+         dev->minor, rc);
+
+ out_unmountable:
+    volume_setstate(vol, volstate_damaged);
+ out:
+    pthread_cleanup_pop(1);
+    pthread_exit(NULL);
+    return NULL;
+}
+
+static void volmgr_start_fs_thread_sighandler(int signo)
+{
+    LOGE("Volume startup thread got signal %d", signo);
+}
+
+static void volume_setstate(volume_t *vol, volume_state_t state)
+{
+    if (state == vol->state)
+        return;
+
+#if DEBUG_VOLMGR
+    LOG_VOL("Volume %s state change from %d -> %d", vol->mount_point, vol->state, state);
+#endif
+    
+    vol->state = state;
+    
+    char *prop_val = conv_volstate_to_propstr(vol->state);
+
+    if (prop_val) {
+        property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val);
+        volume_send_state(vol);
+    }
+}
+
+static int volume_send_state(volume_t *vol)
+{
+    char *event = conv_volstate_to_eventstr(vol->state);
+
+    return send_msg_with_data(event, vol->mount_point);
+}
+
+static char *conv_volstate_to_eventstr(volume_state_t state)
+{
+    int i;
+
+    for (i = 0; volume_state_strings[i].event != NULL; i++) {
+        if (volume_state_strings[i].state == state)
+            break;
+    }
+
+    return volume_state_strings[i].event;
+}
+
+static char *conv_volstate_to_propstr(volume_state_t state)
+{
+    int i;
+
+    for (i = 0; volume_state_strings[i].event != NULL; i++) {
+        if (volume_state_strings[i].state == state)
+            break;
+    }
+
+    return volume_state_strings[i].property_val;
+}
+
diff --git a/vold/volmgr.h b/vold/volmgr.h
new file mode 100644
index 0000000..2c7ec50
--- /dev/null
+++ b/vold/volmgr.h
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_H
+#define _VOLMGR_H
+
+#include <pthread.h>
+
+#include "vold.h"
+#include "blkdev.h"
+#include "media.h"
+#include "devmapper.h"
+
+#define PROP_EXTERNAL_STORAGE_STATE "EXTERNAL_STORAGE_STATE"
+
+// these must match the corresponding states in the MediaState enum.
+// A path to the volume mount point follows the colon
+typedef enum volume_state {
+    volstate_unknown,
+
+    volstate_nomedia,
+#define VOLD_EVT_NOMEDIA       "volume_nomedia:"
+#define VOLD_ES_PVAL_NOMEDIA   "removed"
+
+    volstate_unmounted,
+#define VOLD_EVT_UNMOUNTED     "volume_unmounted:"
+#define VOLD_ES_PVAL_UNMOUNTED "unmounted"
+
+    volstate_checking,
+#define VOLD_EVT_CHECKING      "volume_checking:"
+#define VOLD_ES_PVAL_CHECKING  "checking"
+
+    volstate_mounted,
+#define VOLD_EVT_MOUNTED       "volume_mounted:"
+#define VOLD_ES_PVAL_MOUNTED   "mounted"
+
+    volstate_mounted_ro,
+#define VOLD_EVT_MOUNTED_RO     "volume_mounted_ro:"
+#define VOLD_ES_PVAL_MOUNTED_RO "mounted_ro"
+
+    volstate_badremoval,
+#define VOLD_EVT_BADREMOVAL     "volume_badremoval:"
+#define VOLD_ES_PVAL_BADREMOVAL "bad_removal"
+
+    volstate_damaged,
+#define VOLD_EVT_DAMAGED         "volume_damaged:"
+#define VOLD_ES_PVAL_DAMAGED     "unmountable"
+
+    volstate_nofs,
+#define VOLD_EVT_NOFS            "volume_nofs:"
+#define VOLD_ES_PVAL_NOFS        "nofs"
+
+    volstate_ums,
+#define VOLD_EVT_UMS             "volume_ums:"
+#define VOLD_ES_PVAL_UMS         "shared"
+
+    volstate_ejecting,
+#define VOLD_EVT_EJECTING        "volume_ejecting:"
+#define VOLD_ES_PVAL_EJECTING    "ejecting"
+
+    volstate_formatting,
+} volume_state_t;
+
+struct volume;
+
+struct volmgr_fstable_entry {
+    char *name;
+    int     (*identify_fn) (blkdev_t *dev);
+    int     (*check_fn) (blkdev_t *dev);
+    int     (*mount_fn) (blkdev_t *dev, struct volume *vol, boolean safe_mode);
+    boolean case_sensitive_paths;
+};
+
+struct volmgr_start_args {
+    struct volmgr_fstable_entry *fs;
+    blkdev_t                    *dev;
+};
+
+struct volmgr_reaper_args {
+    void (*cb) (struct volume *, void *);
+    void *cb_arg;
+};
+
+#define VOLMGR_MAX_MEDIAPATHS_PER_VOLUME 8
+
+typedef struct volume {
+    char            *media_paths[VOLMGR_MAX_MEDIAPATHS_PER_VOLUME];
+
+    media_type_t      media_type;
+    char              *mount_point;
+    char              *ums_path;
+    struct devmapping *dm;
+
+    pthread_mutex_t          lock;
+    volume_state_t           state;
+    blkdev_t                 *dev;
+    pid_t                    worker_pid;
+    pthread_t                worker_thread;
+    union {
+        struct volmgr_start_args  start_args;
+        struct volmgr_reaper_args reaper_args;
+    } worker_args;
+    boolean                  worker_running;
+    pthread_mutex_t          worker_sem;
+
+    struct volmgr_fstable_entry *fs;
+
+    struct volume            *next;
+} volume_t;
+
+int volmgr_consider_disk(blkdev_t *dev);
+int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *));
+int volmgr_send_states(void);
+int volmgr_enable_ums(boolean enable);
+int volmgr_stop_volume_by_mountpoint(char *mount_point);
+int volmgr_start_volume_by_mountpoint(char *mount_point);
+int volmgr_safe_mode(boolean enable);
+int volmgr_format_volume(char *mount_point);
+int volmgr_set_volume_key(char *mount_point, unsigned char *key);
+void KillProcessesWithOpenFiles(const char* mountPoint, boolean sigkill, int *excluded, int num_excluded);
+#endif
diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c
new file mode 100644
index 0000000..680be21
--- /dev/null
+++ b/vold/volmgr_ext3.c
@@ -0,0 +1,184 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <linux/ext2_fs.h>
+#include <linux/ext3_fs.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "volmgr_ext3.h"
+#include "logwrapper.h"
+
+
+#define EXT_DEBUG 0
+
+static char E2FSCK_PATH[] = "/system/bin/e2fsck";
+
+int ext_identify(blkdev_t *dev)
+{
+    int rc = -1;
+    int fd;
+    struct ext3_super_block sb;
+    char *devpath;
+
+#if EXT_DEBUG
+    LOG_VOL("ext_identify(%d:%d):", dev-major, dev->minor);
+#endif
+
+    devpath = blkdev_get_devpath(dev);
+
+    if ((fd = open(devpath, O_RDWR)) < 0) {
+        LOGE("Unable to open device '%s' (%s)", devpath,
+             strerror(errno));
+        free(devpath);
+        return -errno;
+    }
+
+    if (lseek(fd, 1024, SEEK_SET) < 0) {
+        LOGE("Unable to lseek to get superblock (%s)", strerror(errno));
+        rc =  -errno;
+        goto out;
+    }
+
+    if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) {
+        LOGE("Unable to read superblock (%s)", strerror(errno));
+        rc =  -errno;
+        goto out;
+    }
+
+    if (sb.s_magic == EXT2_SUPER_MAGIC ||
+        sb.s_magic == EXT3_SUPER_MAGIC)
+        rc = 0;
+    else
+        rc = -ENODATA;
+
+ out:
+#if EXT_DEBUG
+    LOG_VOL("ext_identify(%s): rc = %d", devpath, rc);
+#endif
+    free(devpath);
+    close(fd);
+    return rc;
+}
+
+int ext_check(blkdev_t *dev)
+{
+    char *devpath;
+
+#if EXT_DEBUG
+    LOG_VOL("ext_check(%s):", dev->dev_fspath);
+#endif
+
+    devpath = blkdev_get_devpath(dev);
+
+    if (access(E2FSCK_PATH, X_OK)) {
+        LOGE("ext_check(%s): %s not found (skipping checks)",
+             devpath, E2FSCK_PATH);
+        free(devpath);
+        return 0;
+    }
+
+    char *args[5];
+
+    args[0] = E2FSCK_PATH;
+    args[1] = "-v";
+    args[2] = "-p";
+    args[3] = devpath;
+    args[4] = NULL;
+
+    int rc = logwrap(4, args);
+
+    if (rc == 0) {
+        LOG_VOL("filesystem '%s' had no errors", devpath);
+    } else if (rc == 1) {
+        LOG_VOL("filesystem '%s' had corrected errors", devpath);
+        rc = 0;
+    } else if (rc == 2) {
+        LOGE("VOL volume '%s' had corrected errors (system should be rebooted)", devpath);
+        rc = -EIO;
+    } else if (rc == 4) {
+        LOGE("VOL volume '%s' had uncorrectable errors", devpath);
+        rc = -EIO;
+    } else if (rc == 8) {
+        LOGE("Operational error while checking volume '%s'", devpath);
+        rc = -EIO;
+    } else {
+        LOGE("Unknown e2fsck exit code (%d)", rc);
+        rc = -EIO;
+    }
+    free(devpath);
+    return rc;
+}
+
+int ext_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode)
+{
+#if EXT_DEBUG
+    LOG_VOL("ext_mount(%s, %s, %d):", dev->dev_fspath, vol->mount_point, safe_mode);
+#endif
+
+    char *fs[] = { "ext3", "ext2", NULL };
+    char *devpath;
+
+    devpath = blkdev_get_devpath(dev);
+
+    int flags, rc = 0;
+
+    flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME | MS_NODIRATIME;
+
+    if (safe_mode)
+        flags |= MS_SYNCHRONOUS;
+
+    if (vol->state == volstate_mounted) {
+        LOG_VOL("Remounting %s on %s, safe mode %d", devpath,
+                vol->mount_point, safe_mode);
+        flags |= MS_REMOUNT;
+    }
+ 
+    char **f;
+    for (f = fs; *f != NULL; f++) {
+        rc = mount(devpath, vol->mount_point, *f, flags, NULL);
+        if (rc && errno == EROFS) {
+            LOGE("ext_mount(%s, %s): Read only filesystem - retrying mount RO",
+                 devpath, vol->mount_point);
+            flags |= MS_RDONLY;
+            rc = mount(devpath, vol->mount_point, *f, flags, NULL);
+        }
+#if EXT_DEBUG
+        LOG_VOL("ext_mount(%s, %s): %s mount rc = %d", devpath, *f,
+                vol->mount_point, rc);
+#endif
+        if (!rc)
+            break;
+    }
+    free(devpath);
+
+    // Chmod the mount point so that its a free-for-all.
+    // (required for consistency with VFAT.. sigh)
+    if (chmod(vol->mount_point, 0777) < 0) {
+        LOGE("Failed to chmod %s (%s)", vol->mount_point, strerror(errno));
+        return -errno;
+    }
+    
+    return rc;
+}
diff --git a/vold/volmgr_ext3.h b/vold/volmgr_ext3.h
new file mode 100644
index 0000000..bfe882a
--- /dev/null
+++ b/vold/volmgr_ext3.h
@@ -0,0 +1,27 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_EXT3_H
+#define _VOLMGR_EXT3_H
+
+#include "volmgr.h"
+#include "blkdev.h"
+
+int ext_identify(blkdev_t *blkdev);
+int ext_check(blkdev_t *blkdev);
+int ext_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode);
+#endif
diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c
new file mode 100644
index 0000000..1dc4c8d
--- /dev/null
+++ b/vold/volmgr_vfat.c
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+
+#include <sys/mount.h>
+
+#include "vold.h"
+#include "volmgr.h"
+#include "volmgr_vfat.h"
+#include "logwrapper.h"
+
+#define VFAT_DEBUG 0
+
+static char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
+
+int vfat_identify(blkdev_t *dev)
+{
+#if VFAT_DEBUG
+    LOG_VOL("vfat_identify(%d:%d):", dev->major, dev->minor);
+#endif
+    return 0; // XXX: Implement
+}
+
+int vfat_check(blkdev_t *dev)
+{
+    int rc;
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_check(%d:%d):", dev->major, dev->minor);
+#endif
+
+    if (access(FSCK_MSDOS_PATH, X_OK)) {
+        LOGE("vfat_check(%d:%d): %s not found (skipping checks)",
+             dev->major, dev->minor, FSCK_MSDOS_PATH);
+        return 0;
+    }
+
+#ifdef VERIFY_PASS
+    char *args[7];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-V";
+    args[3] = "-w";
+    args[4] = "-p";
+    args[5] = blkdev_get_devpath(dev);
+    args[6] = NULL;
+    rc = logwrap(6, args);
+    free(args[5]);
+#else
+    char *args[6];
+    args[0] = FSCK_MSDOS_PATH;
+    args[1] = "-v";
+    args[2] = "-w";
+    args[3] = "-p";
+    args[4] = blkdev_get_devpath(dev);
+    args[5] = NULL;
+    rc = logwrap(5, args);
+    free(args[4]);
+#endif
+
+    if (rc == 0) {
+        LOG_VOL("Filesystem check completed OK");
+        return 0;
+    } else if (rc == 1) {
+        LOG_VOL("Filesystem check failed (general failure)");
+        return -EINVAL;
+    } else if (rc == 2) {
+        LOG_VOL("Filesystem check failed (invalid usage)");
+        return -EIO;
+    } else if (rc == 4) {
+        LOG_VOL("Filesystem check completed (errors fixed)");
+    } else if (rc == 8) {
+        LOG_VOL("Filesystem check failed (not a FAT filesystem)");
+        return -ENODATA;
+    } else {
+        LOG_VOL("Filesystem check failed (unknown exit code %d)", rc);
+        return -EIO;
+    }
+    return 0;
+}
+
+int vfat_mount(blkdev_t *dev, volume_t *vol, boolean safe_mode)
+{
+    int flags, rc;
+    char *devpath;
+
+    devpath = blkdev_get_devpath(dev);
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_mount(%d:%d, %s, %d):", dev->major, dev->minor, vol->mount_point, safe_mode);
+#endif
+
+    flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
+
+    if (safe_mode)
+        flags |= MS_SYNCHRONOUS;
+    if (vol->state == volstate_mounted) {
+        LOG_VOL("Remounting %d:%d on %s, safe mode %d", dev->major,
+                dev->minor, vol->mount_point, safe_mode);
+        flags |= MS_REMOUNT;
+    }
+
+    rc = mount(devpath, vol->mount_point, "vfat", flags,
+               "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+
+    if (rc && errno == EROFS) {
+        LOGE("vfat_mount(%d:%d, %s): Read only filesystem - retrying mount RO",
+             dev->major, dev->minor, vol->mount_point);
+        flags |= MS_RDONLY;
+        rc = mount(devpath, vol->mount_point, "vfat", flags,
+                   "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
+    }
+
+#if VFAT_DEBUG
+    LOG_VOL("vfat_mount(%s, %d:%d): mount rc = %d", dev->major,k dev->minor,
+            vol->mount_point, rc);
+#endif
+    free (devpath);
+    return rc;
+}
diff --git a/vold/volmgr_vfat.h b/vold/volmgr_vfat.h
new file mode 100644
index 0000000..d9cf04d
--- /dev/null
+++ b/vold/volmgr_vfat.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _VOLMGR_VFAT_H
+#define _VOLMGR_VFAT_H
+
+#include "volmgr.h"
+#include "blkdev.h"
+
+
+
+int vfat_identify(blkdev_t *blkdev);
+int vfat_check(blkdev_t *blkdev);
+int vfat_mount(blkdev_t *blkdev, volume_t *vol, boolean safe_mode);
+#endif