Index: /trunk/src/kmk/Makefile.kmk
===================================================================
--- /trunk/src/kmk/Makefile.kmk	(revision 1291)
+++ /trunk/src/kmk/Makefile.kmk	(revision 1292)
@@ -175,4 +175,5 @@
 	kmkbuiltin/rm.c \
 	kmkbuiltin/rmdir.c \
+	kmkbuiltin/test.c \
 
 ## Some profiling
@@ -199,4 +200,5 @@
 	kmk_rm \
 	kmk_rmdir \
+	kmk_test \
 	kDepIDB \
 
@@ -268,4 +270,9 @@
 kmk_rmdir_SOURCES = \
 	kmkbuiltin/rmdir.c
+
+kmk_test_TEMPLATE = BIN-KMK
+kmk_test_DEFS = kmk_builtin_test=main
+kmk_test_SOURCES = \
+	kmkbuiltin/test.c
 
 kDepIDB_TEMPLATE = BIN-KMK
Index: /trunk/src/kmk/kmkbuiltin.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin.c	(revision 1291)
+++ /trunk/src/kmk/kmkbuiltin.c	(revision 1292)
@@ -205,6 +205,6 @@
     else if (!strcmp(pszCmd, "rmdir"))
         rc = kmk_builtin_rmdir(argc, argv, environ);
-    /*else if (!strcmp(pszCmd, "test"))
-        rc = kmk_builtin_test(argc, argv, environ, ppapszArgvToSpawn);*/
+    else if (!strcmp(pszCmd, "test"))
+        rc = kmk_builtin_test(argc, argv, environ, ppapszArgvToSpawn);
     /* rarely used commands: */
     else if (!strcmp(pszCmd, "cp"))
Index: /trunk/src/kmk/kmkbuiltin.h
===================================================================
--- /trunk/src/kmk/kmkbuiltin.h	(revision 1291)
+++ /trunk/src/kmk/kmkbuiltin.h	(revision 1292)
@@ -49,4 +49,5 @@
 extern int kmk_builtin_rm(int argc, char **argv, char **envp);
 extern int kmk_builtin_rmdir(int argc, char **argv, char **envp);
+extern int kmk_builtin_test(int argc, char **argv, char **envp, char ***ppapszArgvSpawn);
 extern int kmk_builtin_kDepIDB(int argc, char **argv, char **envp);
 
Index: /trunk/src/kmk/kmkbuiltin/mscfakes.h
===================================================================
--- /trunk/src/kmk/kmkbuiltin/mscfakes.h	(revision 1291)
+++ /trunk/src/kmk/kmkbuiltin/mscfakes.h	(revision 1292)
@@ -110,4 +110,5 @@
 #define fchmod(fd, mode) 0              /** @todo implement fchmod! */
 #define geteuid()  0
+#define getegid()  0
 #define lstat(path, s) stat(path, s)
 #define lchmod(path, mod) chmod(path, mod)
Index: /trunk/src/kmk/kmkbuiltin/test.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin/test.c	(revision 1291)
+++ /trunk/src/kmk/kmkbuiltin/test.c	(revision 1292)
@@ -11,8 +11,8 @@
  */
 
-#include <sys/cdefs.h>
+/*#include <sys/cdefs.h>
 #ifndef lint
 __RCSID("$NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $");
-#endif
+#endif*/
 
 #include <sys/stat.h>
@@ -20,5 +20,5 @@
 
 #include <ctype.h>
-#include <err.h>
+#include "err.h"
 #include <errno.h>
 #include <limits.h>
@@ -26,6 +26,21 @@
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <io.h>
+# include <process.h>
+# include "mscfakes.h"
+#else
+# include <unistd.h>
+#endif
 #include <stdarg.h>
+#include <sys/stat.h>
+
+#include "kmkbuiltin.h"
+
+#ifndef __arraycount
+# define __arraycount(a) 	( sizeof(a) / sizeof(a[0]) )
+#endif
+
 
 /* test(1) accepts the following grammar:
@@ -156,5 +171,5 @@
 static struct t_op const *t_wp_op;
 
-static void syntax(const char *, const char *);
+static int syntax(const char *, const char *);
 static int oexpr(enum token);
 static int aexpr(enum token);
@@ -170,81 +185,123 @@
 static int olderf(const char *, const char *);
 static int equalf(const char *, const char *);
-
-#if defined(SHELL)
-extern void error(const char *, ...) __attribute__((__noreturn__));
-extern void *ckmalloc(size_t);
-#else
-static void error(const char *, ...) __attribute__((__noreturn__));
-
-static void
-error(const char *msg, ...)
-{
-	va_list ap;
-
-	va_start(ap, msg);
-	verrx(2, msg, ap);
-	/*NOTREACHED*/
-	va_end(ap);
-}
-
-static void *ckmalloc(size_t);
-static void *
-ckmalloc(size_t nbytes)
-{
-	void *p = malloc(nbytes);
-
-	if (!p)
-		error("Not enough memory!");
-	return p;
-}
-#endif
-
-#ifdef SHELL
-int testcmd(int, char **);
-
-int
-testcmd(int argc, char **argv)
-#else
-int main(int, char *[]);
-
-int
-main(int argc, char *argv[])
+static int usage(const char *);
+
+#ifdef kmk_builtin_test
+extern void *xmalloc(unsigned int);
+#else
+static void *xmalloc(unsigned int sz)
+{
+    void *p = malloc(sz);
+    if (!p) {
+	    fprintf(stderr, "%s: malloc(%u) failed\n", g_progname, sz);
+	    exit(1);
+    }
+    return p;
+}
+#endif
+
+#ifdef kmk_builtin_test
+int kmk_builtin_test(int argc, char **argv)
+#else
+int kmk_builtin_test(int argc, char **argv, char **envp, char ***ppapszArgvSpawn)
 #endif
 {
 	int res;
-	const char *argv0;
-
-#ifdef SHELL
-	argv0 = argv[0];
-#else
-	setprogname(argv[0]);
-	argv0 = getprogname();
-#endif
-	if (strcmp(argv0, "[") == 0) {
+	char **argv_spawn;
+	int i;
+
+	g_progname = argv[0];
+
+	/* look for the '--', '--help' and '--version'. */
+	argv_spawn = NULL;
+	for (i = 1; i < argc; i++) {
+		if (   argv[i][0] == '-'
+		    && argv[i][1] == '-') {
+    			if (argv[i][2] == '\0') {
+				argc = i;
+				argv[i] = NULL;
+				argv_spawn = &argv[i + 1];
+				break;
+			}
+			if (!strcmp(argv[i], "--help"))
+				return usage(argv[0]);
+			if (!strcmp(argv[i], "--version"))
+				return kbuild_version(argv[0]);
+		}
+	}
+
+	/* are we '['? then check for ']'. */
+	if (strcmp(g_progname, "[") == 0) { /** @todo should skip the path in g_progname */
 		if (strcmp(argv[--argc], "]"))
-			error("missing ]");
+			return errx(1, "missing ]");
 		argv[argc] = NULL;
 	}
 
+	/* evaluate the expression */
 	if (argc < 2)
-		return 1;
-
-	t_wp = &argv[1];
-	res = !oexpr(t_lex(*t_wp));
-
-	if (*t_wp != NULL && *++t_wp != NULL)
-		syntax(*t_wp, "unexpected operator");
+		res = 1;
+	else {
+		t_wp = &argv[1];
+		res = oexpr(t_lex(*t_wp));
+		if (res != -42 && *t_wp != NULL && *++t_wp != NULL)
+			res = syntax(*t_wp, "unexpected operator");
+		if (res == -42)
+			return 1; /* don't mix syntax errors with the argv_spawn ignore */
+		res = !res;
+	}
+
+	/* anything to execute on success? */
+	if (argv_spawn) {
+		if (res != 0 || !argv_spawn[0])
+			res = 0; /* ignored */
+		else {
+#ifdef kmk_builtin_test
+			/* try exec the specified process */
+# if defined(_MSC_VER)
+			res = _spawnvp(_P_WAIT, argv_spawn[0], argv_spawn);
+			if (res == -1)
+			    res = err(1, "_spawnvp(_P_WAIT,%s,..)", argv_spawn[i]);
+# else
+			execvp(argv_spawn[i], &argv_spawn[i]);
+			res = err(1, "execvp(%s,..)", argv_spawn[i]);
+# endif
+#else /* in kmk */
+			/* let job.c spawn the process, make a job.c style argv_spawn copy. */
+			char *buf, *cur, **argv_new;
+			size_t sz = 0;
+			int argc_new = 0;
+			while (argv_spawn[argc_new]) {
+    				size_t len = strlen(argv_spawn[argc_new]) + 1;
+				sz += (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
+				argc_new++;
+			}
+
+			argv_new = xmalloc((argc_new + 1) * sizeof(char *));
+			buf = cur = xmalloc(argc_new * sizeof(char *));
+			for (i = 0; i < argc_new; i++) {
+				size_t len = strlen(argv_spawn[i]) + 1;
+				argv_new[i] = memcpy(cur, argv_spawn[i], len);
+				cur += (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
+			}
+			argv_new[i] = NULL;
+
+			*ppapszArgvSpawn = argv_new;
+			res = 0;
+#endif /* in kmk */
+		}
+	}
 
 	return res;
 }
 
-static void
+static int
 syntax(const char *op, const char *msg)
 {
 
 	if (op && *op)
-		error("%s: %s", op, msg);
+		errx(1, "%s: %s", op, msg);
 	else
-		error("%s", msg);
+		errx(1, "%s", msg);
+	return -42;
 }
 
@@ -255,8 +312,10 @@
 
 	res = aexpr(n);
-	if (*t_wp == NULL)
+	if (res == -42 || *t_wp == NULL)
 		return res;
-	if (t_lex(*++t_wp) == BOR)
-		return oexpr(t_lex(*++t_wp)) || res;
+	if (t_lex(*++t_wp) == BOR) {
+		int res2 = oexpr(t_lex(*++t_wp));
+		return res2 != -42 ? res2 || res : res2;
+	}
 	t_wp--;
 	return res;
@@ -269,8 +328,10 @@
 
 	res = nexpr(n);
-	if (*t_wp == NULL)
+	if (res == -42 || *t_wp == NULL)
 		return res;
-	if (t_lex(*++t_wp) == BAND)
-		return aexpr(t_lex(*++t_wp)) && res;
+	if (t_lex(*++t_wp) == BAND) {
+		int res2 = aexpr(t_lex(*++t_wp));
+		return res2 != -42 ? res2 && res : res2;
+	}
 	t_wp--;
 	return res;
@@ -280,7 +341,8 @@
 nexpr(enum token n)
 {
-
-	if (n == UNOT)
-		return !nexpr(t_lex(*++t_wp));
+	if (n == UNOT) {
+		int res = nexpr(t_lex(*++t_wp));
+		return res != -42 ? !res : res;
+	}
 	return primary(n);
 }
@@ -298,6 +360,6 @@
 			return 0;	/* missing expression */
 		res = oexpr(nn);
-		if (t_lex(*++t_wp) != RPAREN)
-			syntax(NULL, "closing paren expected");
+		if (res != -42 && t_lex(*++t_wp) != RPAREN)
+			return syntax(NULL, "closing paren expected");
 		return res;
 	}
@@ -305,5 +367,5 @@
 		/* unary expression */
 		if (*++t_wp == NULL)
-			syntax(t_wp_op->op_text, "argument expected");
+			return syntax(t_wp_op->op_text, "argument expected");
 		switch (n) {
 		case STREZ:
@@ -320,5 +382,5 @@
 	if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
 		return binop();
-	}	  
+	}
 
 	return strlen(*t_wp) > 0;
@@ -336,6 +398,6 @@
 
 	if ((opnd2 = *++t_wp) == NULL)
-		syntax(op->op_text, "argument expected");
-		
+		return syntax(op->op_text, "argument expected");
+
 	switch (op->op_num) {
 	case STREQ:
@@ -368,4 +430,7 @@
 		abort();
 		/* NOTREACHED */
+#ifdef _MSC_VER
+		return -42;
+#endif
 	}
 }
@@ -449,19 +514,19 @@
  * can never fail for root for any existing files, i.e. files for which 'test
  * -e' succeeds.)
- * 
+ *
  * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
  * 1003.1-2001 to align with the 1003.2b draft.  1003.2b Draft 12 (July 1999),
  * which is the latest copy I have, does carry the same suggested wording as is
  * in 1003.1-2001, with its rationale saying:
- * 
+ *
  * 	This change is a clarification and is the result of interpretation
  * 	request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
- * 
+ *
  * That interpretation can be found here:
- * 
+ *
  *   http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
- * 
+ *
  * Not terribly helpful, unfortunately.  I wonder who that fence sitter was.
- * 
+ *
  * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
  * PASC interpretation and appear to be gone against at least one widely used
@@ -473,5 +538,5 @@
  * "test" and is, I believe, one of the reasons why direct use of access() was
  * avoided in some more recent implementations!
- * 
+ *
  * I have always interpreted '-r' to match '-w' and '-x' as per the original
  * wording in 1003.2-1992, not the other way around.  I think 1003.2b goes much
@@ -480,8 +545,8 @@
  * open() since we already know very well how it will work -- existance of the
  * file is all that matters to open() for root.
- * 
+ *
  * Unfortunately the SVID is no help at all (which is, I guess, partly why
  * we're in this mess in the first place :-).
- * 
+ *
  * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
  * access(name, 2) even though it also goes to much greater lengths for '-x'
@@ -496,5 +561,10 @@
 test_access(struct stat *sp, mode_t stmode)
 {
-	gid_t *groups; 
+#ifdef _MSC_VER
+	/* just pretend to be root for now. */
+	stmode = (stmode << 6) | (stmode << 3) | stmode;
+	return !!(sp->st_mode & stmode);
+#else
+	gid_t *groups;
 	register int n;
 	uid_t euid;
@@ -519,5 +589,5 @@
 		if ((maxgroups = getgroups(0, NULL)) <= 0)
 			maxgroups = NGROUPS_MAX;	/* pre-POSIX system? */
-		groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
+		groups = xmalloc((maxgroups + 1) * sizeof(gid_t));
 		n = getgroups(maxgroups, groups);
 		while (--n >= 0) {
@@ -530,5 +600,6 @@
 	}
 
-	return sp->st_mode & stmode;
+	return !!(sp->st_mode & stmode);
+#endif
 }
 
@@ -555,13 +626,33 @@
 		return S_ISDIR(s.st_mode);
 	case FILCDEV:
+#ifdef S_ISCHR
 		return S_ISCHR(s.st_mode);
+#else
+		return 0;
+#endif
 	case FILBDEV:
+#ifdef S_ISBLK
 		return S_ISBLK(s.st_mode);
+#else
+		return 0;
+#endif
 	case FILFIFO:
+#ifdef S_ISFIFO
 		return S_ISFIFO(s.st_mode);
+#else
+		return 0;
+#endif
 	case FILSOCK:
+#ifdef S_ISSOCK
 		return S_ISSOCK(s.st_mode);
+#else
+		return 0;
+#endif
 	case FILSYM:
+#ifdef S_ISLNK
 		return S_ISLNK(s.st_mode);
+#else
+		return 0;
+#endif
 	case FILSUID:
 		return (s.st_mode & S_ISUID) != 0;
@@ -569,5 +660,9 @@
 		return (s.st_mode & S_ISGID) != 0;
 	case FILSTCK:
+#ifdef S_ISVTX
 		return (s.st_mode & S_ISVTX) != 0;
+#else
+		return 0;
+#endif
 	case FILGZ:
 		return s.st_size > (off_t)0;
@@ -659,5 +754,5 @@
 		return 0;
 	if ((op = findop(s)) != NULL)
-		return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0'); 
+		return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
 	return 0;
 }
@@ -674,11 +769,11 @@
 
 	if (errno != 0)
-	      error("%s: out of range", s);
+	      return errx(-42, "%s: out of range", s);
 
 	while (isspace((unsigned char)*p))
 	      p++;
-	
+
 	if (*p)
-	      error("%s: bad number", s);
+	      return errx(-42, "%s: bad number", s);
 
 	return (int) r;
@@ -715,2 +810,10 @@
 		b1.st_ino == b2.st_ino);
 }
+
+static int
+usage(const char *argv0)
+{
+	fprintf(stdout,
+	        "usage: %s expression [-- <prog> [args]]\n", argv0);
+	return 0; /* only used in --help. */
+}
Index: /trunk/src/kmk/variable.c
===================================================================
--- /trunk/src/kmk/variable.c	(revision 1291)
+++ /trunk/src/kmk/variable.c	(revision 1292)
@@ -1110,5 +1110,5 @@
 #ifdef CONFIG_WITH_KMK_BUILTIN
   /* The supported kMk Builtin commands. */
-  (void) define_variable ("KMK_BUILTIN", 11, "append cat cp cmp echo install kDepIDB ln md5sum mkdir mv printf rm rmdir", o_default, 0);
+  (void) define_variable ("KMK_BUILTIN", 11, "append cat cp cmp echo install kDepIDB ln md5sum mkdir mv printf rm rmdir test", o_default, 0);
 #endif
 
