Index: /trunk/src/kmk/Makefile.am
===================================================================
--- /trunk/src/kmk/Makefile.am	(revision 1706)
+++ /trunk/src/kmk/Makefile.am	(revision 1707)
@@ -61,4 +61,5 @@
 		kmkbuiltin/cp_utils.c \
 		kmkbuiltin/echo.c \
+		kmkbuiltin/expr.c \
 		kmkbuiltin/install.c \
 		kmkbuiltin/kDepIDB.c \
Index: /trunk/src/kmk/Makefile.kmk
===================================================================
--- /trunk/src/kmk/Makefile.kmk	(revision 1706)
+++ /trunk/src/kmk/Makefile.kmk	(revision 1707)
@@ -213,4 +213,5 @@
 	kmkbuiltin/cp_utils.c \
 	kmkbuiltin/echo.c \
+	kmkbuiltin/expr.c \
 	kmkbuiltin/install.c \
 	kmkbuiltin/kDepIDB.c \
@@ -242,4 +243,5 @@
 	kmk_cmp \
 	kmk_echo \
+	kmk_expr \
 	kmk_md5sum \
 	kmk_mkdir \
@@ -287,4 +289,9 @@
 kmk_echo_SOURCES = \
 	kmkbuiltin/echo.c
+
+kmk_expr_TEMPLATE = BIN-KMK
+kmk_expr_DEFS = kmk_builtin_expr=main
+kmk_expr_SOURCES = \
+	kmkbuiltin/expr.c
 
 kmk_install_TEMPLATE = BIN-KMK
Index: /trunk/src/kmk/kmkbuiltin.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin.c	(revision 1706)
+++ /trunk/src/kmk/kmkbuiltin.c	(revision 1707)
@@ -212,4 +212,6 @@
     else if (!strcmp(pszCmd, "cp"))
         rc = kmk_builtin_cp(argc, argv, environ);
+    else if (!strcmp(pszCmd, "expr"))
+        rc = kmk_builtin_expr(argc, argv, environ);
     else if (!strcmp(pszCmd, "ln"))
         rc = kmk_builtin_ln(argc, argv, environ);
Index: /trunk/src/kmk/kmkbuiltin.h
===================================================================
--- /trunk/src/kmk/kmkbuiltin.h	(revision 1706)
+++ /trunk/src/kmk/kmkbuiltin.h	(revision 1707)
@@ -42,4 +42,5 @@
 extern int kmk_builtin_cmp(int argc, char **argv, char **envp);
 extern int kmk_builtin_echo(int argc, char **argv, char **envp);
+extern int kmk_builtin_expr(int argc, char **argv, char **envp);
 extern int kmk_builtin_install(int argc, char **argv, char **envp);
 extern int kmk_builtin_ln(int argc, char **argv, char **envp);
Index: /trunk/src/kmk/kmkbuiltin/expr.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin/expr.c	(revision 1706)
+++ /trunk/src/kmk/kmkbuiltin/expr.c	(revision 1707)
@@ -12,23 +12,29 @@
 #include <locale.h>
 #include <ctype.h>
+#ifdef KMK_WITH_REGEX
 #include <regex.h>
-#include <err.h>
-
-struct val	*make_int(int);
-struct val	*make_str(char *);
-void		 free_value(struct val *);
-int		 is_integer(struct val *, int *);
-int		 to_integer(struct val *);
-void		 to_string(struct val *);
-int		 is_zero_or_null(struct val *);
-void		 nexttoken(int);
-__dead void	 error(void);
-struct val	*eval6(void);
-struct val	*eval5(void);
-struct val	*eval4(void);
-struct val	*eval3(void);
-struct val	*eval2(void);
-struct val	*eval1(void);
-struct val	*eval0(void);
+#endif 
+#include <setjmp.h>
+#include <assert.h>
+#include "err.h"
+#include "getopt.h"
+#include "kmkbuiltin.h"
+
+static struct val	*make_int(int);
+static struct val	*make_str(char *);
+static void		 free_value(struct val *);
+static int		 is_integer(struct val *, int *);
+static int		 to_integer(struct val *);
+static void		 to_string(struct val *);
+static int		 is_zero_or_null(struct val *);
+static void		 nexttoken(int);
+static __dead void	 error(void);
+static struct val	*eval6(void);
+static struct val	*eval5(void);
+static struct val	*eval4(void);
+static struct val	*eval3(void);
+static struct val	*eval2(void);
+static struct val	*eval1(void);
+static struct val	*eval0(void);
 
 enum token {
@@ -49,9 +55,54 @@
 };
 
-enum token	token;
-struct val     *tokval;
-char	      **av;
-
-struct val *
+static enum token	token;
+static struct val     *tokval;
+static char	      **av;
+static jmp_buf          g_expr_jmp;
+static void           **recorded_allocations;
+static int 		num_recorded_allocations;
+
+
+static void expr_mem_record_alloc(void *ptr)
+{
+	if (!(num_recorded_allocations & 31)) {
+    		void *newtab = realloc(recorded_allocations, (num_recorded_allocations + 33) * sizeof(void *));
+		if (!newtab)
+			longjmp(g_expr_jmp, err(3, NULL));
+		recorded_allocations = (void **)newtab;
+	}
+	recorded_allocations[num_recorded_allocations++] = ptr;
+}
+
+
+static void expr_mem_record_free(void *ptr)
+{
+	int i = num_recorded_allocations;
+	while (i-- > 0)
+		if (recorded_allocations[i] == ptr) {
+			num_recorded_allocations--;
+			recorded_allocations[i] = recorded_allocations[num_recorded_allocations];
+			return;
+		}
+	assert(i >= 0);
+}
+
+static void expr_mem_init(void)
+{
+	num_recorded_allocations = 0;
+	recorded_allocations = NULL;
+}
+
+static void expr_mem_cleanup(void)
+{
+	if (recorded_allocations) {
+		while (num_recorded_allocations-- > 0)
+			free(recorded_allocations[num_recorded_allocations]);
+		free(recorded_allocations);
+		recorded_allocations = NULL;
+	}
+}
+
+
+static struct val *
 make_int(int i)
 {
@@ -59,7 +110,7 @@
 
 	vp = (struct val *) malloc(sizeof(*vp));
-	if (vp == NULL) {
-		err(3, NULL);
-	}
+	if (vp == NULL)
+		longjmp(g_expr_jmp, err(3, NULL));
+	expr_mem_record_alloc(vp);
 	vp->type = integer;
 	vp->u.i = i;
@@ -68,5 +119,5 @@
 
 
-struct val *
+static struct val *
 make_str(char *s)
 {
@@ -74,7 +125,8 @@
 
 	vp = (struct val *) malloc(sizeof(*vp));
-	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) {
-		err(3, NULL);
-	}
+	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL))
+		longjmp(g_expr_jmp, err(3, NULL));
+	expr_mem_record_alloc(vp->u.s);
+	expr_mem_record_alloc(vp);
 	vp->type = string;
 	return vp;
@@ -82,15 +134,18 @@
 
 
-void
+static void
 free_value(struct val *vp)
 {
-	if (vp->type == string)
+	if (vp->type == string) {
+		expr_mem_record_free(vp->u.s);
 		free(vp->u.s);
+	}
 	free(vp);
+	expr_mem_record_free(vp);
 }
 
 
 /* determine if vp is an integer; if so, return it's value in *r */
-int
+static int
 is_integer(struct val *vp, int *r)
 {
@@ -134,5 +189,5 @@
 
 /* coerce to vp to an integer */
-int
+static int
 to_integer(struct val *vp)
 {
@@ -143,4 +198,5 @@
 
 	if (is_integer(vp, &r)) {
+		expr_mem_record_free(vp->u.s);
 		free(vp->u.s);
 		vp->u.i = r;
@@ -154,5 +210,5 @@
 
 /* coerce to vp to an string */
-void
+static void
 to_string(struct val *vp)
 {
@@ -163,5 +219,6 @@
 
 	if (asprintf(&tmp, "%d", vp->u.i) == -1)
-		err(3, NULL);
+		longjmp(g_expr_jmp, err(3, NULL));
+	expr_mem_record_alloc(tmp);
 
 	vp->type = string;
@@ -169,5 +226,5 @@
 }
 
-int
+static int
 is_zero_or_null(struct val *vp)
 {
@@ -180,5 +237,5 @@
 }
 
-void
+static void
 nexttoken(int pat)
 {
@@ -220,12 +277,12 @@
 }
 
-__dead void
+static void
 error(void)
 {
-	errx(2, "syntax error");
+	longjmp(g_expr_jmp, errx(2, "syntax error"));
 	/* NOTREACHED */
 }
 
-struct val *
+static struct val *
 eval6(void)
 {
@@ -253,7 +310,8 @@
 
 /* Parse and evaluate match (regex) expressions */
-struct val *
+static struct val *
 eval5(void)
 {
+#ifdef KMK_WITH_REGEX
 	regex_t		rp;
 	regmatch_t	rm[2];
@@ -275,5 +333,5 @@
 		if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
 			regerror(eval, &rp, errbuf, sizeof(errbuf));
-			errx(2, "%s", errbuf);
+			longjmp(g_expr_jmp, errx(2, "%s", errbuf));
 		}
 
@@ -305,8 +363,11 @@
 
 	return l;
+#else
+	longjmp(g_expr_jmp, errx(2, "regex not supported, sorry."));
+#endif 
 }
 
 /* Parse and evaluate multiplication and division expressions */
-struct val *
+static struct val *
 eval4(void)
 {
@@ -320,5 +381,5 @@
 
 		if (!to_integer(l) || !to_integer(r)) {
-			errx(2, "non-numeric argument");
+			longjmp(g_expr_jmp, errx(2, "non-numeric argument"));
 		}
 
@@ -327,5 +388,5 @@
 		} else {
 			if (r->u.i == 0) {
-				errx(2, "division by zero");
+				longjmp(g_expr_jmp, errx(2, "division by zero"));
 			}
 			if (op == DIV) {
@@ -343,5 +404,5 @@
 
 /* Parse and evaluate addition and subtraction expressions */
-struct val *
+static struct val *
 eval3(void)
 {
@@ -355,5 +416,5 @@
 
 		if (!to_integer(l) || !to_integer(r)) {
-			errx(2, "non-numeric argument");
+			longjmp(g_expr_jmp, errx(2, "non-numeric argument"));
 		}
 
@@ -371,5 +432,5 @@
 
 /* Parse and evaluate comparison expressions */
-struct val *
+static struct val *
 eval2(void)
 {
@@ -444,5 +505,5 @@
 
 /* Parse and evaluate & expressions */
-struct val *
+static struct val *
 eval1(void)
 {
@@ -467,5 +528,5 @@
 
 /* Parse and evaluate | expressions */
-struct val *
+static struct val *
 eval0(void)
 {
@@ -490,9 +551,18 @@
 
 int
-main(int argc, char *argv[])
+kmk_builtin_expr(int argc, char *argv[], char **envp)
 {
 	struct val     *vp;
-
+	int rval;
+
+	/* re-init globals */
+	token = 0;
+	tokval = 0;
+	av = 0;
+	expr_mem_init();
+
+#ifdef kmk_builtin_expr /* kmk already does this. */
 	(void) setlocale(LC_ALL, "");
+#endif
 
 	if (argc > 1 && !strcmp(argv[1], "--"))
@@ -501,17 +571,25 @@
 	av = argv + 1;
 
-	nexttoken(0);
-	vp = eval0();
-
-	if (token != EOI) {
-		error();
-		/* NOTREACHED */
-	}
-
-	if (vp->type == integer)
-		printf("%d\n", vp->u.i);
-	else
-		printf("%s\n", vp->u.s);
-
-	exit(is_zero_or_null(vp));
-}
+	rval = setjmp(g_expr_jmp);
+	if (!rval) {
+		nexttoken(0);
+		vp = eval0();
+	
+		if (token != EOI) {
+			error();
+			/* NOTREACHED */
+		}
+	
+		if (vp->type == integer)
+			printf("%d\n", vp->u.i);
+		else
+			printf("%s\n", vp->u.s);
+	
+		rval = is_zero_or_null(vp);
+	} 
+	/* else: longjmp */
+
+	/* cleanup */
+	expr_mem_cleanup();
+	return rval;
+}
Index: /trunk/src/kmk/variable.c
===================================================================
--- /trunk/src/kmk/variable.c	(revision 1706)
+++ /trunk/src/kmk/variable.c	(revision 1707)
@@ -1311,5 +1311,5 @@
 #ifdef CONFIG_WITH_KMK_BUILTIN
   /* The supported kMk Builtin commands. */
-  (void) define_variable ("KMK_BUILTIN", 11, "append cat chmod cp cmp echo install kDepIDB ln md5sum mkdir mv printf rm rmdir test", o_default, 0);
+  (void) define_variable ("KMK_BUILTIN", 11, "append cat chmod cp cmp echo expr install kDepIDB ln md5sum mkdir mv printf rm rmdir test", o_default, 0);
 #endif
 
