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, ×tamp, &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, ×tamp, &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,
+ ¬ificationIterators[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,
+ ®);
+ _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
+ ®);
+ 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, ®);
+ if (op & 0x40)
+ reg -= offset;
+ else
+ reg += offset;
+ _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
+ 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, ®);
+ _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
+ 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,
+ ®);
+ 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,
+ ®);
+ 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 = ¤t->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 = ¤t->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 = ¤t->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®Mask);
+ 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 = ≈
+ 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) "ef;
+ (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, ¤t_char, 1);
+ write(STDOUT_FILENO, ¤t_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, ¤t_char, 1);
+ write(STDERR_FILENO, "\b", 1);
+ }
+ continue;
+ }
+ if(current_char && buf[0] != last_char_in) {
+ write(STDERR_FILENO, ¤t_char, 1);
+ write(STDOUT_FILENO, ¤t_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, ¤t_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 ®isters[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