--- old/usr/src/cmd/projadd/Makefile	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/Makefile	Thu Dec 15 19:51:09 2016
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # cmd/projadd/Makefile
@@ -27,31 +27,20 @@
 
 include ../Makefile.cmd
 
-PROGS= projadd projmod projdel
-USRSBINPROGS= $(PROGS:%=$(ROOTUSRSBIN)/%)
-POFILES= $(PROGS:%=%.po)
+SUBDIRS=	projadd projmod projdel projtest
 
-# No msg catalog here.
-POFILE=
+all :=		TARGET = all
+install :=	TARGET = install
+clean :=	TARGET = clean
+clobber :=	TARGET = clobber
+lint :=		TARGET = lint
+_msg :=		TARGET = _msg
 
-CLOBBERFILES += $(PROGS)
-
 .KEEP_STATE:
 
-all: $(PROGS)
+all install lint clean clobber _msg: $(SUBDIRS)
 
-install : all .WAIT $(USRSBINPROGS)
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(MFLAGS) $(TARGET)
 
-clean lint:
-
-_msg: $(MSGDOMAIN) $(POFILES)
-	$(CP) $(POFILES) $(MSGDOMAIN)
-
-$(MSGDOMAIN):
-	$(INS.dir)
-
-clobber: clean
-	$(RM) $(PROG) $(CLOBBERFILES)
-
-$(ROOTUSRSBIN)/% : %
-	$(INS.file)
+FRC:
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/Makefile.projadd	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/stat/Makefile.stat
+
+PROJADD = $(SRC)/cmd/projadd
+PROJADDCOMMONDIR = $(PROJADD)/common
+
+COMMON_OBJS = projent.o attrib.o util.o resctl.o lst.o
+COMMON_SRCS = $(COMMON_OBJS:%.o=$(PROJADDCOMMONDIR)/%.c)
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/attrib.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,1404 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <stddef.h>
+#include <limits.h>
+#include <rctl.h>
+#include <regex.h>
+#include <ctype.h>
+
+#include <sys/debug.h>
+
+#include "attrib.h"
+#include "resctl.h"
+#include "util.h"
+
+#define	BOSTR_REG_EXP	"^"
+#define	EOSTR_REG_EXP	"$"
+#define	EQUAL_REG_EXP	"="
+#define	STRN0_REG_EXP	"(.*)"
+#define	IDENT_REG_EXP	"[[:alpha:]][[:alnum:]_.-]*"
+#define	INTNM_REG_EXP	"[[:digit:]]+"
+#define	SIGAC_REG_EXP	"sig(nal)?(=.*)?"
+#define	SIGHD_REG_EXP	"(signal|sig)"
+#define	SIGVL_REG_EXP	"(([[:digit:]]+)|((SIG)?([[:upper:]]+)([+-][123])?))"
+#define	SIGNL_REG_EXP	SIGHD_REG_EXP EQUAL_REG_EXP SIGVL_REG_EXP
+#define	STOCK_REG_EXP	"([[:upper:]]{1,5}(.[[:upper:]]{1,5})?,)?"
+#define	ATTRB_REG_EXP	"(" STOCK_REG_EXP IDENT_REG_EXP ")"
+#define	ATVAL_REG_EXP	ATTRB_REG_EXP EQUAL_REG_EXP STRN0_REG_EXP
+
+#define	TO_EXP(X)	BOSTR_REG_EXP X EOSTR_REG_EXP
+
+#define	POOLN_EXP	TO_EXP(IDENT_REG_EXP)
+#define	INTNM_EXP	TO_EXP(INTNM_REG_EXP)
+#define	SIGAC_EXP	TO_EXP(SIGAC_REG_EXP)
+#define	SIGNL_EXP	TO_EXP(SIGNL_REG_EXP)
+#define	ATTRB_EXP	TO_EXP(ATTRB_REG_EXP)
+#define	ATVAL_EXP	TO_EXP(ATVAL_REG_EXP)
+
+#define	MAX_OF(X, Y)	(((X) > (Y)) ? (X) : (Y))
+
+#define	SEQU(X, Y)		(strcmp((X), (Y)) == 0)
+#define	SIN1(X, S1)		((SEQU((X), (S1))))
+#define	SIN2(X, S1, S2)		((SEQU((X), (S1))) || (SIN1((X), (S2))))
+#define	SIN3(X, S1, S2, S3)	((SEQU((X), (S1))) || (SIN2((X), (S2), (S3))))
+
+#define	ATT_VAL_TYPE_NULL	0
+#define	ATT_VAL_TYPE_VALUE	1
+#define	ATT_VAL_TYPE_LIST	2
+
+#define	ATT_ALLOC()		attrib_alloc()
+#define	ATT_VAL_ALLOC(T, V)	attrib_val_alloc((T), (V))
+#define	ATT_VAL_ALLOC_NULL()	ATT_VAL_ALLOC(ATT_VAL_TYPE_NULL, NULL)
+#define	ATT_VAL_ALLOC_VALUE(V)	ATT_VAL_ALLOC(ATT_VAL_TYPE_VALUE, (V))
+#define	ATT_VAL_ALLOC_LIST(L)	ATT_VAL_ALLOC(ATT_VAL_TYPE_LIST, (L))
+
+typedef struct attrib_val_s {
+	int att_val_type;
+	/*LINTED*/
+	union {
+		char *att_val_value;
+		lst_t *att_val_values;
+	};
+} attrib_val_t;
+
+typedef struct attrib_s {
+	char *att_name;
+	attrib_val_t *att_value;
+} attrib_t;
+
+
+attrib_t *attrib_alloc();
+attrib_val_t *attrib_val_alloc(int, void *);
+char *attrib_val_tostring(attrib_val_t *, boolean_t);
+
+int
+attrib_validate_rctl(attrib_t *att, resctlrule_t *rule, lst_t *errlst)
+{
+	int ret = 0;
+	char *atname = att->att_name;
+	attrib_val_t *atv, *atval = att->att_value;
+	attrib_val_t *priv, *val, *action;
+	char *vpriv, *vval, *vaction, *sigstr;
+	int atv_type = atval->att_val_type;
+	char *str;
+	int i, j, k;
+
+	int nmatch;
+	uint8_t rpriv, raction, sigval;
+	uint64_t rmax;
+	regex_t pintexp, sigacexp, signlexp;
+	regmatch_t *mat;
+
+	int nonecount, denycount, sigcount;
+
+	if (regcomp(&pintexp, INTNM_EXP, REG_EXTENDED) != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Failed to compile regex for pos. integer"));
+			return (1);
+	}
+
+	if (regcomp(&sigacexp, SIGAC_EXP, REG_EXTENDED) != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Failed to compile regex for sigaction"));
+			regfree(&pintexp);
+			return (1);
+	}
+
+	if (regcomp(&signlexp, SIGNL_EXP, REG_EXTENDED) != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Failed to compile regex for signal"));
+			regfree(&pintexp);
+			regfree(&sigacexp);
+			return (1);
+	}
+
+	nmatch = signlexp.re_nsub + 1;
+	mat = util_safe_zmalloc(nmatch * sizeof (regmatch_t));
+
+	if (atv_type != ATT_VAL_TYPE_LIST) {
+		util_add_errmsg(errlst, gettext(
+		    "rctl \"%s\" missing value"), atname);
+		ret = 1;
+		goto out;
+	}
+
+	for (i = 0; i < lst_size(atval->att_val_values); i++) {
+		atv = lst_at(atval->att_val_values, i);
+		if (atv->att_val_type != ATT_VAL_TYPE_LIST) {
+			if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" value \"%s\" "
+				    "should be in ()"), atname, str);
+				free(str);
+			} else {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" value "
+				    "should be in ()"), atname);
+			}
+			ret = 1;
+			continue;
+		}
+		/* Values should be in the form (priv, val, actions) */
+		if (lst_size(atv->att_val_values) < 3) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" value should be in the form "
+			    "(priv, val, action[,...])[,....]"), atname);
+			ret = 1;
+			continue;
+		}
+
+		priv = lst_at(atv->att_val_values, 0);
+		val = lst_at(atv->att_val_values, 1);
+		/* actions = el[2], el[3], ... */
+
+		vpriv = priv->att_val_value;
+		rpriv = rule->resctl_privs;
+
+		if (priv->att_val_type != ATT_VAL_TYPE_VALUE) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" invalid privilege"), atname);
+			ret = 1;
+		} else if (!SIN3(vpriv, "basic", "privileged", "priv")) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" unknown privilege \"%s\""),
+			    atname, vpriv);
+			ret = 1;
+		} else if (!(
+		    ((rpriv & RESCTL_PRIV_PRIVE) &&
+		    SEQU(vpriv, "priv")) ||
+		    ((rpriv & RESCTL_PRIV_PRIVD) &&
+		    SEQU(vpriv, "privileged")) ||
+		    ((rpriv & RESCTL_PRIV_BASIC) &&
+		    SEQU(vpriv, "basic")))) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" privilege not allowed \"%s\""),
+			    atname, vpriv);
+			ret = 1;
+		}
+
+		vval = val->att_val_value;
+		rmax = rule->resctl_max;
+
+		if (val->att_val_type != ATT_VAL_TYPE_VALUE) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" invalid value"), atname);
+			ret = 1;
+		} else if (regexec(&pintexp, vval, 0, NULL, 0) != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" value \"%s\" is not an integer"),
+			    atname, vval);
+			ret = 1;
+		} else if (strtoll(vval, NULL, 0) > rmax) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" value \"%s\" exceeds system limit"),
+			    atname, vval);
+			ret = 1;
+		}
+
+		nonecount = 0;
+		denycount = 0;
+		sigcount = 0;
+
+		for (j = 2; j < lst_size(atv->att_val_values); j++) {
+			action = lst_at(atv->att_val_values, j);
+
+			if (action->att_val_type != ATT_VAL_TYPE_VALUE) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" invalid action"), atname);
+				ret = 1;
+				continue;
+			}
+
+			vaction = action->att_val_value;
+
+			if (regexec(&sigacexp, vaction, 0, NULL, 0) != 0 &&
+			    !SIN2(vaction, "none", "deny")) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" unknown action \"%s\""),
+				    atname, vaction);
+				ret = 1;
+				continue;
+			}
+
+			raction = rule->resctl_action;
+			if (!(((raction & RESCTL_ACTN_SIGN) &&
+			    regexec(&sigacexp, vaction, 0, NULL, 0) == 0) ||
+			    ((raction & RESCTL_ACTN_NONE) &&
+			    SEQU(vaction, "none")) ||
+			    ((raction & RESCTL_ACTN_DENY) &&
+			    SEQU(vaction, "deny")))) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" action not allowed \"%s\""),
+				    atname, vaction);
+				ret = 1;
+				continue;
+			}
+
+			if (SEQU(vaction, "none")) {
+				if (nonecount >= 1) {
+					util_add_errmsg(errlst, gettext(
+					    "rctl \"%s\" duplicate action "
+					    "none"), atname);
+					ret = 1;
+				}
+				nonecount++;
+				continue;
+			}
+
+			if (SEQU(vaction, "deny")) {
+				if (denycount >= 1) {
+					util_add_errmsg(errlst, gettext(
+					    "rctl \"%s\" duplicate action "
+					    "deny"), atname);
+					ret = 1;
+				}
+				denycount++;
+				continue;
+			}
+
+			/* At this point, the action must be signal. */
+			if (sigcount >= 1) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" duplicate action sig"),
+				    atname);
+				ret = 1;
+			}
+			sigcount++;
+
+			/*
+			 * Make sure signal is correct format, on of:
+			 * sig=##
+			 * signal=##
+			 * sig=SIGXXX
+			 * signal=SIGXXX
+			 * sig=XXX
+			 * signal=XXX
+			 */
+
+			if (regexec(&signlexp, vaction, nmatch, mat, 0) != 0 ||
+			    (sigstr = util_substr(&signlexp, mat, vaction, 2))
+			    == NULL) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" invalid signal \"%s\""),
+				    atname, vaction);
+				ret = 1;
+				continue;
+			}
+
+			/* Our version of sigstr =~ s/SIG// */
+			if (strstr(sigstr, "SIG") != NULL)
+				sigstr = strstr(sigstr, "SIG") + 3;
+
+			sigval = 0;
+			for (k = 0; k < SIGS_CNT; k++) {
+				if (SEQU(sigs[k].sig, sigstr))
+					sigval = sigs[k].mask;
+			}
+			free(sigstr);
+
+			if (sigval == 0) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" invalid signal \"%s\""),
+				    atname, vaction);
+				ret = 1;
+				continue;
+			}
+
+			if (!(sigval & rule->resctl_sigs)) {
+				util_add_errmsg(errlst, gettext(
+				    "rctl \"%s\" signal not allowed \"%s\""),
+				    atname, vaction);
+				ret = 1;
+				continue;
+			}
+		}
+		if (nonecount > 0 && (denycount > 0 || sigcount > 0)) {
+			util_add_errmsg(errlst, gettext(
+			    "rctl \"%s\" action \"none\" specified with "
+			    "other actions"),
+			    atname);
+			ret = 1;
+		}
+	}
+
+out:
+	free(mat);
+	regfree(&signlexp);
+	regfree(&sigacexp);
+	regfree(&pintexp);
+	return (ret);
+}
+
+int
+attrib_validate(attrib_t *att, lst_t *errlst)
+{
+	int ret = 0;
+	char *atname = att->att_name;
+	attrib_val_t *atv = att->att_value;
+	int atv_type = atv->att_val_type;
+	char *str, *eptr;
+	long long ll;
+
+	resctl_info_t rinfo;
+	resctlrule_t rrule;
+
+	regex_t poolnexp;
+	if (regcomp(&poolnexp, POOLN_EXP, REG_EXTENDED) != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Failed to compile poolname regular expression:"));
+			return (1);
+	}
+
+	if (SEQU(atname, "task.final")) {
+		if (atv_type != ATT_VAL_TYPE_NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "task.final should not have value"));
+			ret = 1;
+		}
+	} else if (SEQU(atname, "rcap.max-rss")) {
+		if (atv_type == ATT_VAL_TYPE_NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "rcap.max-rss missing value"));
+			ret = 1;
+		} else if (atv_type == ATT_VAL_TYPE_LIST) {
+			util_add_errmsg(errlst, gettext(
+			    "rcap.max-rss should have single value"));
+			ret = 1;
+		} else if (atv_type == ATT_VAL_TYPE_VALUE) {
+			if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) {
+				ll = strtoll(str, &eptr, 0);
+				if (*eptr != '\0') {
+					util_add_errmsg(errlst, gettext(
+					    "rcap.max-rss is not an integer "
+					    "value: \"%s\""), str);
+					ret = 1;
+				} else if (ll == LLONG_MIN && errno == ERANGE) {
+					util_add_errmsg(errlst, gettext(
+					    "rcap.max-rss too small"));
+					ret = 1;
+				} else if (ll == LLONG_MAX && errno == ERANGE) {
+					util_add_errmsg(errlst, gettext(
+					    "rcap.max-rss too large"));
+					ret = 1;
+				} else if (ll < 0) {
+					util_add_errmsg(errlst, gettext(
+					    "rcap.max-rss should not have "
+					    "negative value: \"%s\""), str);
+					ret = 1;
+				}
+				free(str);
+			} else {
+				util_add_errmsg(errlst, gettext(
+				    "rcap.max-rss has invalid value"));
+				ret = 1;
+			}
+		}
+	} else if (SEQU(atname, "project.pool")) {
+		if (atv_type == ATT_VAL_TYPE_NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "project.pool missing value"));
+			ret = 1;
+		} else if (atv_type == ATT_VAL_TYPE_LIST) {
+			util_add_errmsg(errlst, gettext(
+			    "project.pool should have single value"));
+			ret = 1;
+		} else if (atv_type == ATT_VAL_TYPE_VALUE) {
+			if ((str = attrib_val_tostring(atv, B_FALSE)) != NULL) {
+				if (regexec(&poolnexp, str, 0, NULL, 0) != 0) {
+					util_add_errmsg(errlst, gettext(
+					    "project.pool: invalid pool "
+					    "name \"%s\""), str);
+					ret = 1;
+				} else if (resctl_pool_exist(str) != 0) {
+					util_add_errmsg(errlst, gettext(
+					    "project.pool: pools not enabled  "
+					    "or pool does not exist: \"%s\""),
+					    str);
+					ret = 1;
+				}
+				free(str);
+			} else {
+				util_add_errmsg(errlst, gettext(
+				    "project.pool has invalid value "));
+				ret = 1;
+			}
+		}
+	} else if (resctl_get_info(atname, &rinfo) == 0) {
+		resctl_get_rule(&rinfo, &rrule);
+		if (attrib_validate_rctl(att, &rrule, errlst) != 0) {
+			ret = 1;
+		}
+	}
+
+	regfree(&poolnexp);
+	return (ret);
+}
+
+int
+attrib_validate_lst(lst_t *attribs, lst_t *errlst)
+{
+	int i, j;
+	attrib_t *att;
+	char **atnames, **atlast;
+	char *atname;
+	int ret = 0;
+
+	atlast = atnames = util_safe_zmalloc(
+	    (lst_size(attribs) + 1) * sizeof (char *));
+	for (i = 0; i < lst_size(attribs); i++) {
+		att = lst_at(attribs, i);
+
+		/* Validate this attribute */
+		if (attrib_validate(att, errlst) != 0)
+			ret = 1;
+		/* Make sure it is not duplicated */
+		for (j = 0; (atname = atnames[j]) != NULL; j++) {
+			if (strcmp(atname, att->att_name) == 0) {
+				util_add_errmsg(errlst, gettext(
+				    "Duplicate attributes \"%s\""), atname);
+				ret = 1;
+			}
+		}
+		/*
+		 * Add it to the attribute name to the
+		 * temporary list if not found
+		 */
+		if (atname == NULL) {
+			*atlast++ = att->att_name;
+		}
+	}
+	free(atnames);
+	return (ret);
+}
+
+attrib_t *
+attrib_alloc()
+{
+	return (util_safe_zmalloc(sizeof (attrib_t)));
+}
+
+attrib_val_t *
+attrib_val_alloc(int type, void *val)
+{
+	attrib_val_t *ret;
+
+	ret = util_safe_malloc(sizeof (attrib_val_t));
+	ret->att_val_type = type;
+	ret->att_val_value = val;
+	return (ret);
+}
+
+char *
+attrib_val_tostring(attrib_val_t *val, boolean_t innerlist)
+{
+	char *ret = NULL;
+	char *vstring;
+	int i;
+	attrib_val_t *v;
+	switch (val->att_val_type) {
+		case ATT_VAL_TYPE_NULL:
+			return (util_safe_strdup(""));
+		case ATT_VAL_TYPE_VALUE:
+			return (util_safe_strdup(val->att_val_value));
+		case ATT_VAL_TYPE_LIST:
+			/* Only innerlists need to be betweeen ( and ) */
+			if (innerlist)
+				ret = UTIL_STR_APPEND1(ret, "(");
+			for (i = 0; i < lst_size(val->att_val_values);
+			    i++) {
+				v = lst_at(val->att_val_values, i);
+				if (i > 0) {
+					ret = UTIL_STR_APPEND1(ret, ",");
+				}
+				if ((vstring =
+				    attrib_val_tostring(v, B_TRUE)) == NULL) {
+					UTIL_FREE_SNULL(ret);
+					goto out;
+				}
+				ret = UTIL_STR_APPEND1(ret, vstring);
+				free(vstring);
+			}
+			if (innerlist)
+				ret = UTIL_STR_APPEND1(ret, ")");
+			return (ret);
+	}
+
+out:
+	return (ret);
+}
+
+char *
+attrib_tostring(void *at)
+{
+	attrib_t *att;
+	char *ret = NULL, *vstring;
+
+	att = (attrib_t *)at;
+	ret = UTIL_STR_APPEND1(ret, att->att_name);
+	if ((vstring = attrib_val_tostring(att->att_value, B_FALSE)) != NULL) {
+		if (strlen(vstring) > 0)
+			ret = UTIL_STR_APPEND2(ret, "=", vstring);
+		free(vstring);
+		return (ret);
+	}
+	UTIL_FREE_SNULL(ret);
+	return (ret);
+}
+
+char *
+attrib_lst_tostring(lst_t *attrs)
+{
+	int i;
+	attrib_t *att;
+	char *ret = NULL;
+	char *str;
+
+	ret = UTIL_STR_APPEND1(ret, "");
+	for (i = 0; i < lst_size(attrs); i++) {
+		att = lst_at(attrs, i);
+
+		if ((str = attrib_tostring(att)) != NULL) {
+			if (i > 0)
+				ret = UTIL_STR_APPEND1(ret, ";");
+
+			ret = UTIL_STR_APPEND1(ret, str);
+			free(str);
+			continue;
+		}
+
+		free(ret);
+		return (NULL);
+	}
+	return (ret);
+}
+
+void
+attrib_val_free(attrib_val_t *atv)
+{
+	attrib_val_t *val;
+
+	if (atv->att_val_type == ATT_VAL_TYPE_VALUE) {
+		free(atv->att_val_value);
+	} else if (atv->att_val_type == ATT_VAL_TYPE_LIST) {
+		while (!lst_is_empty(atv->att_val_values)) {
+			val = lst_at(atv->att_val_values, 0);
+			(void) lst_remove(atv->att_val_values, val);
+			attrib_val_free(val);
+			free(val);
+		}
+		free(atv->att_val_values);
+	}
+}
+
+void
+attrib_free(attrib_t *att)
+{
+	free(att->att_name);
+	if (att->att_value != NULL) {
+		attrib_val_free(att->att_value);
+		free(att->att_value);
+	}
+}
+void
+attrib_free_lst(lst_t *attribs)
+{
+	attrib_t *att;
+
+	if (attribs == NULL)
+		return;
+
+	while (!lst_is_empty(attribs)) {
+		att = lst_at(attribs, 0);
+		(void) lst_remove(attribs, att);
+		attrib_free(att);
+		free(att);
+	}
+}
+
+void
+attrib_sort_lst(lst_t *attribs)
+{
+	int i, j, n;
+	attrib_t *atti, *attj;
+
+	if (attribs == NULL)
+		return;
+
+	n = lst_size(attribs);
+	for (i = 0; i < n - 1; i++) {
+		for (j = i + 1; j < n; j++) {
+			atti = lst_at(attribs, i);
+			attj = lst_at(attribs, j);
+			if (strcmp(attj->att_name, atti->att_name) < 0) {
+				(void) lst_replace_at(attribs, i, attj);
+				(void) lst_replace_at(attribs, j, atti);
+			}
+		}
+	}
+}
+
+void
+attrib_val_to_list(attrib_val_t *atv)
+{
+	void *val;
+	int type;
+	attrib_val_t *mat;
+
+	if (atv->att_val_type == ATT_VAL_TYPE_LIST)
+		return;
+
+	val = atv->att_val_value;
+	type = atv->att_val_type;
+
+	atv->att_val_type = ATT_VAL_TYPE_LIST;
+	atv->att_val_values = util_safe_malloc(sizeof (lst_t));
+	lst_create(atv->att_val_values);
+
+	if (type == ATT_VAL_TYPE_VALUE && val != NULL) {
+		mat = ATT_VAL_ALLOC_VALUE(val);
+		lst_insert_tail(atv->att_val_values, mat);
+	}
+}
+
+void
+attrib_val_append(attrib_val_t *atv, char *token)
+{
+	attrib_val_t *nat;
+	if (atv->att_val_type == ATT_VAL_TYPE_VALUE) {
+		/* convert this to LIST attribute */
+		attrib_val_to_list(atv);
+	}
+
+	if (atv->att_val_type == ATT_VAL_TYPE_NULL) {
+		/* convert this to VALUE attribute */
+		atv->att_val_type = ATT_VAL_TYPE_VALUE;
+		atv->att_val_value = util_safe_strdup(token);
+	} else if (atv->att_val_type == ATT_VAL_TYPE_LIST) {
+		/* append token to the list */
+		nat = ATT_VAL_ALLOC_VALUE(util_safe_strdup(token));
+		lst_insert_tail(atv->att_val_values, nat);
+	}
+}
+
+attrib_val_t *
+attrib_val_parse(char *values, lst_t *errlst)
+{
+	attrib_val_t *ret = NULL;
+	attrib_val_t *at;
+	attrib_val_t *nat;
+	lst_t stk;
+
+	char **tokens, *token, *usedtokens, *prev;
+	int i, error, parendepth;
+
+	error = parendepth = 0;
+	prev = "";
+
+	if ((tokens = util_tokenize(values, errlst)) == NULL) {
+		goto out1;
+	}
+
+	lst_create(&stk);
+	usedtokens = UTIL_STR_APPEND1(NULL, "");
+
+	at = ret = ATT_VAL_ALLOC_NULL();
+
+	for (i = 0; (token = tokens[i]) != NULL; i++) {
+
+		usedtokens = UTIL_STR_APPEND1(usedtokens, token);
+
+		if (SEQU(token, ",")) {
+			if (SIN3(prev, ",", "(", "")) {
+				attrib_val_append(at, "");
+			}
+			attrib_val_to_list(at);
+			prev = ",";
+		} else if (SEQU(token, "(")) {
+			if (!(SIN3(prev, "(", ",", ""))) {
+				util_add_errmsg(errlst, gettext(
+				    "\"%s\" <- \"(\" unexpected"), usedtokens);
+				error = 1;
+				goto out;
+			}
+
+			switch (at->att_val_type) {
+				case ATT_VAL_TYPE_VALUE:
+					util_add_errmsg(errlst, gettext(
+					    "\"%s\" <- \"%s\" unexpected"),
+					    usedtokens, token);
+					error = 1;
+					goto out;
+				case ATT_VAL_TYPE_NULL:
+					/* Make is a LIST attrib */
+					attrib_val_to_list(at);
+					/*FALLTHROUGH*/
+				case ATT_VAL_TYPE_LIST:
+					/* Allocate NULL node */
+					nat = ATT_VAL_ALLOC_NULL();
+					attrib_val_to_list(nat);
+					lst_insert_tail(
+					    at->att_val_values, nat);
+					/* push at down one level */
+					lst_insert_head(&stk, at);
+					at = nat;
+				break;
+			}
+			parendepth++;
+			prev = "(";
+		} else if (SEQU(token, ")")) {
+			if (parendepth <= 0) {
+				util_add_errmsg(errlst, gettext(
+				    "\"%s\" <- \")\" unexpected"), usedtokens);
+				error = 1;
+				goto out;
+			}
+			if (SIN2(prev, ",", "(")) {
+				attrib_val_append(at, "");
+			}
+
+			if (!lst_is_empty(&stk)) {
+				at = lst_at(&stk, 0);
+				(void) lst_remove(&stk, at);
+			}
+			parendepth--;
+			prev = ")";
+		} else {
+			if (!(SIN3(prev, ",", "(", ""))) {
+				util_add_errmsg(errlst, gettext(
+				    "\"%s\" <- \"%s\" unexpected"),
+				    usedtokens, token);
+				error = 1;
+				goto out;
+			}
+
+			attrib_val_append(at, token);
+			prev = token;
+		}
+	}
+
+	if (parendepth != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "\"%s\" <- \")\" missing"),
+		    usedtokens);
+		error = 1;
+		goto out;
+	}
+
+	if (SIN2(prev, ",", "")) {
+		switch (at->att_val_type) {
+			case ATT_VAL_TYPE_NULL:
+				util_add_errmsg(errlst, gettext(
+				    "\"%s\" unexpected"),
+				    usedtokens);
+				error = 1;
+				goto out;
+			case ATT_VAL_TYPE_VALUE:
+			case ATT_VAL_TYPE_LIST:
+				attrib_val_append(at, "");
+			break;
+		}
+	}
+
+out:
+	while (!lst_is_empty(&stk)) {
+		at = lst_at(&stk, 0);
+		(void) lst_remove(&stk, at);
+	}
+
+	util_free_tokens(tokens);
+	free(tokens);
+	free(usedtokens);
+	if (error) {
+		attrib_val_free(ret);
+		UTIL_FREE_SNULL(ret);
+	}
+out1:
+	return (ret);
+}
+
+attrib_t *
+attrib_parse(regex_t *attrbexp, regex_t *atvalexp, char *att, int flags,
+    lst_t *errlst)
+{
+	int nmatch = MAX_OF(attrbexp->re_nsub, atvalexp->re_nsub) + 1;
+	attrib_t *ret = NULL;
+	attrib_val_t *retv, *atv, *atvl;
+	char *values = NULL;
+	int vidx, nidx, vlen;
+	int scale;
+
+	char *num, *mod, *unit;
+	int i;
+
+	resctl_info_t rinfo;
+	resctlrule_t rrule;
+
+	regmatch_t *mat = util_safe_malloc(nmatch * sizeof (regmatch_t));
+	ret = ATT_ALLOC();
+
+	if (regexec(attrbexp, att, attrbexp->re_nsub + 1, mat, 0) == 0) {
+		ret->att_name = util_safe_strdup(att);
+		ret->att_value = ATT_VAL_ALLOC_NULL();
+	} else if (regexec(atvalexp, att,
+	    atvalexp->re_nsub + 1, mat, 0) == 0) {
+		vidx = atvalexp->re_nsub;
+		vlen = mat[vidx].rm_eo - mat[vidx].rm_so;
+		nidx = atvalexp->re_nsub - 3;
+		ret->att_name = util_substr(atvalexp, mat, att, nidx);
+
+		if (vlen > 0) {
+			values = util_substr(atvalexp, mat, att, vidx);
+			ret->att_value = attrib_val_parse(values, errlst);
+			free(values);
+			if (ret->att_value == NULL) {
+				util_add_errmsg(errlst, gettext(
+				    "Invalid value on attribute \"%s\""),
+				    ret->att_name);
+				attrib_free(ret);
+				UTIL_FREE_SNULL(ret);
+				goto out;
+			}
+		} else {
+			/* the value is an empty string */
+			ret->att_value = ATT_VAL_ALLOC_NULL();
+		}
+	} else {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid attribute \"%s\""), att);
+		attrib_free(ret);
+		UTIL_FREE_SNULL(ret);
+		goto out;
+	}
+
+	if (!(flags & F_PAR_UNT))
+		goto out;
+
+	if (SEQU(ret->att_name, "rcap.max-rss")) {
+		values = attrib_val_tostring(ret->att_value, B_FALSE);
+		if (util_val2num(values, BYTES_SCALE, errlst,
+		    &num, &mod, &unit) == 0) {
+			attrib_val_free(ret->att_value);
+			ret->att_value = ATT_VAL_ALLOC_VALUE(num);
+			free(mod);
+			free(unit);
+		} else {
+			attrib_free(ret);
+			UTIL_FREE_SNULL(ret);
+			goto out;
+		}
+		free(values);
+	}
+
+	if (resctl_get_info(ret->att_name, &rinfo) == 0) {
+		resctl_get_rule(&rinfo, &rrule);
+		retv = ret->att_value;
+
+		switch (rrule.resctl_type) {
+			case RESCTL_TYPE_BYTES:
+				scale = BYTES_SCALE;
+				break;
+			case RESCTL_TYPE_SCNDS:
+				scale = SCNDS_SCALE;
+				break;
+			case RESCTL_TYPE_COUNT:
+				scale = COUNT_SCALE;
+				break;
+			default:
+				scale = UNKWN_SCALE;
+				break;
+		}
+
+		if (retv->att_val_type != ATT_VAL_TYPE_LIST)
+			goto out;
+
+
+		for (i = 0; i < lst_size(retv->att_val_values); i++) {
+			atvl = atv = lst_at(retv->att_val_values, i);
+
+			/*
+			 * Continue if not a list and the second value
+			 * is not a scaler value
+			 */
+			if (atv->att_val_type != ATT_VAL_TYPE_LIST ||
+			    lst_size(atv->att_val_values) < 3 ||
+			    (atv = lst_at(atv->att_val_values, 1)) == NULL ||
+			    atv->att_val_type != ATT_VAL_TYPE_VALUE) {
+				continue;
+			}
+			values = attrib_val_tostring(atv, B_FALSE);
+			if (util_val2num(values, scale, errlst,
+			    &num, &mod, &unit) == 0) {
+				attrib_val_free(atv);
+				atv = ATT_VAL_ALLOC_VALUE(num);
+				(void) lst_replace_at(atvl->att_val_values, 1,
+				    atv);
+				free(mod);
+				free(unit);
+
+			} else {
+				free(values);
+				attrib_free(ret);
+				UTIL_FREE_SNULL(ret);
+				goto out;
+			}
+			free(values);
+		}
+	}
+
+out:
+	free(mat);
+	return (ret);
+}
+
+lst_t *
+attrib_parse_attributes(char *attribs, int flags, lst_t *errlst)
+{
+	char *sattrs, *attrs, *att;
+	regex_t attrbexp, atvalexp;
+
+	attrib_t *natt = NULL;
+	lst_t *ret = NULL;
+
+	ret = util_safe_malloc(sizeof (lst_t));
+	lst_create(ret);
+
+	if (regcomp(&attrbexp, ATTRB_EXP, REG_EXTENDED) != 0)
+		goto out1;
+	if (regcomp(&atvalexp, ATVAL_EXP, REG_EXTENDED) != 0)
+		goto out2;
+
+	sattrs = attrs = util_safe_strdup(attribs);
+	while ((att = strsep(&attrs, ";")) != NULL) {
+		if (*att == '\0')
+			continue;
+		if ((natt = attrib_parse(&attrbexp,
+		    &atvalexp, att, flags, errlst)) == NULL) {
+			attrib_free_lst(ret);
+			UTIL_FREE_SNULL(ret);
+			break;
+		}
+
+		lst_insert_tail(ret, natt);
+	}
+
+	free(sattrs);
+	regfree(&atvalexp);
+out2:
+	regfree(&attrbexp);
+out1:
+	return (ret);
+}
+
+attrib_val_t *
+attrib_val_duplicate(attrib_val_t *atv)
+{
+	int i;
+	lst_t *values;
+	attrib_val_t *val;
+	attrib_val_t *natv;
+
+	switch (atv->att_val_type) {
+		case ATT_VAL_TYPE_NULL:
+			natv = ATT_VAL_ALLOC_NULL();
+		break;
+		case ATT_VAL_TYPE_VALUE:
+			natv = ATT_VAL_ALLOC_VALUE(
+			    util_safe_strdup(atv->att_val_value));
+		break;
+		case ATT_VAL_TYPE_LIST:
+			values = util_safe_malloc(sizeof (lst_t));
+			lst_create(values);
+			for (i = 0; i < lst_size(atv->att_val_values);
+			    i++) {
+				val = lst_at(atv->att_val_values, i);
+				lst_insert_tail(values,
+				    attrib_val_duplicate(val));
+			}
+			natv = ATT_VAL_ALLOC_LIST(values);
+		break;
+	}
+
+	return (natv);
+}
+
+attrib_t *
+attrib_duplicate(attrib_t *att)
+{
+	attrib_t *natt = ATT_ALLOC();
+	natt->att_name = util_safe_strdup(att->att_name);
+	natt->att_value = attrib_val_duplicate(att->att_value);
+	return (natt);
+}
+
+attrib_t *
+attrib_merge_add(attrib_t *eatt, attrib_t *natt)
+{
+	int i;
+	attrib_t *att;
+	attrib_val_t *atv, *eatv, *natv;
+	lst_t *values;
+
+	eatv = eatt->att_value;
+	natv = natt->att_value;
+	att = ATT_ALLOC();
+	att->att_name = util_safe_strdup(eatt->att_name);
+
+	if (eatv->att_val_type == ATT_VAL_TYPE_NULL) {
+
+		/* NULL + X -> X */
+		atv = attrib_val_duplicate(natv);
+
+	} else if (natv->att_val_type == ATT_VAL_TYPE_NULL) {
+
+		/* X + NULL -> X */
+		atv = attrib_val_duplicate(eatv);
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE &&
+	    natv->att_val_type == ATT_VAL_TYPE_VALUE) {
+
+		/* VALUE + VALUE -> LIST */
+		values = util_safe_malloc(sizeof (lst_t));
+		lst_create(values);
+		lst_insert_tail(values, attrib_val_duplicate(eatv));
+		lst_insert_tail(values, attrib_val_duplicate(natv));
+		atv = ATT_VAL_ALLOC_LIST(values);
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE &&
+	    natv->att_val_type == ATT_VAL_TYPE_LIST) {
+
+		/* VALUE + LIST -> LIST */
+		atv = attrib_val_duplicate(natv);
+		lst_insert_head(atv->att_val_values,
+		    attrib_val_duplicate(eatv));
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_LIST &&
+	    natv->att_val_type == ATT_VAL_TYPE_VALUE) {
+
+		/* LIST + VALUE -> LIST */
+		atv = attrib_val_duplicate(eatv);
+		lst_insert_tail(atv->att_val_values,
+		    attrib_val_duplicate(natv));
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_LIST &&
+	    natv->att_val_type == ATT_VAL_TYPE_LIST) {
+
+		/* LIST + LIST -> LIST */
+		atv = attrib_val_duplicate(eatv);
+		for (i = 0; i < lst_size(natv->att_val_values); i++) {
+			lst_insert_tail(atv->att_val_values,
+			    attrib_val_duplicate(
+			    lst_at(natv->att_val_values, i)));
+		}
+	}
+
+	att->att_value = atv;
+	return (att);
+}
+
+int
+attrib_val_equal(attrib_val_t *xatv, attrib_val_t *yatv)
+{
+	int i;
+	attrib_val_t *xv, *yv;
+
+	if (xatv->att_val_type != yatv->att_val_type)
+		return (1);
+
+	switch (xatv->att_val_type) {
+		case ATT_VAL_TYPE_NULL:
+			return (0);
+		case ATT_VAL_TYPE_VALUE:
+			if (SEQU(xatv->att_val_value, yatv->att_val_value)) {
+				return (0);
+			}
+			return (1);
+		case ATT_VAL_TYPE_LIST:
+			if (lst_size(xatv->att_val_values) !=
+			    lst_size(yatv->att_val_values))
+				return (1);
+			for (i = 0; i < lst_size(xatv->att_val_values);
+			    i++) {
+				xv = lst_at(xatv->att_val_values, i);
+				yv = lst_at(yatv->att_val_values, i);
+				if (attrib_val_equal(xv, yv) != 0) {
+					return (1);
+				}
+			}
+		break;
+	}
+	return (0);
+}
+
+attrib_t *
+attrib_merge_remove(attrib_t *eatt, attrib_t *natt, lst_t *errlst)
+{
+
+	int i, j;
+	attrib_t *att = NULL;
+	attrib_val_t *eatv, *natv;
+	attrib_val_t *ev, *nv1, *nv2;
+	lst_t *values;
+	boolean_t found;
+
+	eatv = eatt->att_value;
+	natv = natt->att_value;
+
+	if (eatv->att_val_type == ATT_VAL_TYPE_NULL &&
+	    natv->att_val_type == ATT_VAL_TYPE_NULL) {
+
+		/* NULL - NULL -> EMPTY */
+		att = attrib_duplicate(eatt);
+		(void) strcpy(att->att_name, "");
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_NULL ||
+	    (eatv->att_val_type == ATT_VAL_TYPE_VALUE &&
+	    natv->att_val_type == ATT_VAL_TYPE_LIST) ||
+	    (eatv->att_val_type == ATT_VAL_TYPE_LIST &&
+	    natv->att_val_type == ATT_VAL_TYPE_VALUE)) {
+
+		/* NULL - X -> ERR, VALUE - LIST -> ERR, LIST - VALUE -> ERR */
+		util_add_errmsg(errlst, gettext(
+		    "Can not remove attribute \"%s\""),
+		    eatt->att_name);
+
+	} else if (natv->att_val_type == ATT_VAL_TYPE_NULL) {
+
+		/* X - NULL -> X */
+		att = attrib_duplicate(eatt);
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_VALUE &&
+	    natv->att_val_type == ATT_VAL_TYPE_VALUE) {
+
+		/* VALUE - VALUE -> {EMPTY | ERR} */
+		if (attrib_val_equal(eatv, natv) == 0) {
+			att = ATT_ALLOC();
+			att->att_name = util_safe_strdup("");
+			att->att_value = ATT_VAL_ALLOC_NULL();
+		} else {
+			util_add_errmsg(errlst, gettext(
+			    "Can not remove attribute \"%s\""),
+			    eatt->att_name);
+		}
+
+	} else if (eatv->att_val_type == ATT_VAL_TYPE_LIST &&
+	    natv->att_val_type == ATT_VAL_TYPE_LIST) {
+		/* LIST - LIST -> {EMPTY | ERR | LIST} */
+		if (attrib_val_equal(eatv, natv) == 0) {
+			att = ATT_ALLOC();
+			att->att_name = util_safe_strdup("");
+			att->att_value = ATT_VAL_ALLOC_NULL();
+			goto out;
+		}
+
+		for (i = 0; i < lst_size(natv->att_val_values); i++) {
+			nv1 = lst_at(natv->att_val_values, i);
+			for (j = 0; j < lst_size(natv->att_val_values);
+			    j++) {
+				nv2 = lst_at(natv->att_val_values, j);
+				if (i != j && attrib_val_equal(nv1, nv2) == 0) {
+					util_add_errmsg(errlst, gettext(
+					    "Duplicate values, can not remove"
+					    " attribute \"%s\""),
+					    eatt->att_name);
+					goto out;
+				}
+			}
+
+			found = B_FALSE;
+			for (j = 0; j < lst_size(eatv->att_val_values);
+			    j++) {
+				ev = lst_at(eatv->att_val_values, j);
+				if (attrib_val_equal(nv1, ev) == 0) {
+					found = B_TRUE;
+					break;
+				}
+			}
+
+			if (!found) {
+				util_add_errmsg(errlst, gettext(
+				    "Value not found, can not remove"
+				    " attribute \"%s\""),
+				    eatt->att_name);
+				goto out;
+			}
+		}
+
+		values = util_safe_malloc(sizeof (lst_t));
+		lst_create(values);
+		for (i = 0; i < lst_size(eatv->att_val_values); i++) {
+			ev = lst_at(eatv->att_val_values, i);
+			found = B_FALSE;
+			for (j = 0; j < lst_size(natv->att_val_values);
+			    j++) {
+				nv1 = lst_at(natv->att_val_values, j);
+				if (attrib_val_equal(ev, nv1) == 0) {
+					found = B_TRUE;
+					break;
+				}
+			}
+
+			if (!found) {
+				lst_insert_tail(values,
+				    attrib_val_duplicate(ev));
+			}
+		}
+		att = ATT_ALLOC();
+		att->att_name = util_safe_strdup(eatt->att_name);
+		att->att_value = ATT_VAL_ALLOC_LIST(values);
+	}
+
+out:
+	return (att);
+}
+
+attrib_t *
+attrib_merge(attrib_t *eatt, attrib_t *natt, int flags, lst_t *errlst)
+{
+	attrib_t *att = NULL;
+
+	VERIFY(SEQU(eatt->att_name, natt->att_name));
+
+	if (flags & F_MOD_ADD) {
+		att = attrib_merge_add(eatt, natt);
+	} else if (flags & F_MOD_REM) {
+		att = attrib_merge_remove(eatt, natt, errlst);
+	}
+
+	return (att);
+}
+
+void
+attrib_merge_attrib_lst(lst_t **eattrs, lst_t *nattrs, int flags,
+    lst_t *errlst)
+{
+
+	lst_t *attrs = NULL;
+	int i, j;
+	attrib_t *att, *natt, *eatt;
+	boolean_t found;
+
+	if (flags & F_MOD_ADD) {
+		attrs = util_safe_malloc(sizeof (lst_t));
+		lst_create(attrs);
+
+		for (i = 0; i < lst_size(*eattrs); i++) {
+			eatt = lst_at(*eattrs, i);
+			found = B_FALSE;
+			for (j = 0; j < lst_size(nattrs); j++) {
+				natt = lst_at(nattrs, j);
+				if (SEQU(eatt->att_name, natt->att_name)) {
+					found = B_TRUE;
+					break;
+				}
+			}
+
+			att = found ? attrib_merge(eatt, natt, flags, errlst) :
+			    attrib_duplicate(eatt);
+			if (att == NULL) {
+				attrib_free_lst(attrs);
+				UTIL_FREE_SNULL(attrs);
+				goto out;
+			}
+			lst_insert_tail(attrs, att);
+		}
+
+		for (i = 0; i < lst_size(nattrs); i++) {
+			natt = lst_at(nattrs, i);
+			found = B_FALSE;
+			for (j = 0; j < lst_size(*eattrs); j++) {
+				eatt = lst_at(*eattrs, j);
+				if (SEQU(natt->att_name, eatt->att_name)) {
+					found = B_TRUE;
+					break;
+				}
+			}
+			if (found)
+				continue;
+			lst_insert_tail(attrs, attrib_duplicate(natt));
+		}
+
+	} else if (flags & (F_MOD_REM | F_MOD_SUB)) {
+
+		for (i = 0; i < lst_size(nattrs); i++) {
+			natt = lst_at(nattrs, i);
+			for (j = 0; j < lst_size(nattrs); j++) {
+				att = lst_at(nattrs, j);
+				if (SEQU(natt->att_name, att->att_name) &&
+				    i != j) {
+					util_add_errmsg(errlst, gettext(
+					    "Duplicate Attributes \"%s\""),
+					    natt->att_name);
+					goto out;
+				}
+			}
+
+			found = B_FALSE;
+			for (j = 0; j < lst_size(*eattrs); j++) {
+				eatt = lst_at(*eattrs, j);
+				if (SEQU(eatt->att_name, natt->att_name)) {
+					found = B_TRUE;
+					break;
+				}
+			}
+			if (!found) {
+				util_add_errmsg(errlst, gettext(
+				    "Project does not contain \"%s\""),
+				    natt->att_name);
+				goto out;
+			}
+		}
+
+		attrs = util_safe_malloc(sizeof (lst_t));
+		lst_create(attrs);
+
+		for (i = 0; i < lst_size(*eattrs); i++) {
+			eatt = lst_at(*eattrs, i);
+			found = B_FALSE;
+			for (j = 0; j < lst_size(nattrs); j++) {
+				natt = lst_at(nattrs, j);
+				if (SEQU(eatt->att_name, natt->att_name)) {
+					found = B_TRUE;
+					break;
+				}
+			}
+
+			if (flags & F_MOD_REM) {
+				att = found ?
+				    attrib_merge(eatt, natt, flags, errlst) :
+				    attrib_duplicate(eatt);
+			} else if (flags & F_MOD_SUB) {
+				att = attrib_duplicate(found ? natt : eatt);
+			}
+
+			if (att == NULL) {
+				attrib_free_lst(attrs);
+				UTIL_FREE_SNULL(attrs);
+				goto out;
+			} else if (SEQU(att->att_name, "")) {
+				attrib_free(att);
+			} else {
+				lst_insert_tail(attrs, att);
+			}
+		}
+
+	} else if (flags & F_MOD_REP) {
+		attrs = util_safe_malloc(sizeof (lst_t));
+		lst_create(attrs);
+		for (i = 0; i < lst_size(nattrs); i++) {
+			natt = lst_at(nattrs, i);
+			lst_insert_tail(attrs, attrib_duplicate(natt));
+		}
+	}
+out:
+	if (attrs != NULL) {
+		attrib_free_lst(*eattrs);
+		free(*eattrs);
+		*eattrs = attrs;
+	}
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/attrib.h	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,28 @@
+#ifndef	_PROJENT_ATTRIB_H
+#define	_PROJENT_ATTRIB_H
+
+
+#include <sys/types.h>
+#include <regex.h>
+#include <project.h>
+#include <sys/varargs.h>
+
+#include "projent.h"
+#include "lst.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+extern char *attrib_lst_tostring(lst_t *);
+extern lst_t *attrib_parse_attributes(char *, int, lst_t *);
+extern void attrib_free_lst(lst_t *);
+extern char *attrib_tostring(void *);
+extern void attrib_sort_lst(lst_t *);
+extern int attrib_validate_lst(lst_t *, lst_t *);
+extern void attrib_merge_attrib_lst(lst_t **, lst_t *, int, lst_t *);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* _PROJENT_ATTRIB_H */
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/lst.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include "lst.h"
+#include "util.h"
+
+
+void
+lst_create(lst_t *plst)
+{
+	plst->csz = 0;
+	plst->tsz = 0;
+	plst->buf = NULL;
+}
+
+int
+lst_is_empty(lst_t *plst)
+{
+	return (plst->csz == 0);
+}
+
+void
+lst_insert_head(lst_t *plst, void *ndata)
+{
+	int i;
+	if (plst->csz == plst->tsz) {
+		plst->tsz = (plst->tsz == 0) ? 1 : plst->tsz * 2;
+		plst->buf = util_safe_realloc(plst->buf,
+		    plst->tsz * sizeof (void *));
+	}
+
+	for (i = plst->csz; i > 0; i--)
+		plst->buf[i] = plst->buf[i - 1];
+
+	plst->buf[0] = ndata;
+	plst->csz++;
+}
+
+
+void
+lst_insert_tail(lst_t *plst, void *ndata)
+{
+	if (plst->csz == plst->tsz) {
+		plst->tsz = (plst->tsz == 0) ? 1 : plst->tsz * 2;
+		plst->buf = util_safe_realloc(plst->buf,
+		    plst->tsz * sizeof (void *));
+	}
+
+	plst->buf[plst->csz++] = ndata;
+}
+
+int
+lst_remove(lst_t *plst, void *rdata)
+{
+	int i, idx = -1;
+	for (i = 0; i < plst->csz; i++) {
+		if (plst->buf[i] == rdata) {
+			idx = i;
+			break;
+		}
+	}
+	if (idx >= 0) {
+		for (i = idx; i < plst->csz - 1; i++)
+			plst->buf[i] = plst->buf[i + 1];
+
+		if (--plst->csz == 0) {
+			plst->tsz = 0;
+			free(plst->buf);
+			plst->buf = NULL;
+		}
+		return (0);
+	}
+	return (-1);
+}
+
+void *
+lst_at(lst_t *plst, int idx)
+{
+	if (idx < 0 || idx >= plst->csz) {
+		(void) fprintf(stderr, gettext(
+		    "error accessing element outside lst\n"));
+		exit(1);
+	}
+	return (plst->buf[idx]);
+}
+
+void *
+lst_replace_at(lst_t *plst, int idx, void *ndata)
+{
+	void *odata;
+
+	if (idx < 0 || idx >= plst->csz) {
+		(void) fprintf(stderr, gettext(
+		    "error accessing element outside lst\n"));
+		exit(1);
+	}
+	odata = plst->buf[idx];
+	plst->buf[idx] = ndata;
+	return (odata);
+}
+
+int
+lst_size(lst_t *plst)
+{
+	return (plst->csz);
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/lst.h	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,37 @@
+#ifndef	_PROJENT_LST_H
+#define	_PROJENT_LST_H
+
+
+/*
+ * #include <stdlib.h>
+ */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+
+typedef struct lst_s {
+	int csz;
+	int tsz;
+	void **buf;
+} lst_t;
+
+
+void lst_create(lst_t *);
+int lst_is_empty(lst_t *);
+void lst_insert_head(lst_t *, void *);
+void lst_insert_tail(lst_t *, void *);
+
+int lst_remove(lst_t *, void *);
+void *lst_at(lst_t *, int);
+void *lst_replace_at(lst_t *, int, void *);
+int lst_size(lst_t *);
+
+
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* _PROJENT_LST_H */
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/projent.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,830 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <stddef.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <rctl.h>
+#include <regex.h>
+#include <ctype.h>
+
+#include "projent.h"
+#include "attrib.h"
+#include "util.h"
+
+
+#define	BOSTR_REG_EXP	"^"
+#define	EOSTR_REG_EXP	"$"
+#define	IDENT_REG_EXP	"[[:alpha:]][[:alnum:]_.-]*"
+#define	PRJID_REG_EXP	"[[:digit:]]+"
+#define	USERN_REG_EXP	"!?[[:alpha:]][[:alnum:]_.-]*"
+#define	GRUPN_REG_EXP	"!?[[:alnum:]][[:alnum:]]*"
+
+#define	TO_EXP(X)	BOSTR_REG_EXP X EOSTR_REG_EXP
+
+#define	PROJN_EXP	TO_EXP(IDENT_REG_EXP)
+#define	PRJID_EXP	TO_EXP(PRJID_REG_EXP)
+#define	USERN_EXP	TO_EXP(USERN_REG_EXP)
+#define	GRUPN_EXP	TO_EXP(GRUPN_REG_EXP)
+
+/*ARGSUSED*/
+int
+projent_validate_name(char *pname, lst_t *errlst)
+{
+	/* Do nothing, as any parse-able project name is valid */
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+projent_validate_comment(char *comment, lst_t *errlst)
+{
+	/* Do nothing, as any parse-able project name is valid */
+	return (0);
+}
+
+int
+projent_validate_users(char *users, lst_t *errlst)
+{
+	char *susrs, *usrs, *usr;
+	char *u, **ulast, **ulist;
+	int ret = 0;
+	int i;
+
+	susrs = usrs = util_safe_strdup(users);
+	ulast = ulist = util_safe_zmalloc(
+	    (strlen(users) + 1) * sizeof (char *));
+	while ((usr = strsep(&usrs, ",")) != NULL) {
+		if (*usr == '!')
+			usr++;
+		if ((*usr == '\0') || (strcmp(usr, "*") == 0))
+			continue;
+
+		if (getpwnam(usr) == NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "User \"%s\" does not exist"), usr);
+			ret = 1;
+		}
+		for (i = 0; (u = ulist[i]) != NULL; i++) {
+			if (strcmp(u, usr) == 0) {
+				util_add_errmsg(errlst, gettext(
+				    "Duplicate user names \"%s\""), usr);
+				ret = 1;
+			}
+		}
+		/* Add the user to the temporary list if not found */
+		if (u == NULL) {
+			*ulast++ = usr;
+		}
+	}
+	free(ulist);
+	free(susrs);
+	return (ret);
+}
+
+int
+projent_validate_groups(char *groups, lst_t *errlst)
+{
+	char *sgrps, *grps, *grp;
+	char *g, **glast, **glist;
+	int ret = 0;
+	int i;
+
+	sgrps = grps = util_safe_strdup(groups);
+	glast = glist = util_safe_zmalloc(
+	    (strlen(groups) + 1) * sizeof (char *));
+	while ((grp = strsep(&grps, ",")) != NULL) {
+		if (*grp == '!')
+			grp++;
+		if ((*grp == '\0') || (strcmp(grp, "*") == 0))
+			continue;
+
+		if (getgrnam(grp) == NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "Group \"%s\" does not exist"), grp);
+			ret = 1;
+		}
+		for (i = 0; (g = glist[i]) != NULL; i++) {
+			if (strcmp(g, grp) == 0) {
+				util_add_errmsg(errlst, gettext(
+				    "Duplicate group names \"%s\""), grp);
+				ret = 1;
+			}
+		}
+		/* Add the group to the temporary list if not found */
+		if (g == NULL) {
+			*glast++ = grp;
+		}
+	}
+	free(glist);
+	free(sgrps);
+	return (ret);
+}
+
+int
+projent_validate_attributes(lst_t *attrs, lst_t *errlst)
+{
+	return (attrib_validate_lst(attrs, errlst));
+}
+
+char *
+projent_tostring(projent_t *ent)
+{
+	char *attrs;
+	char *ret = NULL;
+	if ((attrs = attrib_lst_tostring(ent->attrs)) != NULL) {
+		(void) asprintf(&ret, "%s:%ld:%s:%s:%s:%s",
+		    ent->projname,
+		    ent->projid,
+		    ent->comment,
+		    ent->userlist,
+		    ent->grouplist,
+		    attrs);
+		free(attrs);
+	}
+	return (ret);
+}
+
+int
+projent_validate(projent_t *pent, int flags, lst_t *errlst)
+{
+	char *str;
+
+	(void) projent_validate_name(pent->projname, errlst);
+	(void) projent_validate_projid(pent->projid, flags, errlst);
+	(void) projent_validate_comment(pent->comment, errlst);
+	(void) projent_validate_users(pent->userlist, errlst);
+	(void) projent_validate_groups(pent->grouplist, errlst);
+	(void) projent_validate_attributes(pent->attrs, errlst);
+
+	if ((str = projent_tostring(pent)) == NULL) {
+		util_add_errmsg(errlst, gettext("error allocating memory"));
+	} else {
+		if (strlen(str) > (PROJECT_BUFSZ - 2)) {
+			util_add_errmsg(errlst, gettext(
+			    "projent line too long"));
+		}
+		free(str);
+	}
+	return (lst_is_empty(errlst) == 0);
+}
+
+int
+projent_validate_lst(lst_t *plst, int flags, lst_t *errlst)
+{
+	int e, i, idx;
+	projent_t *ent;
+	char *pnames = NULL;
+	projid_t *pids = NULL;
+	int ret = 0;
+
+	idx = 0;
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		/* Check for duplicate projname */
+		if (pnames != NULL && strstr(pnames, ent->projname) != NULL) {
+			util_add_errmsg(errlst, gettext(
+			    "Duplicate project name %s"), ent->projname);
+			ret++;
+		}
+
+		/* Check for duplicate projid if DUP is not allowed */
+		if (!(flags & F_PAR_DUP) && pids != NULL) {
+			for (i = 0; i < idx; i++) {
+				if (ent->projid == pids[i]) {
+					util_add_errmsg(errlst, gettext(
+					    "Duplicate proid %d"), ent->projid);
+					ret++;
+					break;
+				}
+			}
+		}
+
+		/* Add the projname an projid to out temp list */
+		pnames = UTIL_STR_APPEND2(pnames, "|", ent->projname);
+		pids = util_safe_realloc(pids, (idx + 1) * sizeof (projid_t));
+		pids[idx] = ent->projid;
+		idx++;
+
+		/* Validate the projet */
+		ret += projent_validate(ent, flags, errlst);
+	}
+
+	free(pnames);
+	free(pids);
+
+	return (ret);
+}
+
+void
+projent_free_attributes(lst_t *attribs)
+{
+	attrib_free_lst(attribs);
+}
+
+void
+projent_sort_attributes(lst_t *attribs)
+{
+	attrib_sort_lst(attribs);
+}
+
+char *
+projent_attrib_tostring(void *attrib)
+{
+	return (attrib_tostring(attrib));
+}
+
+char *
+projent_attrib_lst_tostring(lst_t *lst)
+{
+	return (attrib_lst_tostring(lst));
+}
+
+void
+projent_merge_attributes(lst_t **eattrs, lst_t *nattrs, int flags,
+    lst_t *errlst)
+{
+	attrib_merge_attrib_lst(eattrs, nattrs, flags, errlst);
+}
+
+lst_t *
+projent_parse_attributes(char *attribs, int flags, lst_t *errlst)
+{
+	return (attrib_parse_attributes(attribs, flags, errlst));
+}
+
+void
+projent_merge_usrgrp(char *usrgrp, char **elist, char *nlist,
+    int flags, lst_t *errlst)
+{
+	char *res = NULL;
+	char *seusrs, *eusrs, *eusr;
+	char *snusrs, *nusrs, *nusr;
+	char *sn1usrs, *n1usrs, *n1usr;
+	char *sep;
+	int i, j;
+
+	sep = (flags & F_PAR_SPC) ? " ," : ",";
+
+	if (flags & F_MOD_ADD) {
+		res = util_safe_strdup(*elist);
+
+		snusrs = nusrs = util_safe_strdup(nlist);
+		while ((nusr = strsep(&nusrs, sep)) != NULL) {
+			if (*nusr == '\0')
+				continue;
+			seusrs = eusrs = util_safe_strdup(*elist);
+			while ((eusr = strsep(&eusrs, sep)) != NULL) {
+				if (*eusr == '\0')
+					continue;
+				if (strcmp(eusr, nusr) == 0) {
+					util_add_errmsg(errlst, gettext(
+					    "Project already contains"
+					    " %s \"%s\""), usrgrp, nusr);
+					UTIL_FREE_SNULL(res);
+					free(seusrs);
+					free(snusrs);
+					goto out;
+				}
+			}
+			free(seusrs);
+			/* Append nusr to the result */
+			if (*res != '\0')
+				res = UTIL_STR_APPEND1(res, ",");
+			res = UTIL_STR_APPEND1(res, nusr);
+		}
+		free(snusrs);
+	} else if (flags & F_MOD_REM) {
+
+		snusrs = nusrs = util_safe_strdup(nlist);
+		for (i = 0; (nusr = strsep(&nusrs, sep)) != NULL; i++) {
+			if (*nusr == '\0')
+				continue;
+			sn1usrs = n1usrs = util_safe_strdup(nlist);
+			for (j = 0; (n1usr = strsep(&n1usrs, sep)) != NULL;
+			    j++) {
+				if (i != j && strcmp(nusr, n1usr) == 0) {
+					util_add_errmsg(errlst, gettext(
+					    "Duplicate %s name \"%s\""),
+					    usrgrp, nusr);
+					free(sn1usrs);
+					free(snusrs);
+					goto out;
+				}
+			}
+			free(sn1usrs);
+
+			seusrs = eusrs = util_safe_strdup(*elist);
+			while ((eusr = strsep(&eusrs, sep)) != NULL) {
+				if (strcmp(nusr, eusr) == 0) {
+					break;
+				}
+			}
+			free(seusrs);
+
+			if (eusr == NULL) {
+				util_add_errmsg(errlst, gettext(
+				    "Project does not contain %s name \"%s\""),
+				    usrgrp, nusr);
+				free(snusrs);
+				goto out;
+			}
+		}
+		free(snusrs);
+
+
+		res = util_safe_zmalloc(1);
+		seusrs = eusrs = util_safe_strdup(*elist);
+		while ((eusr = strsep(&eusrs, sep)) != NULL) {
+			if (*eusr == '\0')
+				continue;
+			snusrs = nusrs = util_safe_strdup(nlist);
+			while ((nusr = strsep(&nusrs, sep)) != NULL) {
+				if (strcmp(eusr, nusr) == 0) {
+					break;
+				}
+			}
+			free(snusrs);
+
+			if (nusr == NULL) {
+				if (*res != '\0')
+					res = UTIL_STR_APPEND1(res, ",");
+				res = UTIL_STR_APPEND1(res, eusr);
+			}
+		}
+		free(seusrs);
+	} else if (flags & F_MOD_SUB || flags & F_MOD_REP) {
+		res = util_safe_strdup(nlist);
+	}
+
+out:
+	if (res != NULL) {
+		free(*elist);
+		*elist = res;
+	}
+}
+
+char *
+projent_parse_users(char *nlist, int flags, lst_t *errlst)
+{
+	char *ulist = NULL;
+	char *susrs, *usrs, *usr;
+	regex_t usernexp;
+	char *sep;
+
+	if (regcomp(&usernexp, USERN_EXP, REG_EXTENDED) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to compile regular expression: \"%s\""),
+		    USERN_EXP);
+		goto out;
+	}
+
+	sep = (flags & F_PAR_SPC) ? " ," : ",";
+	susrs = usrs = util_safe_strdup(nlist);
+	ulist = util_safe_zmalloc(1);
+
+	while ((usr = strsep(&usrs, sep)) != NULL) {
+		if (*usr == '\0')
+			continue;
+
+		if (regexec(&usernexp, usr, 0, NULL, 0) != 0 &&
+		    strcmp(usr, "*") != 0 &&
+		    strcmp(usr, "!*") != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Invalid user name \"%s\""), usr);
+			UTIL_FREE_SNULL(ulist);
+			break;
+		}
+		/* Append ',' first if required */
+		if (*ulist != '\0')
+			ulist = UTIL_STR_APPEND1(ulist, ",");
+		ulist = UTIL_STR_APPEND1(ulist, usr);
+	}
+
+	free(susrs);
+	regfree(&usernexp);
+out:
+	return (ulist);
+}
+
+
+char *
+projent_parse_groups(char *nlist, int flags, lst_t *errlst)
+{
+	char *glist = NULL;
+	char *sgrps, *grps, *grp;
+	regex_t groupnexp;
+	char *sep;
+
+	if (regcomp(&groupnexp, GRUPN_EXP, REG_EXTENDED) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to compile regular expression: \"%s\""),
+		    GRUPN_EXP);
+		goto out;
+	}
+
+	sep = (flags & F_PAR_SPC) ? " ," : ",";
+	sgrps = grps = util_safe_strdup(nlist);
+	glist = util_safe_zmalloc(1);
+
+	while ((grp = strsep(&grps, sep)) != NULL) {
+		if (*grp == '\0')
+			continue;
+
+		if (regexec(&groupnexp, grp, 0, NULL, 0) != 0 &&
+		    strcmp(grp, "*") != 0 &&
+		    strcmp(grp, "!*") != 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Invalid group name \"%s\""), grp);
+			UTIL_FREE_SNULL(glist);
+			break;
+		}
+		/* Append ',' first if required */
+		if (*glist != '\0')
+			glist = UTIL_STR_APPEND1(glist, ",");
+		glist = UTIL_STR_APPEND1(glist, grp);
+	}
+
+	free(sgrps);
+	regfree(&groupnexp);
+out:
+	return (glist);
+}
+
+int
+projent_parse_comment(char *comment, lst_t *errlst)
+{
+	int ret = 0;
+	if (strchr(comment, ':') != NULL) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid Comment \"%s\": should not contain ':'"),
+		    comment);
+		ret = 1;
+	}
+	return (ret);
+}
+
+int
+projent_validate_unique_id(lst_t *plst, projid_t projid, lst_t *errlst)
+{
+	int e;
+	projent_t *ent;
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		if (ent->projid == projid) {
+			util_add_errmsg(errlst, gettext(
+			    "Duplicate projid \"%d\""), projid);
+			return (1);
+		}
+	}
+	return (0);
+}
+int
+projent_validate_projid(projid_t projid, int flags, lst_t *errlst)
+{
+	projid_t maxprojid;
+
+	maxprojid = (flags & F_PAR_RES) ? 0 : 100;
+
+	if (projid < maxprojid) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid projid \"%d\": "
+		    "must be >= 100"),
+		    projid);
+		return (1);
+	}
+	return (0);
+}
+
+int
+projent_parse_projid(char *projidstr, projid_t *pprojid, lst_t *errlst)
+{
+	char *ptr;
+	long long llid;
+	regex_t prjidexp;
+	int ret = 0;
+
+	if (regcomp(&prjidexp, PRJID_EXP, REG_EXTENDED) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to compile regular expression: \"%s\""), PRJID_EXP);
+		return (1);
+	}
+
+
+	if (regexec(&prjidexp, projidstr, 0, NULL, 0) != 0) {
+		util_add_errmsg(errlst, gettext("Invalid project id: \"%s\""),
+		    projidstr);
+		ret = 1;
+		goto out;
+	}
+
+	llid = strtoll(projidstr, &ptr, 10);
+
+	/* projid should be a positive number */
+	if (llid == 0 && errno == ERANGE && *ptr != '\0') {
+		util_add_errmsg(errlst, gettext("Invalid project id: \"%s\""),
+		    projidstr);
+		ret = 1;
+		goto out;
+	}
+
+	/* projid should be a positive number >= 0 */
+	if (llid < 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid projid \"%lld\": must be >= 0"), llid);
+		ret = 1;
+		goto out;
+	}
+
+	/* projid should be less than UID_MAX */
+	if (llid > INT_MAX) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid projid \"%lld\": must be <= %d"),
+		    llid, INT_MAX);
+		ret = 1;
+		goto out;
+	}
+
+	if (pprojid != NULL)
+		*pprojid = llid;
+out:
+	regfree(&prjidexp);
+	return (ret);
+}
+
+int
+projent_validate_unique_name(lst_t *plst, char *pname, lst_t *errlst)
+{
+	int e;
+	projent_t *ent;
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		if (strcmp(ent->projname, pname) == 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Duplicate project name \"%s\""), pname);
+			return (1);
+		}
+	}
+	return (0);
+}
+
+int
+projent_parse_name(char *pname, lst_t *errlst)
+{
+	int ret = 1;
+	regex_t projnexp;
+	if (regcomp(&projnexp, PROJN_EXP, REG_EXTENDED) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to compile regular expression: \"%s\""),
+		    PROJN_EXP);
+		goto out;
+	}
+
+	if (regexec(&projnexp, pname, 0, NULL, 0) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid project name \"%s\", "
+		    "contains invalid characters"), pname);
+	} else if (strlen(pname) > PROJNAME_MAX) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid project name \"%s\", "
+		    "name too long"), pname);
+	} else {
+		ret = 0;
+	}
+
+	regfree(&projnexp);
+out:
+	return (ret);
+}
+
+void
+projent_free(projent_t *ent)
+{
+	free(ent->projname);
+	free(ent->comment);
+	free(ent->userlist);
+	free(ent->grouplist);
+	attrib_free_lst(ent->attrs);
+	free(ent->attrs);
+}
+
+projent_t *
+projent_parse_components(char *projname, char *idstr, char *comment,
+    char *users, char *groups, char *attr, int flags, lst_t *errlst)
+{
+	projent_t *ent;
+	int reterr = 0;
+
+	ent = util_safe_zmalloc(sizeof (projent_t));
+
+
+	ent->projname = util_safe_strdup(projname);
+	ent->comment = util_safe_strdup(comment);
+
+	reterr += projent_parse_name(ent->projname, errlst);
+	reterr += projent_parse_projid(idstr, &ent->projid, errlst);
+	reterr += projent_parse_comment(ent->comment, errlst);
+	ent->userlist = projent_parse_users(users, flags, errlst);
+	ent->grouplist = projent_parse_groups(groups, flags, errlst);
+	ent->attrs = projent_parse_attributes(attr, flags, errlst);
+
+	if (reterr > 0 || ent->userlist == NULL ||
+	    ent->grouplist == NULL || ent->attrs == NULL) {
+		projent_free(ent);
+		UTIL_FREE_SNULL(ent);
+	}
+
+	return (ent);
+}
+projent_t *
+projent_parse(char *projstr, int flags, lst_t *errlst)
+{
+	char *str, *sstr;
+	char *projname, *idstr, *comment, *users, *groups, *attrstr;
+	projent_t *ent;
+
+	if (projstr == NULL)
+		return (NULL);
+
+	projname = idstr = comment = users = groups = attrstr = NULL;
+	ent = NULL;
+
+	sstr  = str = util_safe_strdup(projstr);
+
+	if ((projname = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    (idstr = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    (comment = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    (users = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    (groups = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    (attrstr = util_safe_strdup(strsep(&str, ":"))) == NULL ||
+	    strsep(&str, ":") != NULL) {
+		util_add_errmsg(errlst, gettext(
+		    "Incorrect number of fields.  Should have 5 \":\"'s."));
+		goto out;
+	}
+
+	ent = projent_parse_components(projname, idstr, comment, users, groups,
+	    attrstr, flags, errlst);
+out:
+	free(sstr);
+	free(projname); free(idstr); free(comment);
+	free(users); free(groups); free(attrstr);
+	return (ent);
+}
+
+
+void
+projent_free_lst(lst_t *plst)
+{
+	projent_t *ent;
+
+	if (plst == NULL)
+		return;
+
+	while (!lst_is_empty(plst)) {
+		ent = lst_at(plst, 0);
+		(void) lst_remove(plst, ent);
+		projent_free(ent);
+		free(ent);
+	}
+}
+
+
+void
+projent_put_lst(char *projfile, lst_t *plst, lst_t *errlst)
+{
+	char *tmpprojfile, *attrs;
+	FILE *fp;
+	projent_t *ent;
+	struct stat statbuf;
+	int e, ret;
+
+	tmpprojfile = NULL;
+	if (asprintf(&tmpprojfile, "%s.%ld_tmp", projfile, getpid()) == -1) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to allocate memory"));
+		goto out;
+	}
+
+	if (stat(projfile, &statbuf) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to access %s: %s"),
+		    projfile, strerror(errno));
+		goto out;
+	}
+	if ((fp = fopen(tmpprojfile, "wx")) == NULL) {
+		util_add_errmsg(errlst, gettext(
+		    "Cannot create %s: %s"),
+		    tmpprojfile, strerror(errno));
+		goto out;
+	}
+
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		attrs = attrib_lst_tostring(ent->attrs);
+		ret = fprintf(fp, "%s:%ld:%s:%s:%s:%s\n", ent->projname,
+		    ent->projid, ent->comment, ent->userlist, ent->grouplist,
+		    attrs);
+		free(attrs);
+		if (ret < 0) {
+			util_add_errmsg(errlst, gettext(
+			    "Failed to write to  %s: %s"),
+			    tmpprojfile, strerror(errno));
+			/* Remove the temporary file and exit */
+			(void) unlink(tmpprojfile);
+			goto out1;
+		}
+	}
+
+	if (chown(tmpprojfile, statbuf.st_uid, statbuf.st_gid) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Cannot set ownership of  %s: %s"),
+		    tmpprojfile, strerror(errno));
+		(void) unlink(tmpprojfile);
+		goto out1;
+	}
+
+	if (rename(tmpprojfile, projfile) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Cannot rename %s to %s : %s"),
+		    tmpprojfile, projfile, strerror(errno));
+		(void) unlink(tmpprojfile);
+		goto out1;
+	}
+
+out1:
+	(void) fclose(fp);
+out:
+	free(tmpprojfile);
+}
+
+lst_t *
+projent_get_lst(char *projfile, int flags, lst_t *errlst)
+{
+	FILE *fp;
+	lst_t *plst;
+	int line = 0;
+	char *buf = NULL, *nlp;
+	size_t cap = 0;
+	projent_t *ent;
+
+	plst = util_safe_malloc(sizeof (lst_t));
+	lst_create(plst);
+
+	if ((fp = fopen(projfile, "r")) == NULL) {
+		if (errno == ENOENT) {
+			/*
+			 * There is no project file,
+			 * return an empty lst
+			 */
+			return (plst);
+		} else {
+			/* Report the error unable to open the file */
+			util_add_errmsg(errlst, gettext(
+			    "Cannot open %s: %s"),
+			    projfile, strerror(errno));
+
+			free(plst);
+			return (NULL);
+		}
+	}
+
+	while ((getline(&buf, &cap, fp)) != -1 && ++line) {
+
+		if ((nlp = strchr(buf, '\n')) != NULL)
+			*nlp = '\0';
+
+		if ((ent = projent_parse(buf, flags, errlst)) != NULL) {
+			lst_insert_tail(plst, ent);
+		} else {
+			/* Report the error */
+			util_add_errmsg(errlst, gettext(
+			    "Error parsing: %s line: %d: \"%s\""),
+			    projfile, line, buf);
+
+			/* free the allocated resources */
+			projent_free_lst(plst);
+			UTIL_FREE_SNULL(plst);
+			goto out;
+		}
+	}
+
+	if (flags & F_PAR_VLD && plst != NULL) {
+		if (projent_validate_lst(plst, flags, errlst) != 0) {
+			projent_free_lst(plst);
+			UTIL_FREE_SNULL(plst);
+		}
+	}
+
+out:
+	(void) fclose(fp);
+	free(buf);
+	return (plst);
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/projent.h	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,65 @@
+#ifndef	_PROJENT_PROJENT_H
+#define	_PROJENT_PROJENT_H
+
+#include <sys/types.h>
+#include <regex.h>
+#include <project.h>
+#include <sys/varargs.h>
+
+#include "lst.h"
+
+#define	F_PAR_VLD	0x0001	/* Run validation after parsing */
+#define	F_PAR_SPC	0x0002	/* Allow spaces between names */
+#define	F_PAR_UNT	0x0004	/* Allow units in attribs values */
+#define	F_PAR_RES	0x0008	/* Allow projid < 100 */
+#define	F_PAR_DUP	0x0010	/* Allow duplicate projids */
+
+#define	F_MOD_ADD	0x0100
+#define	F_MOD_REM	0x0200
+#define	F_MOD_SUB	0x0400
+#define	F_MOD_REP	0x0800
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct projent {
+	char *projname;
+	projid_t projid;
+	char *comment;
+	char *userlist;
+	char *grouplist;
+	lst_t *attrs;
+} projent_t;
+
+
+extern void projent_free(projent_t *);
+extern projent_t *projent_parse(char *, int, lst_t *);
+extern projent_t *projent_parse_components(char *, char *, char *, char *,
+    char *, char *, int, lst_t *);
+extern int projent_validate(projent_t *, int, lst_t *);
+extern lst_t *projent_get_lst(char *, int, lst_t *);
+extern void projent_free_lst(lst_t *);
+extern int  projent_parse_name(char *, lst_t *);
+extern int  projent_validate_unique_name(lst_t *, char *, lst_t *);
+extern int  projent_parse_projid(char *, projid_t *, lst_t *);
+extern int  projent_validate_projid(projid_t, int, lst_t *);
+extern int  projent_validate_unique_id(lst_t *, projid_t, lst_t *);
+extern int  projent_parse_comment(char *, lst_t *);
+extern void projent_merge_usrgrp(char *, char **, char *, int, lst_t *);
+extern char *projent_parse_users(char *, int, lst_t *);
+extern char *projent_parse_groups(char *, int, lst_t *);
+extern void projent_merge_attributes(lst_t **, lst_t *, int, lst_t *);
+extern lst_t *projent_parse_attributes(char *, int, lst_t *);
+extern void projent_sort_attributes(lst_t *);
+extern void projent_free_attributes(lst_t *);
+extern char *projent_attrib_tostring(void *);
+extern char *projent_attrib_lst_tostring(lst_t *);
+extern void projent_put_lst(char *, lst_t *, lst_t *);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* _PROJENT_PROJENT_H */
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/resctl.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,169 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <stddef.h>
+#include <limits.h>
+#include <rctl.h>
+#include <regex.h>
+#include <ctype.h>
+#include <zone.h>
+#include <pool.h>
+#include <sys/pool_impl.h>
+#include <unistd.h>
+#include <stropts.h>
+
+#include "util.h"
+#include "resctl.h"
+
+sig_t sigs[SIGS_CNT] = {
+	/* Signal names */
+	{"ABRT", RESCTL_SIG_ABRT},
+	{"XRES", RESCTL_SIG_XRES},
+	{"HUP",  RESCTL_SIG_HUP},
+	{"STOP", RESCTL_SIG_STOP},
+	{"TERM", RESCTL_SIG_TERM},
+	{"KILL", RESCTL_SIG_KILL},
+	{"XFSZ", RESCTL_SIG_XFSZ},
+	{"XCPU", RESCTL_SIG_XCPU},
+
+	/* Singnal numbers */
+	{"6",  RESCTL_SIG_ABRT},
+	{"38", RESCTL_SIG_XRES},
+	{"1",  RESCTL_SIG_HUP},
+	{"23", RESCTL_SIG_STOP},
+	{"15", RESCTL_SIG_TERM},
+	{"9",  RESCTL_SIG_KILL},
+	{"31", RESCTL_SIG_XFSZ},
+	{"30", RESCTL_SIG_XCPU},
+};
+
+/*
+ * Check the existance of a resource pool in the system
+ */
+int
+resctl_pool_exist(char *name)
+{
+	pool_conf_t *conf;
+	pool_status_t status;
+	int fd;
+
+	/*
+	 * Determine if pools are enabled using /dev/pool, as
+	 * libpool may not be present.
+	 */
+	if (getzoneid() != GLOBAL_ZONEID ||
+	    (fd = open("/dev/pool", O_RDONLY)) < 0) {
+		return (1);
+	}
+
+	if (ioctl(fd, POOL_STATUS, &status) < 0) {
+		(void) close(fd);
+		return (1);
+	}
+
+	(void) close(fd);
+
+	if (status.ps_io_state != 1)
+		return (1);
+
+	/* If pools are enabled, assume libpool is present. */
+	if ((conf = pool_conf_alloc()) == NULL)
+		return (1);
+
+	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)) {
+		pool_conf_free(conf);
+		return (1);
+	}
+
+	if (pool_get_pool(conf, name) == NULL) {
+		(void) pool_conf_close(conf);
+		pool_conf_free(conf);
+		return (1);
+	}
+
+	(void) pool_conf_close(conf);
+	pool_conf_free(conf);
+	return (0);
+}
+
+int
+resctl_get_info(char *name, resctl_info_t *pinfo)
+{
+	rctlblk_t *blk1, *blk2, *tmp;
+	rctl_priv_t priv;
+	int ret = 1;
+
+	blk1 = blk2 = tmp = NULL;
+	blk1 = util_safe_malloc(rctlblk_size());
+	blk2 = util_safe_malloc(rctlblk_size());
+
+	if (getrctl(name, NULL, blk1, RCTL_FIRST) == 0) {
+		priv = rctlblk_get_privilege(blk1);
+		while (priv != RCPRIV_SYSTEM) {
+			tmp = blk2;
+			blk2 = blk1;
+			blk1 = tmp;
+			if (getrctl(name, blk2, blk1, RCTL_NEXT) != 0) {
+				goto out;
+			}
+			priv = rctlblk_get_privilege(blk1);
+		}
+
+		pinfo->value = rctlblk_get_value(blk1);
+		pinfo->flags = rctlblk_get_global_flags(blk1);
+		ret = 0;
+	}
+
+out:
+	free(blk1);
+	free(blk2);
+	return (ret);
+}
+
+void
+resctl_get_rule(resctl_info_t *pinfo, resctlrule_t *prule)
+{
+
+	prule->resctl_max = pinfo->value;
+	if (pinfo->flags & RCTL_GLOBAL_BYTES) {
+		prule->resctl_type = RESCTL_TYPE_BYTES;
+	} else if (pinfo->flags & RCTL_GLOBAL_SECONDS) {
+		prule->resctl_type = RESCTL_TYPE_SCNDS;
+	} else if (pinfo->flags & RCTL_GLOBAL_COUNT) {
+		prule->resctl_type = RESCTL_TYPE_COUNT;
+	} else {
+		prule->resctl_type = RESCTL_TYPE_UNKWN;
+	}
+
+	if (pinfo->flags & RCTL_GLOBAL_NOBASIC) {
+		prule->resctl_privs = RESCTL_PRIV_PRIVE | RESCTL_PRIV_PRIVD;
+	} else {
+		prule->resctl_privs = RESCTL_PRIV_ALLPR;
+	}
+
+	if (pinfo->flags & RCTL_GLOBAL_DENY_ALWAYS) {
+		prule->resctl_action = RESCTL_ACTN_DENY;
+	} else if (pinfo->flags & RCTL_GLOBAL_DENY_NEVER) {
+		prule->resctl_action = RESCTL_ACTN_NONE;
+	} else {
+		prule->resctl_action = RESCTL_ACTN_NONE | RESCTL_ACTN_DENY;
+	}
+
+	if (pinfo->flags & RCTL_GLOBAL_SIGNAL_NEVER) {
+		prule->resctl_sigs = 0;
+	} else {
+		prule->resctl_action |= RESCTL_ACTN_SIGN;
+		prule->resctl_sigs = RESCTL_SIG_CMN;
+		if (pinfo->flags & RCTL_GLOBAL_CPU_TIME) {
+			prule->resctl_sigs |= RESCTL_SIG_XCPU;
+		}
+		if (pinfo->flags & RCTL_GLOBAL_FILE_SIZE) {
+			prule->resctl_sigs |= RESCTL_SIG_XFSZ;
+		}
+	}
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/resctl.h	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,69 @@
+#ifndef	_PROJENT_RESCTL_H
+#define	_PROJENT_RESCTL_H
+
+
+/*
+ * Put includes here if needed to be.
+ */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+
+#define	RESCTL_TYPE_UNKWN	0x00
+#define	RESCTL_TYPE_BYTES	0x01
+#define	RESCTL_TYPE_SCNDS	0x02
+#define	RESCTL_TYPE_COUNT	0x03
+
+#define	RESCTL_PRIV_PRIVE	0x01
+#define	RESCTL_PRIV_PRIVD	0x02
+#define	RESCTL_PRIV_BASIC	0x04
+#define	RESCTL_PRIV_ALLPR	0x07
+
+#define	RESCTL_ACTN_NONE	0x01
+#define	RESCTL_ACTN_DENY	0x02
+#define	RESCTL_ACTN_SIGN	0x04
+#define	RESCTL_ACTN_ALLA	0x07
+
+#define	RESCTL_SIG_ABRT		0x01
+#define	RESCTL_SIG_XRES		0x02
+#define	RESCTL_SIG_HUP		0x04
+#define	RESCTL_SIG_STOP		0x08
+#define	RESCTL_SIG_TERM		0x10
+#define	RESCTL_SIG_KILL		0x20
+#define	RESCTL_SIG_XFSZ		0x40
+#define	RESCTL_SIG_XCPU		0x80
+
+#define	RESCTL_SIG_CMN		0x3f
+#define	RESCTL_SIG_ALL		0xff
+
+typedef struct sig_s {
+	char sig[6];
+	int mask;
+} sig_t;
+
+#define	SIGS_CNT	16
+extern sig_t sigs[SIGS_CNT];
+
+typedef struct resctlrule_s {
+	uint64_t resctl_max;
+	uint8_t  resctl_type;
+	uint8_t  resctl_privs;
+	uint8_t  resctl_action;
+	uint8_t  resctl_sigs;
+} resctlrule_t;
+
+typedef struct resctl_info_s {
+	unsigned long long value;
+	int flags;
+} resctl_info_t;
+
+extern int resctl_pool_exist(char *);
+extern int resctl_get_info(char *, resctl_info_t *);
+extern void resctl_get_rule(resctl_info_t *, resctlrule_t *);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* _PROJENT_RESCTL_H */
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/util.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,333 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <stddef.h>
+#include <limits.h>
+
+#include <project.h>
+
+#include <ctype.h>
+
+#include "util.h"
+
+
+
+#define	BOSTR_REG_EXP	"^"
+#define	EOSTR_REG_EXP	"$"
+#define	FLTNM_REG_EXP	"([[:digit:]]+(\\.[[:digit:]]+)?)"
+#define	MODIF_REG_EXP	"([kmgtpeKMGTPE])?"
+#define	UNIT__REG_EXP	"([bsBS])?"
+#define	TOKEN_REG_EXP	"[[:alnum:]_./=+-]*"
+#define	VALUE_REG_EXP	FLTNM_REG_EXP MODIF_REG_EXP UNIT__REG_EXP
+
+#define	TO_EXP(X)	BOSTR_REG_EXP X EOSTR_REG_EXP
+#define	TOKEN_EXP	TO_EXP(TOKEN_REG_EXP)
+#define	VALUE_EXP	TO_EXP(VALUE_REG_EXP)
+
+#define	BYTES_SCALE	1
+#define	SCNDS_SCALE	2
+
+char *
+util_safe_strdup(char *str)
+{
+	char *ptr;
+	if (str == NULL)
+		return (NULL);
+
+	if ((ptr = strdup(str)) == NULL) {
+		(void) fprintf(stderr, gettext("error allocating memory"));
+		exit(1);
+	}
+	return (ptr);
+}
+
+void *
+util_safe_realloc(void *ptr, size_t sz)
+{
+	if ((ptr = realloc(ptr, sz)) == NULL) {
+		(void) fprintf(stderr, gettext(
+		    "error reallocating %d bytes of memory"), sz);
+		exit(1);
+	}
+	return (ptr);
+}
+
+
+void *
+util_safe_malloc(size_t sz)
+{
+	char *ptr;
+	if ((ptr = malloc(sz)) == NULL) {
+		(void) fprintf(stderr, gettext(
+		    "error allocating %d bytes of memory\n"), sz);
+		exit(1);
+	}
+	return (ptr);
+}
+
+void *
+util_safe_zmalloc(size_t sz)
+{
+	return (memset(util_safe_malloc(sz), 0, sz));
+}
+
+void
+util_print_errmsgs(lst_t *errlst)
+{
+	char *errmsg;
+	while (!lst_is_empty(errlst)) {
+		errmsg = lst_at(errlst, 0);
+		(void) lst_remove(errlst, errmsg);
+		(void) fprintf(stderr, "%s\n", errmsg);
+		free(errmsg);
+	}
+}
+
+
+void
+util_add_errmsg(lst_t *errlst, char *format, ...)
+{
+	va_list args;
+	char *errmsg;
+
+	va_start(args, format);
+	if (vasprintf(&errmsg, format, args) < 0) {
+		va_end(args);
+		(void) fprintf(stderr, gettext(
+		    "error allocating memory\n"));
+		exit(1);
+	}
+	va_end(args);
+	lst_insert_tail(errlst, errmsg);
+}
+
+char *
+util_str_append(char *str, int nargs, ...)
+{
+	va_list ap;
+	int i, len;
+	char *s;
+
+	if (str == NULL)
+		str = util_safe_zmalloc(1);
+
+	len = strlen(str) + 1;
+	va_start(ap, nargs);
+	for (i = 0; i < nargs; i++) {
+		s = va_arg(ap, char *);
+		len += strlen(s);
+		str = util_safe_realloc(str, len);
+		(void) strcat(str, s);
+	}
+	va_end(ap);
+	return (str);
+}
+
+char *
+util_substr(regex_t *reg, regmatch_t *mat, char *str, int idx)
+{
+	int mat_len;
+	char *ret;
+
+	if (idx < 0 || idx > reg->re_nsub)
+		return (NULL);
+
+	mat_len = mat[idx].rm_eo - mat[idx].rm_so;
+
+	ret = util_safe_malloc(mat_len + 1);
+	*ret = '\0';
+
+	(void) strlcpy(ret, str + mat[idx].rm_so, mat_len + 1);
+	return (ret);
+}
+
+typedef struct {
+	char unit;
+	uint64_t val;
+} scl;
+
+#define	SCLS	7
+
+int
+util_scale(char *unit, int scale, uint64_t *res, lst_t *errlst)
+{
+	int i;
+	scl *sc;
+	scl bscale[SCLS] = {
+		{'\0', 1ULL},
+		{'k', 1024ULL},
+		{'m', 1048576ULL},
+		{'g', 1073741824ULL},
+		{'t', 1099511627776ULL},
+		{'p', 1125899906842624ULL},
+		{'e', 1152921504606846976ULL},
+	};
+
+	scl oscale[SCLS] = {
+		{'\0', 1ULL},
+		{'k', 1000ULL},
+		{'m', 1000000ULL},
+		{'g', 1000000000ULL},
+		{'t', 1000000000000ULL},
+		{'p', 1000000000000000ULL},
+		{'e', 1000000000000000000ULL},
+	};
+
+	sc = (scale == BYTES_SCALE) ? bscale : oscale;
+
+	for (i = 0; i < SCLS; i++) {
+		if (tolower(*unit) == sc[i].unit) {
+			*res = sc[i].val;
+			return (0);
+		}
+	}
+
+	util_add_errmsg(errlst, gettext(
+	    "Invalid scale: %d"), scale);
+	return (1);
+}
+
+
+int
+util_val2num(char *value, int scale, lst_t *errlst, char **retnum,
+    char **retmod, char **retunit)
+{
+	int ret = 1;
+	regex_t valueexp;
+	regmatch_t *mat;
+	int nmatch;
+	char *num, *modifier, *unit;
+	char *ptr;
+
+	uint64_t mul64;
+	long double dnum;
+
+	*retnum = *retmod = *retunit = NULL;
+
+	if (regcomp(&valueexp, VALUE_EXP, REG_EXTENDED) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Failed to compile regex: '%s'"), VALUE_EXP);
+		return (1);
+	}
+
+	nmatch = valueexp.re_nsub + 1;
+	mat = util_safe_malloc(nmatch * sizeof (regmatch_t));
+
+	if (regexec(&valueexp, value, nmatch, mat, 0) != 0) {
+		util_add_errmsg(errlst, gettext(
+		    "Invalid value: '%s'"), value);
+		regfree(&valueexp);
+		return (1);
+	}
+	regfree(&valueexp);
+
+	num = util_substr(&valueexp, mat, value, 1);
+	modifier = util_substr(&valueexp, mat, value, 3);
+	unit = util_substr(&valueexp, mat, value, 4);
+
+	if ((num == NULL || modifier == NULL || unit == NULL) ||
+	    (strlen(modifier) == 0 && strchr(num, '.') != NULL) ||
+	    (util_scale(modifier, scale, &mul64, errlst) != 0) ||
+	    (scale == BYTES_SCALE && *unit != '\0' && tolower(*unit) != 'b') ||
+	    (scale == SCNDS_SCALE && *unit != '\0' && tolower(*unit) != 's') ||
+	    (scale == COUNT_SCALE && *unit != '\0') ||
+	    (scale == UNKWN_SCALE && *unit != '\0'))  {
+		util_add_errmsg(errlst, gettext("Error near: \"%s\""),
+		    value);
+		goto out;
+	}
+
+	dnum = strtold(num, &ptr);
+	if (dnum == 0 &&
+	    (errno == EINVAL || errno == ERANGE) &&
+	    *ptr != '\0') {
+		util_add_errmsg(errlst, gettext("Invalid value:  \"%s\""),
+		    value);
+		goto out;
+	}
+
+	if (UINT64_MAX / mul64 <= dnum) {
+		util_add_errmsg(errlst, gettext("Too big value:  \"%s\""),
+		    value);
+		goto out;
+	}
+
+	if (asprintf(retnum, "%llu",
+	    (unsigned long long)(mul64 * dnum)) == -1) {
+		goto out;
+	}
+
+	free(num);
+	*retmod = modifier;
+	*retunit = unit;
+	return (0);
+out:
+	free(num); free(modifier); free(unit);
+	return (ret);
+}
+
+void
+util_free_tokens(char **tokens)
+{
+	char *token;
+	while ((token = *tokens++) != NULL) {
+		free(token);
+	}
+}
+
+char **
+util_tokenize(char *values, lst_t *errlst)
+{
+	char *token, *t;
+	char *v;
+	regex_t tokenexp;
+	char **ctoken, **tokens = NULL;
+
+	if (regcomp(&tokenexp, TOKEN_EXP, REG_EXTENDED | REG_NOSUB) != 0)
+		return (tokens);
+
+	/* Assume each character will be a token + NULL terminating value. */
+	ctoken = tokens = util_safe_malloc(
+	    (strlen(values) + 1) * sizeof (char *));
+	token = util_safe_malloc(strlen(values) + 1);
+
+	v = values;
+	*ctoken = NULL;
+	while (v < (values + strlen(values))) {
+
+		/* get the next token */
+		t = token;
+		while ((*t = *v++) != '\0') {
+			if (*t == '(' || *t == ')' || *t == ','||
+			    *v == '(' || *v == ')' || *v == ',') {
+				*++t = '\0';
+				break;
+			}
+			t++;
+		}
+
+		if (strcmp(token, "(") != 0 && strcmp(token, ")") != 0 &&
+		    strcmp(token, ",") != 0) {
+			if (regexec(&tokenexp, token, 0, NULL, 0) != 0) {
+				util_add_errmsg(errlst, gettext(
+				    "Invalid Character at or near \"%s\""),
+				    token);
+				util_free_tokens(tokens);
+				UTIL_FREE_SNULL(tokens);
+				goto out1;
+			}
+		}
+		*ctoken++ = util_safe_strdup(token);
+		*ctoken = NULL;
+	}
+
+out1:
+	free(token);
+	regfree(&tokenexp);
+	return (tokens);
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/common/util.h	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,45 @@
+#ifndef	_PROJENT_UTIL_H
+#define	_PROJENT_UTIL_H
+
+#include <sys/types.h>
+#include <regex.h>
+#include <sys/varargs.h>
+
+#include "lst.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* UTIL_STR_APPEND */
+#define	UTIL_STR_APPEND1(S, S1)		util_str_append((S), 1, (S1))
+#define	UTIL_STR_APPEND2(S, S1, S2)	util_str_append((S), 2, (S1), (S2))
+
+/* UTIL_FREE_SNULL */
+#define	UTIL_FREE_SNULL(ptr) {	\
+	free(ptr);		\
+	ptr = NULL;		\
+}
+
+#define	UNKWN_SCALE	0
+#define	BYTES_SCALE	1
+#define	SCNDS_SCALE	2
+#define	COUNT_SCALE	3
+
+
+extern char *util_safe_strdup(char *);
+extern void *util_safe_realloc(void*, size_t);
+extern void *util_safe_malloc(size_t);
+extern void *util_safe_zmalloc(size_t);
+extern char **util_tokenize(char *, lst_t *);
+extern void util_free_tokens(char **);
+extern char *util_substr(regex_t *, regmatch_t *, char *, int);
+extern char *util_str_append(char *, int, ...);
+extern int util_val2num(char *, int, lst_t *, char **, char **, char **);
+extern void util_add_errmsg(lst_t *, char *format, ...);
+extern void util_print_errmsgs(lst_t *);
+
+#ifdef  __cplusplus
+}
+#endif
+#endif /* _PROJENT_UTIL_H */
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/projadd/Makefile	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG = projadd
+OBJS = projadd.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/projadd/Makefile.projadd
+
+LDLIBS += -lpool
+CFLAGS += $(CCVERBOSE) -I${PROJADDCOMMONDIR}
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-parentheses
+
+CPPFLAGS_sparc += -I$(SRC)/uts/sfmmu
+CPPFLAGS_sparc += -I$(SRC)/uts/sun4u/sunfire
+CPPFLAGS += $(CPPFLAGS_$(MACH))
+
+FILEMODE= 0555
+
+lint := LINTFLAGS = -muxs -I$(PROJADDCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o : $(PROJADDCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(PROG) $(OBJS) $(COMMON_OBJS)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/projadd/projadd.c	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,209 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "projent.h"
+#include "util.h"
+
+
+#define	CHECK_ERRORS_FREE_PLST(errlst, plst, attrs, ecode) {	\
+	if (!lst_is_empty(errlst)) {					\
+		util_print_errmsgs(errlst);				\
+		if (plst != NULL) {					\
+			projent_free_lst(plst);				\
+			free(plst);					\
+		}							\
+		free(attrs);						\
+		usage();						\
+		exit(ecode);						\
+	}								\
+}
+
+/*
+ * Print usage
+ */
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "Usage:\n"
+	    "projadd [-n] [-f filename] [-p projid [-o]] [-c comment]\n"
+	    "        [-U user[,user...]] [-G group[,group...]]\n"
+	    "        [-K name[=value[,value...]]] project\n"));
+}
+
+/*
+ * main()
+ */
+int
+main(int argc, char **argv)
+{
+	int e, c;
+
+	extern char *optarg;
+	extern int optind, optopt;
+	projid_t maxpjid = 99;
+	lst_t *plst = NULL;
+	int flags = 0;
+	char *projfile = PROJF_PATH;
+
+	projent_t *ent;
+	boolean_t nflag, pflag, oflag;		/* Command line flags.	*/
+	char *pname, *projidstr, *comment;	/* projent fields.	*/
+	char *users, *groups, *attrs;
+	projid_t projid;
+
+	lst_t errlst;
+
+	/* Project file defaults to system project file "/etc/project" */
+	users = groups = comment = projidstr = "";
+
+	nflag = pflag = oflag = B_FALSE;
+	attrs = util_safe_zmalloc(1);
+	lst_create(&errlst);
+
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"		/* Use this only if it wasn't */
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* Parse the command line argument list */
+	while ((c = getopt(argc, argv, ":hnf:p:oc:U:G:K:")) != EOF)
+		switch (c) {
+			case 'h':
+				usage();
+				exit(0);
+				break;
+			case 'n':
+				nflag = B_TRUE;
+				break;
+			case 'f':
+				projfile = optarg;
+				break;
+			case 'p':
+				pflag = B_TRUE;
+				projidstr = optarg;
+				break;
+			case 'o':
+				oflag = B_TRUE;
+				break;
+			case 'c':
+				comment = optarg;
+				break;
+			case 'U':
+				users = optarg;
+				break;
+			case 'G':
+				groups = optarg;
+				break;
+			case 'K':
+				attrs = UTIL_STR_APPEND2(attrs, ";", optarg);
+				break;
+			default:
+				util_add_errmsg(&errlst, gettext(
+				    "Invalid option: -%c"), optopt);
+				break;
+		}
+
+
+
+	if (oflag && !pflag) {
+		util_add_errmsg(&errlst, gettext(
+		    "-o requires -p projid to be specified"));
+	}
+
+	if (optind != argc -1) {
+		util_add_errmsg(&errlst, gettext("No project name specified"));
+	}
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	if (!nflag)
+		flags |= F_PAR_VLD;
+	flags |= F_PAR_RES | F_PAR_DUP;
+
+	/* Parse the project file to get the list of the projects */
+	plst = projent_get_lst(projfile, flags, &errlst);
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+
+	/* Parse and validate new project id */
+	if (pflag && projent_parse_projid(projidstr, &projid, &errlst) == 0) {
+		if (!nflag) {
+			projent_validate_projid(projid, 0, &errlst);
+			if (!oflag) {
+				projent_validate_unique_id(plst, projid,
+				    &errlst);
+			}
+		}
+	}
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	/* Find the maxprojid */
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		maxpjid = (ent->projid > maxpjid) ? ent->projid : maxpjid;
+	}
+
+
+	if (!pflag && asprintf(&projidstr, "%ld", maxpjid + 1) == -1) {
+		util_add_errmsg(&errlst, gettext("Failed to allocate memory"));
+		CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+	}
+
+	pname = argv[optind];
+	ent = projent_parse_components(pname, projidstr, comment, users,
+	    groups, attrs, F_PAR_SPC | F_PAR_UNT, &errlst);
+	if (!pflag)
+		free(projidstr);
+
+	if (!nflag)
+		projent_validate_unique_name(plst, pname, &errlst);
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	/* Sort attributes list */
+	projent_sort_attributes(ent->attrs);
+
+
+	/* Add the new project entry to the list */
+	lst_insert_tail(plst, ent);
+
+	/* Validate the projent before writing the list to the project file */
+	(void) projent_validate(ent, flags, &errlst);
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	/* Write out the project file */
+	projent_put_lst(projfile, plst, &errlst);
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	return (0);
+}
--- /dev/null	Thu Dec 15 19:51:09 2016
+++ new/usr/src/cmd/projadd/projdel/Makefile	Thu Dec 15 19:51:09 2016
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG = projdel
+OBJS = projdel.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/projadd/Makefile.projadd
+
+LDLIBS += -lpool
+CFLAGS += $(CCVERBOSE) -I${PROJADDCOMMONDIR}
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-parentheses
+
+CPPFLAGS_sparc += -I$(SRC)/uts/sfmmu
+CPPFLAGS_sparc += -I$(SRC)/uts/sun4u/sunfire
+CPPFLAGS += $(CPPFLAGS_$(MACH))
+
+FILEMODE= 0555
+
+lint := LINTFLAGS = -muxs -I$(PROJADDCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o : $(PROJADDCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(PROG) $(OBJS) $(COMMON_OBJS)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Dec 15 19:51:10 2016
+++ new/usr/src/cmd/projadd/projdel/projdel.c	Thu Dec 15 19:51:10 2016
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "projent.h"
+#include "util.h"
+
+#define	SEQU(str1, str2)		(strcmp(str1, str2) == 0)
+
+/*
+ * Print usage
+ */
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "Usage:\n"
+	    "projdel [-f filename] project\n"));
+}
+
+/*
+ * main()
+ */
+int
+main(int argc, char **argv)
+{
+	int e, c, ret = 0;
+	int flags;
+	extern char *optarg;
+	extern int optind, optopt;
+	lst_t *plst;			/* Projects list */
+	projent_t *ent, *delent;
+	int del;
+	char *pname;			/* Project name */
+	lst_t errlst;			/* Errors list */
+	char *projfile = PROJF_PATH;	/* Project file "/etc/project" */
+
+	lst_create(&errlst);
+
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"		/* Use this only if it wasn't */
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* Parse the command line argument list */
+	while ((c = getopt(argc, argv, ":hf:")) != EOF)
+		switch (c) {
+			case 'h':
+				usage();
+				exit(0);
+				break;
+			case 'f':
+				projfile = optarg;
+				break;
+			default:
+				util_add_errmsg(&errlst, gettext(
+				    "Invalid option: -%c"), optopt);
+				break;
+		}
+
+	if (optind != argc -1) {
+		(void) fprintf(stderr, gettext("No project name specified\n"));
+		exit(2);
+	}
+
+	/* Name of the project to delete */
+	pname = argv[optind];
+
+	flags = F_PAR_VLD | F_PAR_RES | F_PAR_DUP;
+
+	/* Parse the project file to get the list of the projects */
+	plst = projent_get_lst(projfile, flags, &errlst);
+
+	if (!lst_is_empty(&errlst)) {
+		util_print_errmsgs(&errlst);
+		usage();
+		exit(2);
+	}
+
+	/* Find the project to be deleted */
+	del = 0;
+	for (e = 0; e < lst_size(plst); e++) {
+		ent = lst_at(plst, e);
+		if (SEQU(ent->projname, pname)) {
+			del++;
+			delent = ent;
+		}
+	}
+
+	if (del == 0) {
+		(void) fprintf(stderr, gettext(
+		    "Project \"%s\" does not exist\n"), pname);
+		usage();
+		ret = 2;
+		goto out;
+	} else if (del > 1) {
+		(void) fprintf(stderr, gettext(
+		    "Duplicate project name \"%s\""), pname);
+		usage();
+		ret = 2;
+		goto out;
+	}
+
+	/* Remove the project entry from the list */
+	(void) lst_remove(plst, delent);
+
+	/* Write out the project file */
+	projent_put_lst(projfile, plst, &errlst);
+
+	if (!lst_is_empty(&errlst)) {
+		util_print_errmsgs(&errlst);
+		usage();
+		ret = 2;
+	}
+out:
+	projent_free_lst(plst);
+	free(plst);
+	return (ret);
+}
--- /dev/null	Thu Dec 15 19:51:10 2016
+++ new/usr/src/cmd/projadd/projmod/Makefile	Thu Dec 15 19:51:10 2016
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG = projmod
+OBJS = projmod.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/projadd/Makefile.projadd
+
+LDLIBS += -lpool -lproject
+CFLAGS += $(CCVERBOSE) -I${PROJADDCOMMONDIR}
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-parentheses
+
+CPPFLAGS_sparc += -I$(SRC)/uts/sfmmu
+CPPFLAGS_sparc += -I$(SRC)/uts/sun4u/sunfire
+CPPFLAGS += $(CPPFLAGS_$(MACH))
+
+FILEMODE= 0555
+
+lint := LINTFLAGS = -muxs -I$(PROJADDCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o : $(PROJADDCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(PROG) $(OBJS) $(COMMON_OBJS)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Dec 15 19:51:10 2016
+++ new/usr/src/cmd/projadd/projmod/projmod.c	Thu Dec 15 19:51:10 2016
@@ -0,0 +1,392 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/task.h>
+
+#include <sys/debug.h>
+
+
+#include "projent.h"
+#include "util.h"
+
+#define	SEQU(str1, str2)		(strcmp(str1, str2) == 0)
+
+#define	CHECK_ERRORS_FREE_PLST(errlst, plst, attrs, ecode) {	\
+	if (!lst_is_empty(errlst)) {					\
+		util_print_errmsgs(errlst);				\
+		if (plst != NULL) {					\
+			projent_free_lst(plst);				\
+			free(plst);					\
+		}							\
+		free(attrs);						\
+		usage();						\
+		exit(ecode);						\
+	}								\
+}
+
+
+/*
+ * Print usage
+ */
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "Usage:\n"
+	    "projmod [-n] [-A|-f filename] [-p projid [-o]] [-c comment]\n"
+	    "        [-a|-s|-r] [-U user[,user...]] [-G group[,group...]]\n"
+	    "        [-K name[=value[,value...]]] [-l new_projectname]\n"
+	    "        project\n"));
+}
+
+
+/*
+ * main()
+ */
+int
+main(int argc, char **argv)
+{
+	int e, c, error;
+
+	extern char *optarg;
+	extern int optind, optopt;
+	lst_t *plst = NULL;
+	int flags = 0;
+	projent_t *ent, *modent;
+
+	/* Command line options */
+	boolean_t fflag, nflag, cflag, oflag, pflag, lflag;
+	boolean_t sflag, rflag, aflag;
+	boolean_t Uflag, Gflag, Kflag, Aflag;
+	boolean_t modify;
+
+	/* Project entry fields */
+	char *pname, *npname;
+	char *comment, *users, *groups, *attrs;
+	char *pusers, *pgroups;
+
+	lst_t *pattribs;
+
+	lst_t errlst;
+
+	/* Project file defaults to system project file "/etc/project" */
+	char *projfile = PROJF_PATH;
+	struct project proj, *projp;
+	char buf[PROJECT_BUFSZ];
+	char *str;
+
+	comment = users = groups = "";
+	pname = npname = NULL;
+
+	fflag = nflag = cflag = oflag = pflag = lflag = B_FALSE;
+	sflag = rflag = aflag = B_FALSE;
+	Uflag = Gflag = Kflag = Aflag = B_FALSE;
+
+	modify = B_FALSE;
+
+
+
+	attrs = util_safe_zmalloc(1);
+	lst_create(&errlst);
+
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* Parse the command line argument list */
+	while ((c = getopt(argc, argv, ":hf:nc:op:l:sraU:G:K:A")) != EOF)
+		switch (c) {
+			case 'h':
+				usage();
+				exit(0);
+				break;
+			case 'f':
+				fflag = B_TRUE;
+				projfile = optarg;
+				break;
+			case 'n':
+				nflag = B_TRUE;
+				break;
+			case 'c':
+				cflag = B_TRUE;
+				comment = optarg;
+				break;
+			case 'o':
+				oflag = B_TRUE;
+				break;
+			case 'p':
+				pflag = B_TRUE;
+				break;
+			case 'l':
+				lflag = B_TRUE;
+				npname = optarg;
+				break;
+			case 's':
+				sflag = B_TRUE;
+				break;
+			case 'r':
+				rflag = B_TRUE;
+				break;
+			case 'a':
+				aflag = B_TRUE;
+				break;
+			case 'U':
+				Uflag = B_TRUE;
+				users = optarg;
+				break;
+			case 'G':
+				Gflag = B_TRUE;
+				groups = optarg;
+				break;
+			case 'K':
+				Kflag = B_TRUE;
+				attrs = UTIL_STR_APPEND2(attrs, ";", optarg);
+				break;
+			case 'A':
+				Aflag = B_TRUE;
+				break;
+			default:
+				util_add_errmsg(&errlst, gettext(
+				    "Invalid option: -%c"), optopt);
+				break;
+		}
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	if (optind == argc - 1) {
+		pname = argv[optind];
+	}
+
+	if (cflag || Gflag || lflag || pflag || Uflag || Kflag || Aflag) {
+		modify = B_TRUE;
+		if (pname == NULL) {
+			util_add_errmsg(&errlst, gettext(
+			    "No project name specified"));
+		}
+	} else if (pname != NULL) {
+		util_add_errmsg(&errlst, gettext(
+		    "missing -c, -G, -l, -p, -U, or -K"));
+	}
+
+	if (Aflag && fflag) {
+		util_add_errmsg(&errlst, gettext(
+		    "-A and -f are mutually exclusive"));
+	}
+
+	if (oflag && !pflag) {
+		util_add_errmsg(&errlst, gettext(
+		    "-o requires -p projid to be specified"));
+	}
+
+	if ((aflag && (rflag || sflag)) || (rflag && (aflag || sflag)) ||
+	    (sflag && (aflag || rflag))) {
+		util_add_errmsg(&errlst, gettext(
+		    "-a, -r, and -s are mutually exclusive"));
+	}
+
+	if ((aflag || rflag || sflag) && !(Uflag || Gflag || Kflag)) {
+		util_add_errmsg(&errlst, gettext(
+		    "-a, -r, and -s require -U users, -G groups "
+		    "or -K attributes to be specified"));
+	}
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	if (aflag) {
+		flags |= F_MOD_ADD;
+	} else if (rflag) {
+		flags |= F_MOD_REM;
+	} else if (sflag) {
+		flags |= F_MOD_SUB;
+	} else {
+		flags |= F_MOD_REP;
+	}
+	if (!nflag) {
+		flags |= F_PAR_VLD;
+	}
+	flags |= F_PAR_RES | F_PAR_DUP;
+
+	plst = projent_get_lst(projfile, flags, &errlst);
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	modent = NULL;
+	if (pname != NULL) {
+		for (e = 0; e < lst_size(plst); e++) {
+			ent = lst_at(plst, e);
+			if (SEQU(ent->projname, pname)) {
+				modent = ent;
+			}
+		}
+		if (modent == NULL) {
+			util_add_errmsg(&errlst, gettext(
+			    "Project \"%s\" does not exist"), pname);
+		}
+	}
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	/*
+	 * If there is no modification options, simply reading the file, which
+	 * includes parsing and verifying, is sufficient.
+	 */
+	if (!modify) {
+		exit(0);
+	}
+
+	VERIFY(modent != NULL);
+
+	if (lflag && projent_parse_name(pname, &errlst) == 0 &&
+	    projent_validate_unique_name(plst, npname, &errlst) == 0) {
+		free(modent->projname);
+		modent->projname = util_safe_strdup(npname);
+	}
+
+	if (cflag && projent_parse_comment(comment, &errlst) == 0) {
+		free(modent->comment);
+		modent->comment = util_safe_strdup(comment);
+	}
+
+	if (Uflag) {
+		pusers = projent_parse_users(users, F_PAR_SPC,
+		    &errlst);
+		if (pusers != NULL) {
+			projent_merge_usrgrp("user", &modent->userlist,
+			    pusers, flags, &errlst);
+			free(pusers);
+		}
+	}
+
+	if (Gflag) {
+		pgroups = projent_parse_groups(groups, F_PAR_SPC,
+		    &errlst);
+		if (pgroups != NULL) {
+			projent_merge_usrgrp("group", &modent->grouplist,
+			    pgroups, flags, &errlst);
+			free(pgroups);
+		}
+	}
+
+	if (Kflag) {
+		pattribs = projent_parse_attributes(attrs, F_PAR_UNT, &errlst);
+		if (pattribs != NULL) {
+			projent_merge_attributes(&modent->attrs,
+			    pattribs, flags, &errlst);
+			projent_sort_attributes(modent->attrs);
+			projent_free_attributes(pattribs);
+			UTIL_FREE_SNULL(pattribs);
+		}
+	}
+
+	if (!nflag) {
+		(void) projent_validate(modent, flags, &errlst);
+	}
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	if (modify) {
+		projent_put_lst(projfile, plst, &errlst);
+	}
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	if (Aflag && (error = setproject(pname, "root",
+	    TASK_FINAL|TASK_PROJ_PURGE)) != 0) {
+		if (error == SETPROJ_ERR_TASK) {
+			if (errno == EAGAIN) {
+				util_add_errmsg(&errlst, gettext(
+				    "resource control limit has been reached"));
+			} else if (errno == ESRCH) {
+				util_add_errmsg(&errlst, gettext(
+				    "user \"%s\" is not a member "
+				    "of project \"%s\""), "root", pname);
+			} else {
+				util_add_errmsg(&errlst, gettext(
+				    "could not join project \"%s\""), pname);
+			}
+		} else if (error == SETPROJ_ERR_POOL) {
+			if (errno == EACCES) {
+				util_add_errmsg(&errlst, gettext(
+				    "no resource pool accepting default "
+				    "bindings exists for project \"%s\""),
+				    pname);
+			} else if (errno == ESRCH) {
+				util_add_errmsg(&errlst, gettext(
+				    "specified resource pool does not exist "
+				    "for project \"%s\""), pname);
+			} else {
+				util_add_errmsg(&errlst, gettext(
+				    "could not bind to default resource pool "
+				    "for project \"%s\""), pname);
+			}
+		} else {
+			/*
+			 * error represents the position - within the
+			 * semi-colon delimited attribute - that generated
+			 * the error.
+			 */
+			if (error <= 0) {
+				util_add_errmsg(&errlst, gettext(
+				    "setproject failed for project \"%s\""),
+				    pname);
+			} else {
+				/* To be completed */
+				projp = getprojbyname(pname, &proj, buf,
+				    sizeof (buf));
+				pattribs = (projp != NULL) ?
+				    projent_parse_attributes(projp->pj_attr,
+				    0, &errlst) : NULL;
+				if (projp != NULL && pattribs != NULL &&
+				    (str = projent_attrib_tostring(
+				    lst_at(pattribs, error - 1))) != NULL) {
+					util_add_errmsg(&errlst, gettext(
+					    "warning, \"%s\" resource control "
+					    "assignment failed for project "
+					    "\"%s\""), str, pname);
+					free(str);
+				} else {
+					util_add_errmsg(&errlst, gettext(
+					    "warning, resource control "
+					    "assignment failed for project "
+					    "\"%s\" attribute %d"), pname,
+					    error);
+				}
+
+				if (pattribs != NULL) {
+					projent_free_attributes(pattribs);
+					UTIL_FREE_SNULL(pattribs);
+				}
+			}
+		}
+	}
+
+	CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
+
+	return (0);
+}
--- /dev/null	Thu Dec 15 19:51:10 2016
+++ new/usr/src/cmd/projadd/projtest/Makefile	Thu Dec 15 19:51:10 2016
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG = projtest
+OBJS = projtest.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/projadd/Makefile.projadd
+
+LDLIBS += -lpool
+CFLAGS += $(CCVERBOSE) -I${PROJADDCOMMONDIR}
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-parentheses
+
+CPPFLAGS_sparc += -I$(SRC)/uts/sfmmu
+CPPFLAGS_sparc += -I$(SRC)/uts/sun4u/sunfire
+CPPFLAGS += $(CPPFLAGS_$(MACH))
+
+FILEMODE= 0555
+
+lint := LINTFLAGS = -muxs -I$(PROJADDCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o : $(PROJADDCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(PROG) $(OBJS) $(COMMON_OBJS)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Dec 15 19:51:10 2016
+++ new/usr/src/cmd/projadd/projtest/projtest.c	Thu Dec 15 19:51:10 2016
@@ -0,0 +1,670 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "projent.h"
+#include "util.h"
+
+/*
+ * All the below test cases were copied from perl test code located at
+ * "usr/src/cmd/perl/contrib/Sun/Solaris/Project/t/Project.t". Part of the
+ * Parse/Validate test cases were commented out because the implementation
+ * of users list parsing is more permissive in this C code compared to the
+ * original perl code. It is more consistent this way that perl way.
+ *
+ * For example, the original perl code assume the below test succeeds
+ *	{ 0, 0, FLAG_000, "comma8:158:::root,,:"},
+ * On the same time, it assumes, the below this test case to fail.
+ *	{ 1, 0, FLAG_000, "comma6:195:::,,root,bin:"},
+ * This C implementations passes both of these cases and neglect the extra
+ * commas in both cases.
+ */
+
+/*
+ * file1, parse error (extra ":") on group.staff project.
+ */
+#define	FILE1 "test1:123:project one:root,bin:adm:attr1=a;attr2=b\n"	\
+"user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q\n"		\
+"group.test3:678:project three::root,nobody:root,lp:attr1=y;attr2=z\n"	\
+"test4:678:project four:root:root:\n"					\
+"test5:679:project five::sys:\n"					\
+"test6:690::::\n"
+
+/*
+ * file2, duplicate project names.
+ */
+#define	FILE2 "test1:123:project one:root,bin:adm:attr1=a;attr2=b\n"	\
+"user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q\n"		\
+"group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z\n"	\
+"test1:678:project four:root:root:\n"					\
+"test5:679:project five::sys:\n"					\
+"test6:690::::\n"
+
+/*
+ * file3, duplicate project ids.
+ */
+#define	FILE3 "test1:123:project one:root,bin:adm:attr1=a;attr2=b\n"	\
+"user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q\n"		\
+"group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z\n"	\
+"test4:678:project four:root:root:\n"					\
+"test5:678:project five::sys:\n"					\
+"test6:690::::\n"
+
+/*
+ * file4, system project ids.
+ */
+#define	FILE4 "system:0::::\n"						\
+"user.root:1::::\n"							\
+"noproject:2::::\n"							\
+"default:3::::\n"							\
+"group.staff:10::::\n"							\
+"test1:123:project one:root,bin:adm:attr1=a;attr2=b\n"			\
+"user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q\n"		\
+"group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z\n"	\
+"test4:678:project four:root:root:\n"					\
+"test5:679:project five::sys:\n"					\
+"test6:690::::\n"
+
+/*
+ * file5, all unique user projects.
+ */
+#define	FILE5 "test1:123:project one:root,bin:adm:attr1=a;attr2=b\n"	\
+"user.test2:456:project two:adm,uucp:staff:attr1=p;attr2=q\n"		\
+"group.test3:677:project three:root,nobody:root,lp:attr1=y;attr2=z\n"	\
+"test4:678:project four:root:root:\n"					\
+"test5:679:project five::sys:\n"					\
+"test6:690::::\n"
+
+
+#define	FLAG1	(0)
+#define	FLAG2	(F_PAR_VLD)
+#define	FLAG3	(F_PAR_VLD | F_PAR_RES)
+#define	FLAG4	(F_PAR_VLD | F_PAR_DUP)
+
+
+typedef struct {
+	char *file;
+	int flags;
+	int res;
+} read_test;
+
+#define	READ_TESTS_SIZE	20
+read_test rtests[READ_TESTS_SIZE] = {
+	{FILE1, FLAG1, 1},
+	{FILE1, FLAG2, 1},
+	{FILE1, FLAG3, 1},
+	{FILE1, FLAG4, 1},
+	{FILE2, FLAG1, 0},
+	{FILE2, FLAG2, 1},
+	{FILE2, FLAG3, 1},
+	{FILE2, FLAG4, 1},
+	{FILE3, FLAG1, 0},
+	{FILE3, FLAG2, 1},
+	{FILE3, FLAG3, 1},
+	{FILE3, FLAG4, 0},
+	{FILE4, FLAG1, 0},
+	{FILE4, FLAG2, 1},
+	{FILE4, FLAG3, 0},
+	{FILE4, FLAG4, 1},
+	{FILE5, FLAG1, 0},
+	{FILE5, FLAG2, 0},
+	{FILE5, FLAG3, 0},
+	{FILE5, FLAG4, 0},
+};
+
+
+typedef struct {
+	int pres;
+	int vres;
+	int flags;
+	char *pline;
+} parse_validate_test;
+
+
+
+#define	FLAG_000 0
+#define	FLAG_RES F_PAR_RES
+#define	FLAG_SPC F_PAR_SPC
+#define	FLAG_UNT F_PAR_UNT
+
+#define	PARSE_VALIDATE_TESTS_SIZE	302
+parse_validate_test pvtests[PARSE_VALIDATE_TESTS_SIZE] = {
+/* positive */
+	{ 0, 0, FLAG_RES, "system:0::::"},
+	{ 0, 0, FLAG_RES, "user.root:1::::"},
+	{ 0, 0, FLAG_RES, "noproject:2::::"},
+	{ 0, 0, FLAG_RES, "default:3::::"},
+	{ 0, 0, FLAG_RES, "group.staff:10::::"},
+	{ 0, 0, FLAG_000, "long:100::::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
+	{ 0, 0, FLAG_000, "Validname:101::::"},
+	{ 0, 0, FLAG_000, "Validname2:102::::"},
+	{ 0, 0, FLAG_000, "valid3name:103::::"},
+	{ 0, 0, FLAG_000, "VALIDNAME:104::::"},
+	{ 0, 0, FLAG_000, "VALIDNAME5:105::::"},
+	{ 0, 0, FLAG_000, "vAlid5name:106::::"},
+	{ 0, 0, FLAG_000, "valid.name:107::::"},
+	{ 0, 0, FLAG_000, "valid8.NAME:108::::"},
+	{ 0, 0, FLAG_000, "Valid_name9:109::::"},
+	{ 0, 0, FLAG_000, "V_alid.name10:110::::"},
+	{ 0, 0, FLAG_000, "valid12345678901234567890123456789012345678901234567890123456789:111::::"},
+	{ 0, 0, FLAG_000, "projid:2147483647::::"},
+	{ 0, 0, FLAG_000, "comment:111: this is ! & my crazy  !@#$%^&*()_+|~`\\=-]{ 0, 0, FLAG_000,}{';\\\"/.,?>< comment:::"},
+	{ 0, 0, FLAG_000, "user1:112::*::"},
+	{ 0, 0, FLAG_000, "user2:113::!*::"},
+	{ 0, 0, FLAG_000, "user3:114::root::"},
+	{ 0, 0, FLAG_000, "user4:115::!root::"},
+	{ 0, 0, FLAG_000, "user5:116::*,!sys::"},
+	{ 0, 0, FLAG_000, "user6:117::!*,daemon::"},
+	{ 0, 0, FLAG_000, "user7:118::root,sys,daemon,bin::"},
+	{ 0, 0, FLAG_000, "user8:119::root,!sys,daemon,!bin::"},
+	{ 0, 0, FLAG_SPC, "user9:116::*, !sys::"},
+	{ 0, 0, FLAG_SPC, "user10:117::!* ,daemon::"},
+	{ 0, 0, FLAG_SPC, "user11:118::root ,sys ,daemon, bin::"},
+	{ 0, 0, FLAG_SPC, "user12:119::root, !sys, daemon ,!bin::"},
+	{ 0, 0, FLAG_000, "group1:120:::*:"},
+	{ 0, 0, FLAG_000, "group2:121:::!*:"},
+	{ 0, 0, FLAG_000, "group3:122:::root:"},
+	{ 0, 0, FLAG_000, "group4:123:::!root:"},
+	{ 0, 0, FLAG_000, "group5:124:::*,!sys:"},
+	{ 0, 0, FLAG_000, "group6:125:::!*,daemon:"},
+	{ 0, 0, FLAG_000, "group7:126:::root,sys,daemon,bin:"},
+	{ 0, 0, FLAG_000, "group8:127:::root,!sys,daemon,!bin:"},
+	{ 0, 0, FLAG_SPC, "group9:124:::*, !sys:"},
+	{ 0, 0, FLAG_SPC, "group10:125:::!* ,daemon:"},
+	{ 0, 0, FLAG_SPC, "group11:126:::root, sys ,daemon, bin:"},
+	{ 0, 0, FLAG_SPC, "group12:127:::root ,!sys, daemon ,!bin:"},
+	{ 0, 0, FLAG_000, "group9:128:::sys:"},
+	{ 0, 0, FLAG_000, "attrib1:129::::one"},
+	{ 0, 0, FLAG_000, "attrib2:130::::One"},
+	{ 0, 0, FLAG_000, "attrib3:131::::ONE"},
+	{ 0, 0, FLAG_000, "attrib4:132::::attrib10"},
+	{ 0, 0, FLAG_000, "attrib5:133::::attrib.attrib="},
+	{ 0, 0, FLAG_000, "attrib6:134::::attib_"},
+	{ 0, 0, FLAG_000, "attrib7:135::::a10-._attib"},
+	{ 0, 0, FLAG_000, "attrib8:136::::SUNW,attrib"},
+	{ 0, 0, FLAG_000, "attrib9:137::::A,A10="},
+	{ 0, 0, FLAG_000, "attrib10:138::::FIVEE,name"},
+	{ 0, 0, FLAG_000, "attrib11:139::::one;two"},
+	{ 0, 0, FLAG_000, "attrib12:140::::one=1;two=four"},
+	{ 0, 0, FLAG_000, "attrib13:141::::one;two=;three=four"},
+	{ 0, 0, FLAG_000, "value1:142::::one=foo,bar"},
+	{ 0, 0, FLAG_000, "value2:143::::one=,bar,"},
+	{ 0, 0, FLAG_000, "value3:144::::one=(foo,bar)"},
+	{ 0, 0, FLAG_000, "value4:145::::one=(foo,bar,baz),boo"},
+	{ 0, 0, FLAG_000, "value5:146::::one;two=bar,(baz),foo,((baz)),(,)"},
+	{ 0, 0, FLAG_000, "value6:147::::one=100/200"},
+	{ 0, 0, FLAG_000, "value7:148::::two=.-_/="},
+	{ 0, 0, FLAG_000, "value8:149::::name=one=two"},
+	{ 0, 0, FLAG_UNT, "value9:150::::task.max-lwps=(priv,1000M,deny,signal=SIGHUP),(priv,1000k,deny,signal=SIGKILL)"},
+	{ 0, 0, FLAG_000, "comma1:151::,::"},
+	{ 0, 0, FLAG_000, "comma2:152::,,::"},
+	{ 0, 0, FLAG_000, "comma3:153::root,::"},
+	{ 0, 0, FLAG_000, "comma4:154::bin,root,,::"},
+	{ 0, 0, FLAG_000, "comma5:155:::,:"},
+	{ 0, 0, FLAG_000, "comma6:156:::,,:"},
+	{ 0, 0, FLAG_000, "comma7:157:::bin,root,:"},
+	{ 0, 0, FLAG_000, "comma8:158:::root,,:"},
+	{ 0, 0, FLAG_000, "semi1:159::::;"},
+	{ 0, 0, FLAG_000, "semi2:160::::;;"},
+	{ 0, 0, FLAG_000, "semi3:161::::foo=(one,two);"},
+	{ 0, 0, FLAG_000, "semi4:162::::foo;;"},
+	{ 0, 0, FLAG_UNT, "rctl1:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,1000k,deny,signal=15)"},
+	{ 0, 0, FLAG_000, "rctl1:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,10001,deny,signal=15)"},
+	{ 0, 0, FLAG_000, "rctl2:164::::process.max-port-events=(basic,1000,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl3:165::::project.max-crypto-memory=(priv,2.2gb,deny)"},
+	{ 0, 0, FLAG_000, "rctl3:165::::project.max-crypto-memory=(priv,10,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl4:166::::project.max-crypto-memory=(privileged,100m,deny)"},
+	{ 0, 0, FLAG_000, "rctl4:166::::project.max-crypto-memory=(privileged,100,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl5:167::::project.max-crypto-memory=(priv,1000m,deny)"},
+	{ 0, 0, FLAG_000, "rctl5:167::::project.max-crypto-memory=(priv,1000,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl6:168::::project.max-crypto-memory=(priv,1000k,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl6:168::::project.max-crypto-memory=(priv,1000m,deny)"},
+	{ 0, 0, FLAG_000, "rctl7:169::::process.max-msg-messages=(priv,10,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl8:170::::process.max-msg-qbytes=(priv,10000kb,deny)"},
+	{ 0, 0, FLAG_000, "rctl8:170::::process.max-msg-qbytes=(priv,10000,deny)"},
+	{ 0, 0, FLAG_000, "rctl9:171::::process.max-sem-ops=(priv,10000000,deny)"},
+	{ 0, 0, FLAG_000, "rctl10:172::::process.max-sem-nsems=(basic,1,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl11:173::::process.max-address-space=(priv,2GB,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl12:174::::process.max-file-descriptor=(basic,1K,deny),(basic,2K,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl13:175::::process.max-core-size=(priv,10Mb,deny),(priv,2GB,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl14:176::::process.max-stack-size=(priv,1.8Gb,deny),(priv,100MB,deny)"},
+	{ 0, 0, FLAG_000, "rctl15:177::::process.max-data-size=(priv,1010100101,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl16:178::::process.max-file-size=(priv,100mb,deny,signal=SIGXFSZ),(priv,1000mb,deny,signal=31)"},
+	{ 0, 0, FLAG_UNT, "rctl17:179::::process.max-cpu-time=(priv,1t,signal=XCPU),(priv,100ms,sig=30)"},
+	{ 0, 0, FLAG_UNT, "rctl18:180::::task.max-cpu-time=(priv,1M,sig=SIGKILL)"},
+	{ 0, 0, FLAG_UNT, "rctl19:181::::task.max-lwps=(basic,10,signal=1),(priv,100,deny,signal=KILL)"},
+	{ 0, 0, FLAG_000, "rctl20:182::::project.max-device-locked-memory=(priv,1000,deny,sig=TERM)"},
+	{ 0, 0, FLAG_000, "rctl21:183::::project.max-port-ids=(priv,100,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl22:184::::project.max-shm-memory=(priv,1000mb,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl23:185::::project.max-shm-ids=(priv,1k,deny,signal=SIGSTOP)"},
+	{ 0, 0, FLAG_UNT, "rctl24:186::::project.max-msg-ids=(priv,1m,deny,signal=XRES)"},
+	{ 0, 0, FLAG_000, "rctl25:187::::project.max-sem-ids=(priv,10,deny,signal=ABRT)"},
+	{ 0, 0, FLAG_UNT, "rctl26:188::::project.cpu-shares=(priv,63k,none)"},
+	{ 0, 0, FLAG_UNT, "rctl27:189::::zone.cpu-shares=(priv,20k,none)"},
+	{ 0, 0, FLAG_000, "rctl28:190::::zone.cpu-shares=(priv,100,none)"},
+	{ 0, 0, FLAG_UNT, "rctl29:191::::project.max-shm-memory=(priv,200G,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl30:192::::project.max-shm-memory=(priv,200Gb,deny)"},
+	{ 0, 0, FLAG_UNT, "rctl31:193::::project.max-shm-memory=(priv,2000B,deny)"},
+	{ 0, 0, FLAG_000, "rctl32:194::::project.max-shm-memory=(priv,2000,deny)"},
+	{ 0, 0, FLAG_000, "rctl33:195::::task.max-cpu-time=(priv,2000,none)"},
+	{ 0, 0, FLAG_UNT, "rctl34:196::::task.max-cpu-time=(priv,2000s,none)"},
+	{ 0, 0, FLAG_UNT, "rctl35:197::::task.max-cpu-time=(priv,20.1ps,none)"},
+	{ 0, 0, FLAG_UNT, "rctl36:198::::task.max-cpu-time=(priv,20T,none)"},
+/* negative */
+	{ 0, 1, FLAG_000, "system:0::::"},
+	{ 0, 1, FLAG_000, "user.root:1::::"},
+	{ 0, 1, FLAG_000, "noproject:2::::"},
+	{ 0, 1, FLAG_000, "default:3::::"},
+	{ 0, 1, FLAG_000, "group.staff:10::::"},
+	{ 0, 1, FLAG_000, "long:100::::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
+	{ 1, 0, FLAG_000, "extrafields:101:::::"},
+	{ 1, 0, FLAG_000, "missingfields:102:::"},
+	{ 1, 0, FLAG_000, "_invalidname:103::::"},
+	{ 1, 0, FLAG_000, "10invlidname:104::::"},
+	{ 1, 0, FLAG_000, "invalid%name:105::::"},
+	{ 1, 0, FLAG_000, "invalid/name:106::::"},
+	{ 1, 0, FLAG_000, ".invalidname:107::::"},
+	{ 1, 0, FLAG_000, "=invalidName:108::::"},
+	{ 1, 0, FLAG_000, "invalid=name:109::::"},
+	{ 1, 0, FLAG_000, "invalid/name:110::::"},
+	{ 1, 0, FLAG_000, "/invalidname:111::::"},
+	{ 1, 0, FLAG_000, "/invalidname:112::::"},
+	{ 1, 0, FLAG_000, "invalidname*:113::::"},
+	{ 1, 0, FLAG_000, "invalid?name:114::::"},
+	{ 1, 0, FLAG_000, ":115:invalid name comment:::"},
+	{ 1, 0, FLAG_000, "invalid!name:116::::"},
+	{ 1, 0, FLAG_000, "invalidname!:117::::"},
+	{ 1, 0, FLAG_000, "invalid12345678901234567890123456789012345678901234567890123456789:118::::"},
+	{ 1, 0, FLAG_000, "projid:-1::::"},
+	{ 1, 0, FLAG_000, "projid:abc::::"},
+	{ 1, 0, FLAG_000, "projid:2147483648::::"},
+	{ 1, 0, FLAG_000, "projid:::::"},
+	{ 1, 0, FLAG_000, "user1:118::*!::"},
+	{ 1, 0, FLAG_000, "user2:119::10user::"},
+	{ 0, 1, FLAG_000, "user3:120::NOLOWER::"},
+	{ 0, 1, FLAG_000, "user4:121::toooolong::"},
+	{ 1, 0, FLAG_000, "user5:122::root!::"},
+	{ 1, 0, FLAG_000, "user6:123::root;sys::"},
+	{ 0, 1, FLAG_000, "user7:124::sys,NOLOWER::"},
+	{ 1, 0, FLAG_000, "user8:125::sys/bin,root::"},
+	{ 1, 0, FLAG_000, "user9:116::*, !sys::"},
+	{ 1, 0, FLAG_000, "user10:117::!* ,daemon::"},
+	{ 1, 0, FLAG_000, "user11:118::root ,sys ,daemon, bin::"},
+	{ 1, 0, FLAG_000, "user12:119::root, !sys, daemon ,!bin::"},
+	{ 1, 0, FLAG_000, "group1:126:::*!:"},
+	{ 0, 1, FLAG_000, "group2:127:::oneUpper:"},
+	{ 0, 1, FLAG_000, "group3:128:::NOLOWER:"},
+	{ 0, 1, FLAG_000, "group4:129:::toooolong:"},
+	{ 1, 0, FLAG_000, "group5:130:::root!:"},
+	{ 1, 0, FLAG_000, "group6:131:::root;sys:"},
+	{ 0, 1, FLAG_000, "group7:132:::sys,NOLOWER:"},
+	{ 1, 0, FLAG_000, "group8:133:::sys-bin,root:"},
+	{ 1, 0, FLAG_000, "group9:124:::*, !sys:"},
+	{ 1, 0, FLAG_000, "group10:125:::!* ,daemon:"},
+	{ 1, 0, FLAG_000, "group11:126:::root, sys ,daemon, bin:"},
+	{ 1, 0, FLAG_000, "group12:127:::root ,!sys, daemon ,!bin:"},
+	{ 1, 0, FLAG_000, "attrib1:134::::10"},
+	{ 1, 0, FLAG_000, "attrib2:135::::_foo="},
+	{ 1, 0, FLAG_000, "attrib3:136::::,foo"},
+	{ 1, 0, FLAG_000, "attrib4:137::::sun,foo"},
+	{ 1, 0, FLAG_000, "attrib6:139::::!attrib"},
+	{ 1, 0, FLAG_000, "attrib7:140::::_attrib"},
+	{ 1, 0, FLAG_000, "attrib8:141::::attib,attrib"},
+	{ 1, 0, FLAG_000, "attrib9:142::::attrib/attrib"},
+	{ 1, 0, FLAG_000, "attrib10:143::::one;two,three"},
+	{ 1, 0, FLAG_000, "attrib11:144::::one=two;three/"},
+	{ 1, 0, FLAG_000, "value1:145::::one=foo%"},
+	{ 1, 0, FLAG_000, "value2:146::::one= two"},
+	{ 1, 0, FLAG_000, "value3:147::::var=foo?"},
+	{ 1, 0, FLAG_000, "value4:148::::name=value;name=value2)"},
+	{ 1, 0, FLAG_000, "value5:149::::(foo)"},
+	{ 1, 0, FLAG_000, "value6:150::::name=(foo,bar"},
+	{ 1, 0, FLAG_000, "value7:151::::name=(value)(value)"},
+	{ 1, 0, FLAG_000, "value8:152::::name=)"},
+	{ 1, 0, FLAG_000, "value9:153::::name=value,(value value)"},
+	{ 1, 0, FLAG_000, "value10:154::::name=(value(value))"},
+	{ 1, 0, FLAG_000, "value11:155::::name=(value)value"},
+	{ 1, 0, FLAG_000, "value11:156::::name=va?lue"},
+	{ 1, 0, FLAG_000, "value12:157::::name=(value,value))"},
+	{ 1, 0, FLAG_000, "value13:158::::name=(value),value)"},
+	{ 1, 0, FLAG_000, "space1 :159::::"},
+	{ 1, 0, FLAG_000, " space2:160::::"},
+	{ 1, 0, FLAG_000, "space3: 161::::"},
+	{ 1, 0, FLAG_000, "space4:162 ::::"},
+	{ 1, 0, FLAG_000, "space 5:163::::"},
+	{ 1, 0, FLAG_000, "space6:1 64::::"},
+	{ 1, 0, FLAG_000, "space7:165:: root::"},
+	{ 1, 0, FLAG_000, "space8:166::root ::"},
+	{ 1, 0, FLAG_000, "space9:167::daemon, root::"},
+	{ 1, 0, FLAG_000, "space10:168::bin root::"},
+	{ 1, 0, FLAG_000, "space11:169::daemon ,root::"},
+	{ 1, 0, FLAG_000, "space12 :170::::"},
+	{ 1, 0, FLAG_000, " space13:171::::"},
+	{ 1, 0, FLAG_000, "space14: 172::::"},
+	{ 1, 0, FLAG_000, "space15:173 ::::"},
+	{ 1, 0, FLAG_000, "space 16:174::::"},
+	{ 1, 0, FLAG_000, "space17:1 75::::"},
+	{ 1, 0, FLAG_000, "space18:176::: root:"},
+	{ 1, 0, FLAG_000, "space19:177:::root :"},
+	{ 1, 0, FLAG_000, "space20:178:::daemon, root:"},
+	{ 1, 0, FLAG_000, "space21:179:::bin root:"},
+	{ 1, 0, FLAG_000, "space22:180:::daemon ,root:"},
+	{ 1, 0, FLAG_000, "space23:181:::: foo"},
+	{ 1, 0, FLAG_000, "space34:182::::foo =one"},
+	{ 1, 0, FLAG_000, "space35:183::::foo= (one)"},
+	{ 1, 0, FLAG_000, "space36:184::::foo=(one, two)"},
+	{ 1, 0, FLAG_000, "space37:185::::foo=(one ,two)"},
+	{ 1, 0, FLAG_000, "space38:186::::foo=( one)"},
+	{ 1, 0, FLAG_000, "space39:187::::foo=(one )"},
+	{ 1, 0, FLAG_000, "space40:188::::foo=(one) ,two"},
+	{ 1, 0, FLAG_000, "space41:189::::foo=one, (two)"},
+/*
+ *	{ 1, 0, FLAG_000, "comma1:190::,root,bin::"},
+ *	{ 1, 0, FLAG_000, "comma2:191::root,,bin::"},
+ *	{ 1, 0, FLAG_000, "comma3:192::,,root,bin::"},
+ *	{ 1, 0, FLAG_000, "comma4:193:::,root,bin:"},
+ *	{ 1, 0, FLAG_000, "comma5:194:::root,,bin:"},
+ *	{ 1, 0, FLAG_000, "comma6:195:::,,root,bin:"},
+ *	{ 1, 0, FLAG_000, "semi1:196::::;foo"},
+ *	{ 1, 0, FLAG_000, "semi2:197::::foo;;bar=1"},
+ *	{ 1, 0, FLAG_000, "semi3:198::::;;bar=(10)"},
+ */
+	{ 0, 1, FLAG_000, "rctl1:199::::task.max-lwps=,"},
+	{ 0, 1, FLAG_000, "rctl2:200::::task.max-lwps="},
+	{ 0, 1, FLAG_000, "rctl3:201::::task.max-lwps=priv"},
+	{ 0, 1, FLAG_000, "rctl4:202::::task.max-lwps=priv,1000"},
+	{ 0, 1, FLAG_000, "rctl5:203::::task.max-lwps=priv,1000,deny"},
+	{ 0, 1, FLAG_000, "rctl6:204::::task.max-lwps=(priv)"},
+	{ 0, 1, FLAG_000, "rctl7:205::::task.max-lwps=(priv,1000)"},
+	{ 0, 1, FLAG_000, "rctl8:206::::task.max-lwps=(foo,100,deny)"},
+	{ 0, 1, FLAG_000, "rctl9:207::::task.max-lwps=(priv,foo,none)"},
+	{ 1, 0, FLAG_UNT, "rctl9:207::::task.max-lwps=(priv,foo,none)"},
+	{ 1, 0, FLAG_UNT, "rctl10:208::::task.max-lwps=(priv,100foo,none)"},
+	{ 0, 1, FLAG_000, "rctl11:209::::task.max-lwps=(priv,1000,foo)"},
+	{ 0, 1, FLAG_UNT, "rctl12:210::::task.max-lwps=(priv,1000k,deny,signal)"},
+	{ 0, 1, FLAG_000, "rctl13:211::::task.max-lwps=(priv,1000,deny,signal=)"},
+	{ 0, 1, FLAG_000, "rctl14:212::::task.max-lwps=(priv,1000,deny,signal=foo)"},
+	{ 0, 1, FLAG_000, "rctl15:213::::task.max-lwps=(priv,1000,deny,signal=1fo)"},
+	{ 0, 1, FLAG_000, "rctl16:214::::task.max-lwps=(priv,1000,deny,signal=100)"},
+	{ 0, 1, FLAG_000, "rctl17:215::::task.max-lwps=(priv,1000,deny,signal=SIG)"},
+	{ 0, 1, FLAG_000, "rctl18:216::::task.max-lwps=(priv,1000,deny,signal=SIG1)"},
+	{ 0, 1, FLAG_000, "rctl19:217::::task.max-lwps=(priv,1000,deny,signal=SIGhup)"},
+	{ 0, 1, FLAG_000, "rctl20:218::::task.max-lwps=(priv,1000,deny,signal=SIGHU)"},
+	{ 0, 1, FLAG_000, "rctl21:219::::task.max-lwps=(priv,1000,deny,signal=SIGHUPP)"},
+	{ 0, 1, FLAG_000, "rctl22:220::::task.max-lwps=(priv,1000,deny,signal=SIGURG)"},
+	{ 0, 1, FLAG_000, "rctl23:221::::task.max-lwps=(priv,1000,deny,signal=SIGXCPU)"},
+	{ 0, 1, FLAG_000, "rctl24:222::::task.max-lwps=(priv,1000,deny,signal=SIGKILL,10)"},
+	{ 0, 1, FLAG_000, "rctl25:223::::task.max-lwps=(priv,1000,deny,signal=SIGKILL,foo)"},
+	{ 0, 1, FLAG_000, "rctl26:224::::process.max-port-events=(priv,1000,none)"},
+	{ 0, 1, FLAG_UNT, "rctl27:225::::process.max-address-space=(basic,1024mb,deny,signal=TERM)"},
+	{ 0, 1, FLAG_000, "rctl28:226::::process.max-cpu-time=(basic,3600,deny)"},
+	{ 0, 1, FLAG_000, "rctl29:227::::task.max-lwps=()"},
+	{ 0, 1, FLAG_000, "rctl30:228::::task.max-lwps=((priv),deny)"},
+	{ 0, 1, FLAG_000, "rctl31:229::::task.max-lwps=((priv,1000,deny))"},
+	{ 0, 1, FLAG_000, "rctl32:230::::task.max-lwps=(priv,((1000,2000,1000)),deny)"},
+	{ 0, 1, FLAG_000, "rctl33:231::::task.max-lwps=(,,,)"},
+	{ 0, 1, FLAG_000, "rctl34:232::::task.max-lwps=(priv,1000,(deny))"},
+	{ 0, 1, FLAG_000, "rctl35:233::::task.max-lwps=(priv,1000,deny),foo"},
+	{ 0, 1, FLAG_000, "rctl36:234::::task.max-lwps=(priv,1000,deny),(priv,1000)"},
+	{ 1, 0, FLAG_UNT, "rctl37:235::::project.max-msg-ids=(priv,15EB,deny)"},
+	{ 1, 0, FLAG_UNT, "rctl38:236::::process.max-address-space=(priv,16.1EB,deny)"},
+	{ 1, 0, FLAG_UNT, "rctl39:237::::process.max-address-space=(priv,18000000000gb,deny)"},
+	{ 1, 0, FLAG_UNT, "rctl40:238::::zone.cpu-shares=(priv,10kb,none)"},
+	{ 1, 0, FLAG_UNT, "rctl41:239::::zone.cpu-shares=(priv,10Ks,none)"},
+	{ 1, 0, FLAG_UNT, "rctl42:240::::zone.cpu-shares=(priv,10s,none)"},
+	{ 1, 0, FLAG_UNT, "rctl43:241::::zone.cpu-shares=(priv,100000b,none)"},
+	{ 1, 0, FLAG_UNT, "rctl44:242::::project.max-shm-memory=(priv,200Ts,deny)"},
+	{ 1, 0, FLAG_UNT, "rctl45:243::::project.max-shm-memory=(priv,200s,deny)"},
+	{ 1, 0, FLAG_UNT, "rctl46:244::::task.max-cpu-time=(priv,20B,none)"},
+	{ 1, 0, FLAG_UNT, "rctl47:245::::task.max-cpu-time=(priv,20Kb,none)"},
+	{ 0, 1, FLAG_UNT, "rctl48:246::::project.cpu-shares=(priv,100k,none)"},
+	{ 0, 1, FLAG_000, "rctl147:150::::task.max-lwps=(priv,1000M,deny,signal=SIGHUP),(priv,1000k,deny,signal=SIGKILL)"},
+	{ 0, 1, FLAG_000, "rctl148:163::::task.max-lwps=(priv,1000,deny,signal=HUP),(priv,1000k,deny,signal=15)"},
+	{ 0, 1, FLAG_000, "rctl3:165::::project.max-crypto-memory=(priv,10eb,deny)"},
+	{ 0, 1, FLAG_000, "rctl4:166::::project.max-crypto-memory=(privileged,100p,deny)"},
+	{ 0, 1, FLAG_000, "rctl5:167::::project.max-crypto-memory=(priv,1000t,deny)"},
+	{ 0, 1, FLAG_000, "rctl6:168::::project.max-crypto-memory=(priv,1000g,deny)"},
+	{ 0, 1, FLAG_000, "rctl7:169::::process.max-msg-messages=(priv,10m,deny)"},
+	{ 0, 1, FLAG_000, "rctl8:170::::process.max-msg-qbytes=(priv,10000kb,deny)"},
+	{ 0, 1, FLAG_000, "rctl11:173::::process.max-address-space=(priv,10EB,deny)"},
+	{ 0, 1, FLAG_000, "rctl12:174::::process.max-file-descriptor=(basic,1K,deny),(basic,2K,deny)"},
+	{ 0, 1, FLAG_000, "rctl13:175::::process.max-core-size=(priv,1Eb,deny),(priv,10PB,deny)"},
+	{ 0, 1, FLAG_000, "rctl14:176::::process.max-stack-size=(priv,10Tb,deny),(priv,10TB,deny)"},
+	{ 0, 1, FLAG_000, "rctl16:178::::process.max-file-size=(priv,100mb,deny,signal=SIGXFSZ),(priv,1000mb,deny,signal=31)"},
+	{ 0, 1, FLAG_000, "rctl17:179::::process.max-cpu-time=(priv,1t,signal=XCPU),(priv,100ms,sig=30)"},
+	{ 0, 1, FLAG_000, "rctl18:180::::task.max-cpu-time=(priv,1M,sig=SIGKILL)"},
+	{ 0, 1, FLAG_000, "rctl22:184::::project.max-shm-memory=(priv,1000mb,deny)"},
+	{ 0, 1, FLAG_000, "rctl23:185::::project.max-shm-ids=(priv,1k,deny,signal=SIGSTOP)"},
+	{ 0, 1, FLAG_000, "rctl24:186::::project.max-msg-ids=(priv,1m,deny,signal=XRES)"},
+	{ 0, 1, FLAG_000, "rctl26:188::::project.cpu-shares=(priv,63k,none)"},
+	{ 0, 1, FLAG_000, "rctl27:189::::zone.cpu-shares=(priv,20k,none)"},
+	{ 0, 1, FLAG_000, "rctl29:191::::project.max-shm-memory=(priv,200G,deny)"},
+	{ 0, 1, FLAG_000, "rctl30:192::::project.max-shm-memory=(priv,200Gb,deny)"},
+	{ 0, 1, FLAG_000, "rctl31:193::::project.max-shm-memory=(priv,2000B,deny)"},
+	{ 0, 1, FLAG_000, "rctl34:196::::task.max-cpu-time=(priv,2000s,none)"},
+	{ 0, 1, FLAG_000, "rctl35:197::::task.max-cpu-time=(priv,20.1ps,none)"},
+	{ 0, 1, FLAG_000, "rctl36:198::::task.max-cpu-time=(priv,20T,none)"},
+};
+
+
+boolean_t vflag = B_FALSE;
+boolean_t kflag = B_FALSE;
+
+int
+projtest_compare_two_files(char *file1, char *file2, lst_t *errlst)
+{
+	FILE *fp1, *fp2;
+	int c1, c2;
+
+	if ((fp1 = fopen(file1, "r")) == NULL) {
+		util_add_errmsg(errlst, "failed to open \"%s\"", file1);
+		return (1);
+	}
+
+	if ((fp2 = fopen(file2, "r")) == NULL) {
+		util_add_errmsg(errlst, "failed to open \"%s\"", file2);
+		return (1);
+	}
+
+	c1 = getc(fp1);
+	c2 = getc(fp2);
+	while ((c1 != EOF) && (c2 != EOF) && (c1 == c2)) {
+		c1 = getc(fp1);
+		c2 = getc(fp2);
+	}
+	(void) fclose(fp1);
+	(void) fclose(fp2);
+
+	if (c1 != c2) {
+		util_add_errmsg(errlst, "files differ \"%s\" \"%s\"",
+		    file1, file2);
+		return (1);
+	}
+
+	return (0);
+}
+
+void
+projtest_free_errlst(lst_t *errlst)
+{
+	char *msg;
+	while (!lst_is_empty(errlst)) {
+		msg = lst_at(errlst,  0);
+		(void) lst_remove(errlst, msg);
+		free(msg);
+	}
+}
+
+void
+do_read_test(read_test *rt, int tnum)
+{
+	FILE *fp;
+	char *projfile, *projwrite;
+	lst_t *plst;
+	lst_t errlst;
+	lst_create(&errlst);
+
+
+	(void) asprintf(&projfile, "/tmp/project_%ld_%d_tmp", getpid(), tnum);
+	(void) unlink(projfile);
+	if ((fp = fopen(projfile, "wx")) == NULL) {
+		(void) fprintf(stderr, "error opening %s\n", projfile);
+		exit(1);
+	}
+	(void) fputs(rt->file, fp);
+	(void) fclose(fp);
+
+	(void) asprintf(&projwrite, "/tmp/project_%ld_%d_wrt", getpid(), tnum);
+	if ((fp = fopen(projwrite, "wx")) == NULL) {
+		(void) fprintf(stderr, "error opening %s\n", projfile);
+		exit(1);
+	}
+	(void) fclose(fp);
+
+
+	plst = projent_get_lst(projfile, rt->flags, &errlst);
+
+
+	if (plst == NULL && rt->res == 1) {
+		(void) printf("Read test[%02d]: **************[-SUCCESS-]\n",
+		    tnum);
+	} else if (plst != NULL && rt->res == 0) {
+
+		projent_put_lst(projwrite, plst, &errlst);
+		(void) projtest_compare_two_files(projfile, projwrite, &errlst);
+		(void) printf("Read test[%02d]: **************[%s]\n", tnum,
+		    lst_is_empty(&errlst) ? "-SUCCESS-" : "*FAILURE*");
+
+	} else {
+		(void) printf("Read test[%02d]: **************[*FAILURE*]\n",
+		    tnum);
+	}
+
+	if (vflag) {
+		util_print_errmsgs(&errlst);
+	}
+
+	if (!kflag) {
+		(void) unlink(projfile);
+		(void) unlink(projwrite);
+	}
+
+	projent_free_lst(plst);
+	projtest_free_errlst(&errlst);
+}
+
+
+void
+do_parse_validate_test(parse_validate_test *pvt, int tnum)
+{
+	lst_t errlst;
+	projent_t *ent;
+	int vres;
+
+
+	lst_create(&errlst);
+
+
+	ent = projent_parse(pvt->pline, pvt->flags, &errlst);
+
+	if (ent == NULL && pvt->pres == 1) {
+		(void) printf("Prse test[%02d]: **************[-SUCCESS-]\n", tnum);
+	} else if (ent != NULL && pvt->pres == 0) {
+		(void) printf("Prse test[%02d]: **************[-SUCCESS-]\n", tnum);
+		vres = projent_validate(ent, pvt->flags, &errlst);
+		(void) printf("Vdte test[%02d]: **************[%s]\n", tnum,
+		    ((vres == 0 && pvt->vres == 0) ||
+		    (vres != 0 && pvt->vres != 0)) ?
+		    "-SUCCESS-" : "*FAILURE*");
+	} else {
+		(void) printf("Prse test[%02d]: **************[*FAILURE*]\n", tnum);
+	}
+
+	if (vflag) {
+		util_print_errmsgs(&errlst);
+	}
+
+	if (ent != NULL) {
+		projent_free(ent);
+		free(ent);
+	}
+	projtest_free_errlst(&errlst);
+}
+
+void
+start_read_test(void)
+{
+	int i;
+	for (i = 0; i < READ_TESTS_SIZE; i++) {
+		do_read_test(&rtests[i], i);
+	}
+}
+
+void
+start_parse_validate_test(void)
+{
+	int i;
+	for (i = 0; i < PARSE_VALIDATE_TESTS_SIZE; i++) {
+		do_parse_validate_test(&pvtests[i], i);
+	}
+}
+
+void
+usage(void)
+{
+	(void) fprintf(stderr,
+	    "Usage:\n"
+	    "projtest [-k] [-v]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+	int c;
+
+	while ((c = getopt(argc, argv, "vk")) != EOF) {
+		switch (c) {
+			case 'v':
+				vflag = B_TRUE;
+				break;
+			case 'k':
+				kflag = B_TRUE;
+				break;
+			default:
+				usage();
+				exit(1);
+				break;
+		}
+	}
+
+	start_read_test();
+	start_parse_validate_test();
+	return (0);
+}
