+
+struct EvalResult
+match_list(struct Gc *gc, const char *format, struct Expr xs, ...)
+{
+ va_list args_list;
+ va_start(args_list, xs);
+
+ /* TODO(#544): match_list is O(N) even in best case (format == "*") */
+ if (!list_p(xs)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "listp", xs);
+ }
+
+ long int i = 0;
+ for (i = 0; *format != 0 && !nil_p(xs); ++i) {
+ struct Expr x = CAR(xs);
+
+ switch (*format) {
+ case 'd': {
+ if (!number_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "numberp", x);
+ }
+
+ long int *p = va_arg(args_list, long int *);
+ if (p != NULL) {
+ *p = x.atom->num;
+ }
+ } break;
+
+ case 's': {
+ if (!string_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "stringp", x);
+ }
+
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->str;
+ }
+ } break;
+
+ case 'q': {
+ if (!symbol_p(x)) {
+ va_end(args_list);
+ return wrong_argument_type(gc, "symbolp", x);
+ }
+
+ const char **p = va_arg(args_list, const char**);
+ if (p != NULL) {
+ *p = x.atom->sym;
+ }
+ } break;
+
+ case 'e': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ *p = x;
+ } break;
+
+ case '*': {
+ struct Expr *p = va_arg(args_list, struct Expr*);
+ if (p != NULL) {
+ *p = xs;
+ }
+ xs = NIL(gc);
+ } break;
+ }
+
+ format++;
+ if (!nil_p(xs)) {
+ xs = CDR(xs);
+ }
+ }
+
+ if (*format != 0 || !nil_p(xs)) {
+ return wrong_number_of_arguments(gc, i);
+ }
+
+ return eval_success(NIL(gc));
+}
+
+/* TODO(#542): format_list(). Similar to match_list() but for constructing list */