diff --git a/src/xpdev/GNUmakefile b/src/xpdev/GNUmakefile
index 019a20e01c8de066cb5619dd2e6097a374871ac8..e08ce2bbb7776b199b86bf9e673a8cfbc5a64b84 100644
--- a/src/xpdev/GNUmakefile
+++ b/src/xpdev/GNUmakefile
@@ -61,3 +61,6 @@ $(XPDEV-MT_SHLIB_BUILD): $(MTOBJS) | $(MTOBJODIR)
 	@echo Creating $@
 	$(QUIET)$(MKSHLIB) $(LDFLAGS) $(MTOBJS) $(SHLIBOPTS) -o $@
 
+xpprintf_test: xpprintf.c $(DEPS)
+	@echo Linking $@
+	$(QUIET)$(CC) $(CFLAGS) $(MT_CFLAGS) -DXP_PRINTF_TEST $< -o $@ $(LDFLAGS) $(MT_LDFLAGS) $(XPDEV-MT_LIB_BUILD) -lm $(XPDEV-MT_LIBS)
diff --git a/src/xpdev/xpprintf.c b/src/xpdev/xpprintf.c
index 22fb7e43a48d510c23e8fb29f9dc132699b695c8..e7fdca0d88148eeb7a4e785d63f2939b5f7903cf 100644
--- a/src/xpdev/xpprintf.c
+++ b/src/xpdev/xpprintf.c
@@ -68,11 +68,91 @@ int asprintf(char **strptr, const char *format, ...)
 /* Maximum length of a format specifier including the % */
 #define MAX_FORMAT_LEN	256
 
+struct arg_table_entry {
+	int type;
+	union {
+		int int_type;
+		unsigned uint_type;
+		long long_type;
+		unsigned long ulong_type;
+#if defined(XP_PRINTF_TYPE_LONGLONG)
+		long long longlong_type;
+#endif
+#if defined(XP_PRINTF_TYPE_ULONGLONG)
+		unsigned long long ulonglong_type;
+#endif
+		char *charp_type;
+		double double_type;
+		long double longdouble_type;
+		void *voidp_type;
+#if 0
+		intmax_t intmax_type;
+		uintmax_t uintmax_type;
+		ptrdiff_t ptrdiff_type;
+#endif
+		size_t size_type;
+	};
+};
+
 void xp_asprintf_free(char *format)
 {
 	free(format);
 }
 
+int xp_printf_get_next(char *format)
+{
+	char buf[6];
+	size_t bufpos = 0;
+	int j = 1;
+	long l;
+
+	if (!*(size_t *)format)
+		return (-1);
+	char *p = format + *(size_t *)format;
+	if (*p != '%')
+		return(-1);
+	p++;
+
+	while (j) {
+		switch(*p) {
+			case '0':
+				if (j > 1)
+					buf[bufpos++] = *p++;
+				else
+					j = 0;
+				break;
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				j = 2;
+				buf[bufpos++] = *p++;
+				if (bufpos == sizeof(buf))
+					return -1;
+				break;
+			case '$':
+				buf[bufpos++] = 0;
+				j = 0;
+				break;
+			default:
+				j = 0;
+				break;
+		}
+	}
+	if (*p == '$' && bufpos) {
+		l = strtol(buf, NULL, 10);
+		if (l > 0 && l < 100000)
+			return l;
+		return -1;
+	}
+	return 0;
+}
+
 int xp_printf_get_type(const char *format)
 {
 	const char	*p;
@@ -87,6 +167,41 @@ int xp_printf_get_type(const char *format)
 		return(0);
 	p++;
 
+	/*
+	 * Skip field specifier (zero or one)
+	 * Do not allow a leading zero
+	 */
+	j = 1;
+	while (j) {
+		switch(*p) {
+			case '0':
+				if (j > 1)
+					p++;
+				else
+					j = 0;
+				break;
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				p++;
+				j = 2;
+				break;
+			case '$':
+				p++;
+				j = 0;
+				break;
+			default:
+				j = 0;
+				break;
+		}
+	}
+
 	/*
 	 * Skip flags (zero or more)
 	 */
@@ -288,13 +403,38 @@ int xp_printf_get_type(const char *format)
 	return(correct_type);
 }
 
+/*
+ * Search for next non-%% separator and set offset
+ * to zero if none found for wrappers to know when
+ * they're done.
+ */
+static const char *
+find_next_replacement(char *format)
+{
+	/*
+	 * Find the next non %% format, leaving %% as it is
+	 */
+	char *p;
+	for (p = format + *(size_t *)format + 1; *p; p++) {
+		if (*p == '%') {
+			if (*(p + 1) == '%')
+				p++;
+			else
+				break;
+		}
+	}
+	if (!*p)
+		*(size_t *)format = 0;
+	else
+		*(size_t *)format = p - format;
+	return (format);
+}
+
 /*
  * Performs the next replacement in format using the variable
  * specified as the only vararg which is currently the type
  * specified in type (defined in xpprintf.h).
  *
- * Does not currently support the $ argument selector.
- *
  * Currently, the type is not overly useful, but this could be used for
  * automatic type conversions (ie: int to char *).  Right now it just assures
  * that the type passed to sprintf() is the type passed to
@@ -326,6 +466,7 @@ char* xp_asprintf_next(char *format, int type, ...)
 	char			*entry;
 	char			this_format[MAX_FORMAT_LEN];
 	char			*fmt;
+	char			*fmt_nofield;
 	int				modifier=0;
 	int				correct_type=0;
 	char			num_str[128];		/* More than enough room for a 256-bit int */
@@ -340,9 +481,46 @@ char* xp_asprintf_next(char *format, int type, ...)
 	offset=p-format;
 	format_len=*(size_t *)(format+sizeof(size_t))+sizeof(size_t)*2+1;
 	this_format[0]=0;
-	fmt=this_format;
+	fmt=fmt_nofield=this_format;
 	*(fmt++)=*(p++);
 
+	/*
+	 * Skip field specifier (zero or one)
+	 * Do not allow a leading zero
+	 */
+	j = 1;
+	while (j) {
+		switch(*p) {
+			case '0':
+				if (j > 1)
+					*(fmt++)=*(p++);
+				else
+					j = 0;
+				break;
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				*(fmt++)=*(p++);
+				j = 2;
+				break;
+			case '$':
+				fmt_nofield = fmt;
+				*(fmt++)='%';
+				p++;
+				j = 0;
+				break;
+			default:
+				j = 0;
+				break;
+		}
+	}
+
 	/*
 	 * Skip flags (zero or more)
 	 */
@@ -1185,42 +1363,42 @@ char* xp_asprintf_next(char *format, int type, ...)
 	switch(type) {
 		case XP_PRINTF_TYPE_CHAR:	/* Also includes char and short */
 		case XP_PRINTF_TYPE_INT:	/* Also includes char and short */
-			j=asprintf(&entry, this_format, i);
+			j=asprintf(&entry, fmt_nofield, i);
 			break;
 		case XP_PRINTF_TYPE_UINT:	/* Also includes char and short */
-			j=asprintf(&entry, this_format, ui);
+			j=asprintf(&entry, fmt_nofield, ui);
 			break;
 		case XP_PRINTF_TYPE_LONG:
-			j=asprintf(&entry, this_format, l);
+			j=asprintf(&entry, fmt_nofield, l);
 			break;
 		case XP_PRINTF_TYPE_ULONG:
-			j=asprintf(&entry, this_format, ul);
+			j=asprintf(&entry, fmt_nofield, ul);
 			break;
 #if defined(XP_PRINTF_TYPE_LONGLONG)
 		case XP_PRINTF_TYPE_LONGLONG:
-			j=asprintf(&entry, this_format, ll);
+			j=asprintf(&entry, fmt_nofield, ll);
 			break;
 		case XP_PRINTF_TYPE_ULONGLONG:
-			j=asprintf(&entry, this_format, ull);
+			j=asprintf(&entry, fmt_nofield, ull);
 			break;
 #endif
 		case XP_PRINTF_TYPE_CHARP:
 			if(cp==NULL)
-				j=asprintf(&entry, this_format, "<null>");
+				j=asprintf(&entry, fmt_nofield, "<null>");
 			else
-				j=asprintf(&entry, this_format, cp);
+				j=asprintf(&entry, fmt_nofield, cp);
 			break;
 		case XP_PRINTF_TYPE_DOUBLE:
-			j=asprintf(&entry, this_format, d);
+			j=asprintf(&entry, fmt_nofield, d);
 			break;
 		case XP_PRINTF_TYPE_LONGDOUBLE:
-			j=asprintf(&entry, this_format, ld);
+			j=asprintf(&entry, fmt_nofield, ld);
 			break;
 		case XP_PRINTF_TYPE_VOIDP:
-			j=asprintf(&entry, this_format, pntr);
+			j=asprintf(&entry, fmt_nofield, pntr);
 			break;
 		case XP_PRINTF_TYPE_SIZET:
-			j=asprintf(&entry, this_format, s);
+			j=asprintf(&entry, fmt_nofield, s);
 			break;
 		default:
 			j = -1;
@@ -1332,63 +1510,269 @@ char* xp_asprintf_end(char *format, size_t *lenret)
 	return(format);
 }
 
-char* xp_vasprintf(const char *format, va_list va)
+static struct arg_table_entry *
+build_arg_table(const char *format, va_list va)
 {
-	char	*working;
 	char	*next;
-	int		type;
+	va_list wva;
+	unsigned maxarg = 0;
+	static struct arg_table_entry *ret;
 
+	// First, count arguments...
 	next=xp_asprintf_start(format);
 	if(next==NULL)
 		return(NULL);
-	working=next;
-	while(*(size_t *)working) {
-		type=xp_printf_get_type(working);
-		switch(type) {
-			case 0:
-				free(working);
-				return(NULL);
+	int curpos = 0;
+	while(*(size_t *)next) {
+		int newpos = xp_printf_get_next(next);
+		if (newpos == -1) {
+			free(next);
+			return(NULL);
+		}
+		if (newpos > 0 && newpos != (curpos + 1))
+			curpos = newpos;
+		else
+			curpos++;
+		if (curpos > maxarg)
+			maxarg = curpos;
+		find_next_replacement(next);
+	}
+	free(next);
+
+	// Now, allocate the table...
+	ret = calloc(maxarg, sizeof(*ret));
+	if (ret == NULL)
+		return NULL;
+
+	// Now go through again and get the types
+	next=xp_asprintf_start(format);
+	if(next==NULL) {
+		free(ret);
+		return(NULL);
+	}
+	curpos = 0;
+	while(*(size_t *)next) {
+		int newpos = xp_printf_get_next(next);
+		if (newpos == -1) {
+			free(next);
+			free(ret);
+			return(NULL);
+		}
+		if (newpos > 0 && newpos != (curpos + 1))
+			curpos = newpos;
+		else
+			curpos++;
+		int type = xp_printf_get_type(next);
+		if (type == XP_PRINTF_TYPE_AUTO) {
+			free(next);
+			free(ret);
+			return NULL;
+		}
+		ret[curpos - 1].type = type;
+		find_next_replacement(next);
+	}
+	free(next);
+
+	// And finally, get all the values...
+	va_copy(wva, va);
+	for (int i = 0; i < maxarg; i++) {
+		switch(ret[i].type) {
 			case XP_PRINTF_TYPE_CHAR:
-			case XP_PRINTF_TYPE_INT:	/* Also includes char and short */
-				next=xp_asprintf_next(working, type, va_arg(va, int));
+			case XP_PRINTF_TYPE_INT:
+				ret[i].int_type = va_arg(wva, int);
 				break;
-			case XP_PRINTF_TYPE_UINT:	/* Also includes char and short */
-				next=xp_asprintf_next(working, type, va_arg(va, unsigned int));
+			case XP_PRINTF_TYPE_UINT:
+				ret[i].uint_type = va_arg(wva, unsigned);
 				break;
 			case XP_PRINTF_TYPE_LONG:
-				next=xp_asprintf_next(working, type, va_arg(va, long));
+				ret[i].long_type = va_arg(wva, long);
 				break;
 			case XP_PRINTF_TYPE_ULONG:
-				next=xp_asprintf_next(working, type, va_arg(va, unsigned long));
+				ret[i].ulong_type = va_arg(wva, unsigned long);
 				break;
-#if defined(XP_PRINTF_TYPE_LONGLONG)
+#ifdef XP_PRINTF_TYPE_LONGLONG
 			case XP_PRINTF_TYPE_LONGLONG:
-				next=xp_asprintf_next(working, type, va_arg(va, long long));
+				ret[i].longlong_type = va_arg(wva, long long);
 				break;
+#endif
+#ifdef XP_PRINTF_TYPE_ULONGLONG
 			case XP_PRINTF_TYPE_ULONGLONG:
-				next=xp_asprintf_next(working, type, va_arg(va, unsigned long long));
+				ret[i].ulonglong_type = va_arg(wva, unsigned long long);
 				break;
 #endif
 			case XP_PRINTF_TYPE_CHARP:
-				next=xp_asprintf_next(working, type, va_arg(va, char *));
+				ret[i].charp_type = va_arg(wva, char *);
 				break;
 			case XP_PRINTF_TYPE_DOUBLE:
-				next=xp_asprintf_next(working, type, va_arg(va, double));
+				ret[i].double_type = va_arg(wva, double);
 				break;
 			case XP_PRINTF_TYPE_LONGDOUBLE:
-				next=xp_asprintf_next(working, type, va_arg(va, long double));
+				ret[i].longdouble_type = va_arg(wva, long double);
 				break;
 			case XP_PRINTF_TYPE_VOIDP:
-				next=xp_asprintf_next(working, type, va_arg(va, void *));
+				ret[i].voidp_type = va_arg(wva, void *);
 				break;
 			case XP_PRINTF_TYPE_SIZET:
-				next=xp_asprintf_next(working, type, va_arg(va, size_t));
+				ret[i].size_type = va_arg(wva, size_t);
 				break;
+			default:
+				va_end(wva);
+				free(ret);
+				return NULL;
 		}
-		if(next==NULL)
+	}
+	va_end(wva);
+	return ret;
+}
+
+char* xp_vasprintf(const char *format, va_list va)
+{
+	char	*working;
+	char	*next;
+	int		type;
+	int curpos;
+	int newpos;
+	va_list wva;
+	struct arg_table_entry *atable = NULL;
+
+	next=xp_asprintf_start(format);
+	if(next==NULL)
+		return(NULL);
+	working=next;
+	va_copy(wva, va);
+	curpos = 1;
+	while(*(size_t *)working) {
+		newpos = xp_printf_get_next(working);
+		if (atable == NULL) {
+			if (newpos == -1) {
+				va_end(wva);
+				free(working);
+				return(NULL);
+			}
+			if (newpos > 0 && newpos != curpos) {
+				va_end(wva);
+				va_copy(wva, va);
+				atable = build_arg_table(format, va);
+				if (atable == NULL) {
+					free(working);
+					return NULL;
+				}
+				continue;
+			}
+			else
+				curpos++;
+			type=xp_printf_get_type(working);
+			switch(type) {
+				case 0:
+					va_end(wva);
+					free(working);
+					return(NULL);
+				case XP_PRINTF_TYPE_CHAR:
+				case XP_PRINTF_TYPE_INT:	/* Also includes char and short */
+					next=xp_asprintf_next(working, type, va_arg(wva, int));
+					break;
+				case XP_PRINTF_TYPE_UINT:	/* Also includes char and short */
+					next=xp_asprintf_next(working, type, va_arg(wva, unsigned int));
+					break;
+				case XP_PRINTF_TYPE_LONG:
+					next=xp_asprintf_next(working, type, va_arg(wva, long));
+					break;
+				case XP_PRINTF_TYPE_ULONG:
+					next=xp_asprintf_next(working, type, va_arg(wva, unsigned long));
+					break;
+	#if defined(XP_PRINTF_TYPE_LONGLONG)
+				case XP_PRINTF_TYPE_LONGLONG:
+					next=xp_asprintf_next(working, type, va_arg(wva, long long));
+					break;
+				case XP_PRINTF_TYPE_ULONGLONG:
+					next=xp_asprintf_next(working, type, va_arg(wva, unsigned long long));
+					break;
+	#endif
+				case XP_PRINTF_TYPE_CHARP:
+					next=xp_asprintf_next(working, type, va_arg(wva, char *));
+					break;
+				case XP_PRINTF_TYPE_DOUBLE:
+					next=xp_asprintf_next(working, type, va_arg(wva, double));
+					break;
+				case XP_PRINTF_TYPE_LONGDOUBLE:
+					next=xp_asprintf_next(working, type, va_arg(wva, long double));
+					break;
+				case XP_PRINTF_TYPE_VOIDP:
+					next=xp_asprintf_next(working, type, va_arg(wva, void *));
+					break;
+				case XP_PRINTF_TYPE_SIZET:
+					next=xp_asprintf_next(working, type, va_arg(wva, size_t));
+					break;
+			}
+		}
+		else {
+			if (newpos == -1) {
+				free(atable);
+				free(working);
+				return(NULL);
+			}
+			if (newpos > 0 && newpos != curpos)
+				curpos = newpos;
+			else
+				curpos++;
+			// Use atable
+			type = atable[curpos - 1].type;
+			switch(type) {
+				case 0:
+					free(atable);
+					free(working);
+					return(NULL);
+				case XP_PRINTF_TYPE_CHAR:
+				case XP_PRINTF_TYPE_INT:	/* Also includes char and short */
+					next=xp_asprintf_next(working, type, atable[curpos - 1].int_type);
+					break;
+				case XP_PRINTF_TYPE_UINT:	/* Also includes char and short */
+					next=xp_asprintf_next(working, type, atable[curpos - 1].uint_type);
+					break;
+				case XP_PRINTF_TYPE_LONG:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].long_type);
+					break;
+				case XP_PRINTF_TYPE_ULONG:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].ulong_type);
+					break;
+#if defined(XP_PRINTF_TYPE_LONGLONG)
+				case XP_PRINTF_TYPE_LONGLONG:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].longlong_type);
+					break;
+#endif
+#if defined(XP_PRINTF_TYPE_ULONGLONG)
+				case XP_PRINTF_TYPE_ULONGLONG:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].ulonglong_type);
+					break;
+#endif
+				case XP_PRINTF_TYPE_CHARP:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].charp_type);
+					break;
+				case XP_PRINTF_TYPE_DOUBLE:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].double_type);
+					break;
+				case XP_PRINTF_TYPE_LONGDOUBLE:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].longdouble_type);
+					break;
+				case XP_PRINTF_TYPE_VOIDP:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].voidp_type);
+					break;
+				case XP_PRINTF_TYPE_SIZET:
+					next=xp_asprintf_next(working, type, atable[curpos - 1].size_type);
+					break;
+			}
+		}
+		if(next==NULL) {
+			if (atable == NULL)
+				va_end(wva);
+			free(atable);
 			return(NULL);
+		}
 		working=next;
 	}
+	if (atable == NULL)
+		va_end(wva);
+	free(atable);
 	next=xp_asprintf_end(working, NULL);
 	if(next==NULL) {
 		free(working);
@@ -1427,6 +1811,11 @@ int main(int argc, char *argv[])
 	p=xp_asprintf("%%%%%*.*f %% %%%ss %cs %*.*lu",3,3,123.123456789,"%llutesting%",32,3,3,123);
 	printf("%s\n",p);
 	free(p);
+
+	p=xp_asprintf("%3$d %1$d %d",2, 3, 1);
+	printf("%s\n",p);
+	free(p);
+
 	if(argc < 2)
 		return(1);
 
diff --git a/src/xpdev/xpprintf.h b/src/xpdev/xpprintf.h
index 107c7990ed6941f7de7b38b54b5195fa74cc9ed8..ed9114ddc8781e5a2142cee461a723d31e315dfd 100644
--- a/src/xpdev/xpprintf.h
+++ b/src/xpdev/xpprintf.h
@@ -50,7 +50,7 @@
 #define XP_PRINTF_TYPE_PTRDIFF		14	/* Not currently implemented */
 #define XP_PRINTF_TYPE_SIZET		15
 
-#define XP_PRINTF_CONVERT		(1<<31)	/* OR with type to request a conversion - Not implemented */
+#define XP_PRINTF_CONVERT		(1<<31)	/* OR with type to request a conversion to format-specified type */
 
 #if defined(__cplusplus)
 extern "C" {