Initial Contribution
diff --git a/amend/register.c b/amend/register.c
new file mode 100644
index 0000000..167dd32
--- /dev/null
+++ b/amend/register.c
@@ -0,0 +1,394 @@
+/*
+ * 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>
+#undef NDEBUG
+#include <assert.h>
+#include "commands.h"
+
+#include "register.h"
+
+#define UNUSED(p)   ((void)(p))
+
+#define CHECK_BOOL() \
+    do { \
+        assert(argv == NULL); \
+        if (argv != NULL) return -1; \
+        assert(argc == true || argc == false); \
+        if (argc != true && argc != false) return -1; \
+    } while (false)
+
+#define CHECK_WORDS() \
+    do { \
+        assert(argc >= 0); \
+        if (argc < 0) return -1; \
+        assert(argc == 0 || argv != NULL); \
+        if (argc != 0 && argv == NULL) return -1; \
+        if (permissions != NULL) { \
+            int CW_I_; \
+            for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
+                assert(argv[CW_I_] != NULL); \
+                if (argv[CW_I_] == NULL) return -1; \
+            } \
+        } \
+    } while (false)
+
+#define CHECK_FN() \
+    do { \
+        CHECK_WORDS(); \
+        if (permissions != NULL) { \
+            assert(result == NULL); \
+            if (result != NULL) return -1; \
+        } else { \
+            assert(result != NULL); \
+            if (result == NULL) return -1; \
+        } \
+    } while (false)
+
+#define NO_PERMS(perms) \
+    do { \
+        PermissionRequestList *NP_PRL_ = (perms); \
+        if (NP_PRL_ != NULL) { \
+            int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
+                    "", false, PERM_NONE); \
+            if (NP_RET_ < 0) { \
+                /* Returns from the calling function. \
+                 */ \
+                return NP_RET_; \
+            } \
+        } \
+    } while (false)
+
+/*
+ * Command definitions
+ */
+
+/* assert <boolexpr>
+ */
+static int
+cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_BOOL();
+    NO_PERMS(permissions);
+
+    /* If our argument is false, return non-zero (failure)
+     * If our argument is true, return zero (success)
+     */
+    if (argc) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+/* format <root>
+ */
+static int
+cmd_format(const char *name, void *cookie, int argc, const char *argv[],
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_WORDS();
+//xxx
+    return -1;
+}
+
+/* copy_dir <srcdir> <dstdir>
+ */
+static int
+cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_WORDS();
+//xxx
+    return -1;
+}
+
+/* mark <resource> dirty|clean
+ */
+static int
+cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_WORDS();
+//xxx when marking, save the top-level hash at the mark point
+//    so we can retry on failure.  Otherwise the hashes won't match,
+//    or someone could intentionally dirty the FS to force a downgrade
+//xxx
+    return -1;
+}
+
+/* done
+ */
+static int
+cmd_done(const char *name, void *cookie, int argc, const char *argv[],
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_WORDS();
+//xxx
+    return -1;
+}
+
+int
+registerUpdateCommands()
+{
+    int ret;
+
+    ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL);
+    if (ret < 0) return ret;
+
+//xxx some way to fix permissions
+//xxx could have "installperms" commands that build the fs_config list
+//xxx along with a "commitperms", and any copy_dir etc. needs to see
+//    a commitperms before it will work
+
+    return 0;
+}
+
+
+/*
+ * Function definitions
+ */
+
+/* update_forced()
+ *
+ * Returns "true" if some system setting has determined that
+ * the update should happen no matter what.
+ */
+static int
+fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
+        char **result, size_t *resultLen,
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_FN();
+    NO_PERMS(permissions);
+
+    if (argc != 0) {
+        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+                name, argc);
+        return 1;
+    }
+
+    //xxx check some global or property
+    bool force = true;
+    if (force) {
+        *result = strdup("true");
+    } else {
+        *result = strdup("");
+    }
+    if (resultLen != NULL) {
+        *resultLen = strlen(*result);
+    }
+
+    return 0;
+}
+
+/* get_mark(<resource>)
+ *
+ * Returns the current mark associated with the provided resource.
+ */
+static int
+fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
+        char **result, size_t *resultLen,
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_FN();
+    NO_PERMS(permissions);
+
+    if (argc != 1) {
+        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+                name, argc);
+        return 1;
+    }
+
+    //xxx look up the value
+    *result = strdup("");
+    if (resultLen != NULL) {
+        *resultLen = strlen(*result);
+    }
+
+    return 0;
+}
+
+/* hash_dir(<path-to-directory>)
+ */
+static int
+fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
+        char **result, size_t *resultLen,
+        PermissionRequestList *permissions)
+{
+    int ret = -1;
+
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_FN();
+
+    const char *dir;
+    if (argc != 1) {
+        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+                name, argc);
+        return 1;
+    } else {
+        dir = argv[0];
+    }
+
+    if (permissions != NULL) {
+        if (dir == NULL) {
+            /* The argument is the result of another function.
+             * Assume the worst case, where the function returns
+             * the root.
+             */
+            dir = "/";
+        }
+        ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
+    } else {
+//xxx build and return the string
+        *result = strdup("hashvalue");
+        if (resultLen != NULL) {
+            *resultLen = strlen(*result);
+        }
+        ret = 0;
+    }
+
+    return ret;
+}
+
+/* matches(<str>, <str1> [, <strN>...])
+ * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
+ * otherwise returns "".
+ *
+ * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
+ */
+static int
+fn_matches(const char *name, void *cookie, int argc, const char *argv[],
+        char **result, size_t *resultLen,
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_FN();
+    NO_PERMS(permissions);
+
+    if (argc < 2) {
+        fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
+                name, argc);
+        return 1;
+    }
+
+    int i;
+    for (i = 1; i < argc; i++) {
+        if (strcmp(argv[0], argv[i]) == 0) {
+            *result = strdup(argv[0]);
+            if (resultLen != NULL) {
+                *resultLen = strlen(*result);
+            }
+            return 0;
+        }
+    }
+
+    *result = strdup("");
+    if (resultLen != NULL) {
+        *resultLen = 1;
+    }
+    return 0;
+}
+
+/* concat(<str>, <str1> [, <strN>...])
+ * Returns the concatenation of all strings.
+ */
+static int
+fn_concat(const char *name, void *cookie, int argc, const char *argv[],
+        char **result, size_t *resultLen,
+        PermissionRequestList *permissions)
+{
+    UNUSED(name);
+    UNUSED(cookie);
+    CHECK_FN();
+    NO_PERMS(permissions);
+
+    size_t totalLen = 0;
+    int i;
+    for (i = 0; i < argc; i++) {
+        totalLen += strlen(argv[i]);
+    }
+
+    char *s = (char *)malloc(totalLen + 1);
+    if (s == NULL) {
+        return -1;
+    }
+    s[totalLen] = '\0';
+    for (i = 0; i < argc; i++) {
+        //TODO: keep track of the end to avoid walking the string each time
+        strcat(s, argv[i]);
+    }
+    *result = s;
+    if (resultLen != NULL) {
+        *resultLen = strlen(s);
+    }
+
+    return 0;
+}
+
+int
+registerUpdateFunctions()
+{
+    int ret;
+
+    ret = registerFunction("update_forced", fn_update_forced, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerFunction("get_mark", fn_get_mark, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerFunction("hash_dir", fn_hash_dir, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerFunction("matches", fn_matches, NULL);
+    if (ret < 0) return ret;
+
+    ret = registerFunction("concat", fn_concat, NULL);
+    if (ret < 0) return ret;
+
+    return 0;
+}