+static char *
+missing_operator_error(const char *arg, const char **allowed_operators,
+ size_t n_allowed)
+{
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_format(&s, "%s: argument does not end in ", arg);
+ ds_put_format(&s, "\"%s\"", allowed_operators[0]);
+ if (n_allowed == 2) {
+ ds_put_format(&s, " or \"%s\"", allowed_operators[1]);
+ } else if (n_allowed > 2) {
+ size_t i;
+
+ for (i = 1; i < n_allowed - 1; i++) {
+ ds_put_format(&s, ", \"%s\"", allowed_operators[i]);
+ }
+ ds_put_format(&s, ", or \"%s\"", allowed_operators[i]);
+ }
+ ds_put_format(&s, " followed by a value.");
+
+ return ds_steal_cstr(&s);
+}
+
+/* Breaks 'arg' apart into a number of fields in the following order:
+ *
+ * - If 'columnp' is nonnull, the name of a column in 'table'. The column
+ * is stored into '*columnp'. The column name may be abbreviated.
+ *
+ * - If 'keyp' is nonnull, optionally a key string. (If both 'columnp'
+ * and 'keyp' are nonnull, then the column and key names are expected to
+ * be separated by ':'). The key is stored as a malloc()'d string into
+ * '*keyp', or NULL if no key is present in 'arg'.
+ *
+ * - If 'valuep' is nonnull, an operator followed by a value string. The
+ * allowed operators are the 'n_allowed' string in 'allowed_operators',
+ * or just "=" if 'n_allowed' is 0. If 'operatorp' is nonnull, then the
+ * operator is stored into '*operatorp' (one of the pointers from
+ * 'allowed_operators' is stored; nothing is malloc()'d). The value is
+ * stored as a malloc()'d string into '*valuep', or NULL if no value is
+ * present in 'arg'.
+ *
+ * At least 'columnp' or 'keyp' must be nonnull.
+ *
+ * On success, returns NULL. On failure, returned a malloc()'d string error
+ * message and stores NULL into all of the nonnull output arguments. */