--- old/usr/src/test/libc-tests/tests/common/test_common.c	Thu Apr  2 18:43:05 2015
+++ new/usr/src/test/libc-tests/tests/common/test_common.c	Thu Apr  2 18:43:05 2015
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2015 Garrett D'Amore <garrett@damore.org>
  */
 
 /*
@@ -20,7 +20,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
+#include <errno.h>
 #include <pthread.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/param.h>
 #include "test_common.h"
 
 static int debug = 0;
@@ -27,6 +32,9 @@
 static int force = 0;
 static pthread_mutex_t lk;
 
+static int passes;
+static int tests;
+
 struct test {
 	char		*name;
 	int		ntids;
@@ -76,10 +84,12 @@
 	test_passed(t);
 	test_set_debug();
 	test_set_force();
+	test_summary();
+	(void) test_load_config(t, NULL, NULL);
 #endif
 
+	tests++;
 	return (t);
-
 }
 
 void
@@ -88,6 +98,16 @@
 	va_list args;
 
 	(void) pthread_mutex_lock(&lk);
+	if (t == NULL) {
+		(void) printf("FAILURE: ");
+		va_start(args, format);
+		(void) vprintf(format, args);
+		va_end(args);
+		(void) printf("\n");
+		(void) fflush(stdout);
+		(void) pthread_mutex_unlock(&lk);
+		return;
+	}
 	if (force || (t->ntids > 0)) {
 		(void) printf("TEST FAILING %s: ", t->name);
 	} else {
@@ -114,6 +134,9 @@
 void
 test_passed(test_t t)
 {
+	if (t == NULL) {
+		return;
+	}
 	if (t->ntids > 0) {
 		if (debug) {
 			(void) pthread_mutex_lock(&lk);
@@ -124,6 +147,7 @@
 	}
 	(void) pthread_mutex_lock(&lk);
 	if (t->fails == 0) {
+		passes++;
 		(void) printf("TEST PASS: %s\n", t->name);
 	} else {
 		(void) printf("TEST FAILED: %d failures\n", t->fails);
@@ -138,6 +162,17 @@
 }
 
 void
+test_summary(void)
+{
+	if (passes == tests) {
+		(void) printf("TEST SUMMARY: %d / %d (ok)\n", passes, tests);
+	} else {
+		(void) printf("TEST SUMMARY: %d / %d (%d failing)\n",
+		    passes, tests, tests - passes);
+	}
+}
+
+void
 test_debugf(test_t t, const char *format, ...)
 {
 	va_list args;
@@ -146,8 +181,11 @@
 		return;
 
 	(void) pthread_mutex_lock(&lk);
-	(void) printf("TEST DEBUG %s: ", t->name);
-
+	if (t) {
+		(void) printf("TEST DEBUG %s: ", t->name);
+	} else {
+		(void) printf("TEST DEBUG: ");
+	}
 	va_start(args, format);
 	(void) vprintf(format, args);
 	va_end(args);
@@ -203,4 +241,166 @@
 		t->ntids--;
 	}
 	test_passed(t);
+}
+
+void
+test_trim(char **ptr)
+{
+	char *p = *ptr;
+	while (isspace(*p)) {
+		p++;
+	}
+	*ptr = p;
+	p += strlen(p);
+	while ((--p >= *ptr) && (isspace(*p))) {
+		*p = 0;
+	}
+}
+
+#define	MAXCB		20
+#define	MAXFIELD	20
+
+int
+test_load_config(test_t t, const char *fname, ...)
+{
+	va_list		va;
+	const char	*keyws[MAXCB];
+	test_cfg_func_t	callbs[MAXCB];
+	char		*fields[MAXFIELD];
+	int		nfields;
+
+	FILE    	*cfg;
+	char    	line[1024];
+	char    	buf[1024];
+	int		done;
+	char		*ptr;
+	char		*tok;
+	char		*err;
+	int		lineno;
+	int		rv;
+	int		found;
+	char		path[MAXPATHLEN];
+	int		i;
+
+	va_start(va, fname);
+	for (i = 0; i < MAXCB; i++) {
+		keyws[i] = (const char *)va_arg(va, const char *);
+		if (keyws[i] == NULL)
+			break;
+		callbs[i] = (test_cfg_func_t)va_arg(va, test_cfg_func_t);
+	}
+	va_end(va);
+	if (i == MAXCB) {
+		test_debugf(t, "too many arguments to function >= %d", MAXCB);
+	}
+
+	found = 0;
+
+	if (access(fname, F_OK) == 0) {
+		found++;
+	}
+	if (!found && fname[0] != '/') {
+		char *stf = getenv("STF_SUITE");
+		if (stf == NULL) {
+			stf = "../..";
+		}
+		(void) snprintf(path, sizeof (path), "%s/cfg/%s", stf, fname);
+		if (access(path, F_OK) == 0) {
+			fname = path;
+			found++;
+		} else {
+			(void) snprintf(path, sizeof (path), "cfg/%s", fname);
+			if (access(path, F_OK) == 0) {
+				fname = path;
+				found++;
+			}
+		}
+	}
+
+	if ((cfg = fopen(fname, "r")) ==  NULL) {
+		test_failed(t, "open(%s): %s", fname, strerror(errno));
+		return (-1);
+	}
+
+	line[0] = 0;
+	done = 0;
+	lineno = 0;
+
+	while (!done) {
+
+		lineno++;
+
+		if (fgets(buf, sizeof (buf), cfg) == NULL) {
+			done++;
+		} else {
+			(void) strtok(buf, "\n");
+			if ((*buf != 0) && (buf[strlen(buf)-1] == '\\')) {
+				/*
+				 * Continuation.  This isn't quite right,
+				 * as it doesn't allow for a "\" at the
+				 * end of line (no escaping).
+				 */
+				buf[strlen(buf)-1] = 0;
+				(void) strlcat(line, buf, sizeof (line));
+				continue;
+			}
+			(void) strlcat(line, buf, sizeof (line));
+		}
+
+		/* got a line */
+		ptr = line;
+		test_trim(&ptr);
+
+		/* skip comments and empty lines */
+		if (ptr[0] == 0 || ptr[0] == '#') {
+			line[0] = 0;
+			continue;
+		}
+
+		tok = strsep(&ptr, "|");
+		if (tok == NULL) {
+			break;
+		}
+		test_trim(&tok);
+
+		for (nfields = 0; nfields < MAXFIELD; nfields++) {
+			fields[nfields] = strsep(&ptr, "|");
+			if (fields[nfields] == NULL) {
+				break;
+			}
+			test_trim(&fields[nfields]);
+		}
+
+		found = 0;
+		rv = 0;
+
+		for (int i = 0; keyws[i] != NULL; i++) {
+			if (strcmp(tok, keyws[i]) == 0) {
+				found++;
+				err = NULL;
+				rv = callbs[i](fields, nfields, &err);
+			}
+		}
+		if (!found) {
+			rv = -1;
+			err = NULL;
+			(void) asprintf(&err, "unknown keyword %s", tok);
+		}
+		if (rv != 0) {
+			if (err) {
+				test_failed(t, "%s:%d: %s", fname,
+				    lineno, err);
+				free(err);
+			} else {
+				test_failed(t, "%s:%d: unknown error",
+				    fname, lineno);
+			}
+			(void) fclose(cfg);
+			return (rv);
+		}
+
+		line[0] = 0;
+	}
+	(void) fclose(cfg);
+	return (0);
 }
