diff --git a/evaluate.c b/evaluate.c
index b96696d..82ddf9f 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -2243,11 +2243,154 @@ static struct symbol *evaluate_alignof(struct expression *expr)
 	return size_t_ctype;
 }
 
+static int decompose_format_printf(const char *string, struct symbol **result)
+{
+	int count = 0;
+
+	for (; string[0] != '\0'; string++) {
+		if (string[0] == '%') {
+			int len = 0;
+			struct symbol *sym = NULL;
+			if (string[1] == '%') {
+				string++;
+				continue;
+			}
+
+			/* get rid of any formatting width bits */
+			while (isdigit(string[1]) || string[1] == '+' || string[1] == '-')
+			       string++;
+
+			switch (string[1]) {
+			case 'C':
+				/* TODO - same as lc */
+				break;
+			case 'c':
+				/* TODO - can take l modifier */
+				sym = &char_ctype;
+				break;
+			case 'f':
+			case 'g':
+				sym = &double_ctype;
+				break;
+			case 'h':
+				/* TODO hh */
+				len = -1;
+				break;
+			case 'j':	/* ignore intmax/uintmax for the moment */
+				break;
+			case 'L':
+				sym = &ldouble_ctype;
+				break;
+			case 'l':
+				len++;
+				break;
+			case 'p':
+				/* TODO - deal with void * not being de-referenced in some cases*/
+				sym = &ptr_ctype;
+				break;
+			case 'q':
+				len = 2;
+				break;
+			case 's':
+				sym = &string_ctype;
+				break;
+			case 'n':
+				/* TODO - actually pointer to integer */
+				sym = &ptr_ctype;
+				break;
+				/* note, d is out of alpha order */
+			case 'd':
+				switch (len) {
+				case -1: sym = &short_ctype; break;
+				case 0: sym = &int_ctype; break;
+				case 1: sym = &long_ctype; break;
+				case 2: sym = &llong_ctype; break;
+				case 3: sym = &lllong_ctype; break;
+				}
+				break;
+			case 'u':
+				switch (len) {
+				case -1: sym = &ushort_ctype; break;
+				case 0: sym = &uint_ctype; break;
+				case 1: sym = &ulong_ctype; break;
+				case 2: sym = &ullong_ctype; break;
+				case 3: sym = &ulllong_ctype; break;
+				}
+				break;
+			case 'x':
+			case 'X':
+				switch (len) {
+				case 0: sym = &uint_ctype; break;
+				case 1: sym = &ulong_ctype; break;
+				case 2: sym = &ullong_ctype; break;
+				case 3: sym = &ulllong_ctype; break;
+				}
+				break;
+			case 'z':
+			case 'Z':
+				sym = &uint_ctype;	/* TODO */
+				break;
+			}
+
+			if (result && sym)
+				*result++ = sym;
+			if (sym)
+				count++;
+
+			while (string[0] != ' ' && string[0] != '\0')
+				string++;
+
+			string--;
+		}
+	}
+
+	return count;
+}
+
+
+static int evaluate_format_printf(struct symbol *fn, struct expression *expr, struct symbol ***result)
+{
+	const char *fmt_string = NULL;
+
+	if (!expr)
+		return -1;
+	if (expr->string && expr->string->length)
+		fmt_string = expr->string->data;
+	if (!fmt_string) {
+		struct symbol *sym = evaluate_expression(expr);
+
+		/* attempt to find initialiser for this */
+		if (sym && sym->initializer && sym->initializer->string)
+			fmt_string = sym->initializer->string->data;
+	}
+
+	if (fmt_string) {
+		struct symbol **syms = NULL;
+		int count;
+
+		count = decompose_format_printf(fmt_string, NULL);
+		if (count <= 0)
+			return count;
+
+		syms = calloc(sizeof(struct symbol *), count);
+		if (!syms)
+			return -1;
+
+		count = decompose_format_printf(fmt_string, syms);
+		*result = syms;
+		return count;
+	}
+
+	return -1;
+}
+
 static int evaluate_arguments(struct symbol *fn, struct expression_list *head)
 {
 	struct expression *expr;
 	struct symbol_list *argument_types = fn->arguments;
+	struct symbol **variadic = NULL;
 	struct symbol *argtype;
+	int variadic_limit = 0;
 	int i = 1;
 
 	PREPARE_PTR_LIST(argument_types, argtype);
@@ -2259,7 +2402,18 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head)
 		if (!ctype)
 			return 0;
 
-		target = argtype;
+		if (i == fn->printf_msg) {
+			int ret = evaluate_format_printf(fn, *p, &variadic);
+			if (ret < 0)
+				warning((*p)->pos, "cannot parse format");
+			else if (ret > 0)
+				variadic_limit = fn->printf_va_start + ret;
+		}
+
+		if (i >= fn->printf_va_start && i <= variadic_limit)
+			target = variadic[i - fn->printf_va_start];
+		else
+			target = argtype;
 		if (!target) {
 			struct symbol *type;
 			int class = classify_type(ctype, &type);
@@ -2286,6 +2440,8 @@ static int evaluate_arguments(struct symbol *fn, struct expression_list *head)
 		NEXT_PTR_LIST(argtype);
 	} END_FOR_EACH_PTR(expr);
 	FINISH_PTR_LIST(argtype);
+
+	free(variadic);
 	return 1;
 }
 
