+ new->serial = serial;
+ new->ret = 0;
+ new->group_start = 0;
+}
+
+static void op_add_nothing(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (words[2])
+ fail(filename, op_num+1, "Expected no arguments");
+ op[op_num].key = tdb_null;
+}
+
+static void op_add_key(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (words[2] == NULL || words[3])
+ fail(filename, op_num+1, "Expected just a key");
+
+ op[op_num].key = make_tdb_data(op, filename, op_num+1, words[2]);
+ if (op[op_num].op != OP_TDB_TRAVERSE)
+ total_keys++;
+}
+
+static void op_add_key_ret(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (!words[2] || !words[3] || !words[4] || words[5]
+ || !streq(words[3], "="))
+ fail(filename, op_num+1, "Expected <key> = <ret>");
+ op[op_num].ret = atoi(words[4]);
+ op[op_num].key = make_tdb_data(op, filename, op_num+1, words[2]);
+ /* May only be a unique key if it fails */
+ if (op[op_num].ret != 0)
+ total_keys++;
+}
+
+static void op_add_key_data(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (!words[2] || !words[3] || !words[4] || words[5]
+ || !streq(words[3], "="))
+ fail(filename, op_num+1, "Expected <key> = <data>");
+ op[op_num].key = make_tdb_data(op, filename, op_num+1, words[2]);
+ op[op_num].data = make_tdb_data(op, filename, op_num+1, words[4]);
+ /* May only be a unique key if it fails */
+ if (!op[op_num].data.dptr)
+ total_keys++;
+}
+
+/* <serial> tdb_store <rec> <rec> <flag> = <ret> */
+static void op_add_store(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (!words[2] || !words[3] || !words[4] || !words[5] || !words[6]
+ || words[7] || !streq(words[5], "="))
+ fail(filename, op_num+1, "Expect <key> <data> <flag> = <ret>");
+
+ op[op_num].flag = strtoul(words[4], NULL, 0);
+ op[op_num].ret = atoi(words[6]);
+ op[op_num].key = make_tdb_data(op, filename, op_num+1, words[2]);
+ op[op_num].data = make_tdb_data(op, filename, op_num+1, words[3]);
+ total_keys++;
+}
+
+/* <serial> tdb_append <rec> <rec> = <rec> */
+static void op_add_append(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ TDB_DATA post_append;
+
+ if (!words[2] || !words[3] || !words[4] || !words[5] || words[6]
+ || !streq(words[4], "="))
+ fail(filename, op_num+1, "Expect <key> <data> = <rec>");
+
+ op[op_num].key = make_tdb_data(op, filename, op_num+1, words[2]);
+ op[op_num].data = make_tdb_data(op, filename, op_num+1, words[3]);
+
+ post_append = make_tdb_data(op, filename, op_num+1, words[5]);
+
+ /* By subtraction, figure out what previous data was. */
+ op[op_num].pre_append.dptr = post_append.dptr;
+ op[op_num].pre_append.dsize = post_append.dsize - op[op_num].data.dsize;
+ total_keys++;
+}
+
+/* <serial> tdb_get_seqnum = <ret> */
+static void op_add_seqnum(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (!words[2] || !words[3] || words[4] || !streq(words[2], "="))
+ fail(filename, op_num+1, "Expect = <ret>");
+
+ op[op_num].key = tdb_null;
+ op[op_num].ret = atoi(words[3]);
+}
+
+static void op_add_traverse(const char *filename,
+ struct op op[], unsigned int op_num, char *words[])
+{
+ if (words[2])
+ fail(filename, op_num+1, "Expect no arguments");
+
+ op[op_num].key = tdb_null;
+ op[op_num].trav = NULL;
+}
+
+static void op_add_transaction(const char *filename, struct op op[],
+ unsigned int op_num, char *words[])
+{
+ if (words[2])
+ fail(filename, op_num+1, "Expect no arguments");
+
+ op[op_num].key = tdb_null;
+ op[op_num].transaction_end = 0;
+}
+
+static void op_analyze_transaction(const char *filename,
+ struct op op[], unsigned int op_num,
+ char *words[])
+{
+ int i, start;
+
+ op[op_num].key = tdb_null;
+
+ if (words[2])
+ fail(filename, op_num+1, "Expect no arguments");
+
+ for (i = op_num-1; i >= 0; i--) {
+ if (op[i].op == OP_TDB_TRANSACTION_START &&
+ !op[i].transaction_end)
+ break;
+ }
+
+ if (i < 0)
+ fail(filename, op_num+1, "no transaction start found");
+
+ start = i;
+ op[start].transaction_end = op_num;
+
+ /* This rolls in nested transactions. I think that's right. */
+ for (i++; i <= op_num; i++)
+ op[i].group_start = start;
+}
+
+struct traverse_hash {
+ TDB_DATA key;
+ unsigned int index;
+};
+
+/* A traverse is a hash of keys, each one associated with ops. */
+struct traverse {
+ /* How many traversal callouts should I do? */
+ unsigned int num;
+
+ /* Where is traversal end op? */
+ unsigned int end;
+
+ /* For trivial traversals. */
+ struct traverse_hash *hash;
+};
+
+/* A trivial traversal is one which doesn't terminate early and only
+ * plays with its own record. We can reliably replay these even if
+ * traverse order changes. */
+static bool is_trivial_traverse(struct op op[], unsigned int end)
+{
+#if 0
+ unsigned int i;
+ TDB_DATA cur = tdb_null;
+
+ if (op[end].ret != 0)
+ return false;
+
+ for (i = 0; i < end; i++) {
+ if (!op[i].key.dptr)
+ continue;
+ if (op[i].op == OP_TDB_TRAVERSE)
+ cur = op[i].key;
+ if (!key_eq(cur, op[i].key))
+ return false;
+ }
+ return true;
+#endif
+ /* With multiple things happening at once, no traverse is trivial. */
+ return false;
+}
+
+static void op_analyze_traverse(const char *filename,
+ struct op op[], unsigned int op_num,
+ char *words[])
+{
+ int i, start;
+ struct traverse *trav = talloc(op, struct traverse);
+
+ op[op_num].key = tdb_null;
+
+ /* = %u means traverse function terminated. */
+ if (words[2]) {
+ if (!streq(words[2], "=") || !words[3] || words[4])
+ fail(filename, op_num+1, "expect = <num>");
+ op[op_num].ret = atoi(words[3]);
+ } else
+ op[op_num].ret = 0;
+
+ trav->num = 0;
+ trav->end = op_num;
+ for (i = op_num-1; i >= 0; i--) {
+ if (op[i].op == OP_TDB_TRAVERSE)
+ trav->num++;
+ if (op[i].op != OP_TDB_TRAVERSE_READ_START
+ && op[i].op != OP_TDB_TRAVERSE_START)
+ continue;
+ if (op[i].trav)
+ continue;
+ break;
+ }
+
+ if (i < 0)
+ fail(filename, op_num+1, "no traversal start found");
+
+ start = i;
+ op[start].trav = trav;
+
+ for (i = start; i <= op_num; i++)
+ op[i].group_start = start;
+
+ if (is_trivial_traverse(op+i, op_num-i)) {
+ /* Fill in a plentiful hash table. */
+ op[start].trav->hash = talloc_zero_array(op[i].trav,
+ struct traverse_hash,
+ trav->num * 2);
+ for (i = start; i < op_num; i++) {
+ unsigned int h;
+ if (op[i].op != OP_TDB_TRAVERSE)
+ continue;
+ h = hash_key(&op[i].key) % (trav->num * 2);
+ while (trav->hash[h].index)
+ h = (h + 1) % (trav->num * 2);
+ trav->hash[h].index = i+1;
+ trav->hash[h].key = op[i].key;
+ }
+ } else
+ trav->hash = NULL;
+}
+
+/* Keep -Wmissing-declarations happy: */
+const struct op_table *
+find_keyword (register const char *str, register unsigned int len);
+
+#include "keywords.c"
+
+struct depend {
+ /* We can have more than one */
+ struct list_node list;
+ unsigned int file;
+ unsigned int op;
+};
+
+struct depend_xmit {
+ unsigned int dst_op;
+ unsigned int src_file, src_op;
+};
+
+static void remove_matching_dep(struct list_head *deps,
+ unsigned int file, unsigned int op)
+{
+ struct depend *dep;
+
+ list_for_each(deps, dep, list) {
+ if (dep->file == file && dep->op == op) {
+ list_del(&dep->list);
+ return;
+ }
+ }
+ errx(1, "Failed to find depend on file %u line %u\n", file, op+1);
+}
+
+static void check_deps(const char *filename, struct op op[], unsigned int num)
+{
+#ifdef DEBUG_DEPS
+ unsigned int i;
+
+ for (i = 1; i < num; i++)
+ if (!list_empty(&op[i].pre))
+ fail(filename, i+1, "Still has dependencies");
+#endif
+}
+
+static void dump_pre(char *filename[], unsigned int file,
+ struct op op[], unsigned int i)
+{
+ struct depend *dep;
+
+ printf("%s:%u still waiting for:\n", filename[file], i+1);
+ list_for_each(&op[i].pre, dep, list)
+ printf(" %s:%u\n", filename[dep->file], dep->op+1);
+ check_deps(filename[file], op, i);
+}
+
+static void do_pre(char *filename[], unsigned int file, int pre_fd,
+ struct op op[], unsigned int i)
+{
+ while (!list_empty(&op[i].pre)) {
+ struct depend_xmit dep;
+
+#if DEBUG_DEPS
+ printf("%s:%u:waiting for pre\n", filename[file], i+1);
+ fflush(stdout);
+#endif
+ alarm(10);
+ while (read(pre_fd, &dep, sizeof(dep)) != sizeof(dep)) {
+ if (errno == EINTR) {
+ dump_pre(filename, file, op, i);
+ exit(1);
+ } else
+ errx(1, "Reading from pipe");
+ }
+ alarm(0);
+
+#if DEBUG_DEPS
+ printf("%s:%u:got pre %u from %s:%u\n", filename[file], i+1,
+ dep.dst_op+1, filename[dep.src_file], dep.src_op+1);
+ fflush(stdout);
+#endif
+ /* This could be any op, not just this one. */
+ remove_matching_dep(&op[dep.dst_op].pre,
+ dep.src_file, dep.src_op);
+ }
+}
+
+static void do_post(char *filename[], unsigned int file,
+ const struct op op[], unsigned int i)
+{
+ struct depend *dep;
+
+ list_for_each(&op[i].post, dep, list) {
+ struct depend_xmit dx;
+
+ dx.src_file = file;
+ dx.src_op = i;
+ dx.dst_op = dep->op;
+#if DEBUG_DEPS
+ printf("%s:%u:sending to file %s:%u\n", filename[file], i+1,
+ filename[dep->file], dep->op+1);
+#endif
+ if (write(pipes[dep->file].fd[1], &dx, sizeof(dx))
+ != sizeof(dx))
+ err(1, "%s:%u failed to tell file %s",
+ filename[file], i+1, filename[dep->file]);
+ }