basically none of these build
diff --git a/src/libauth/attr.c b/src/libauth/attr.c
new file mode 100644
index 0000000..5f35750
--- /dev/null
+++ b/src/libauth/attr.c
@@ -0,0 +1,174 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+
+int
+_attrfmt(Fmt *fmt)
+{
+	char *b, buf[1024], *ebuf;
+	Attr *a;
+
+	ebuf = buf+sizeof buf;
+	b = buf;
+	strcpy(buf, " ");
+	for(a=va_arg(fmt->args, Attr*); a; a=a->next){
+		if(a->name == nil)
+			continue;
+		switch(a->type){
+		case AttrQuery:
+			b = seprint(b, ebuf, " %q?", a->name);
+			break;
+		case AttrNameval:
+			b = seprint(b, ebuf, " %q=%q", a->name, a->val);
+			break;
+		case AttrDefault:
+			b = seprint(b, ebuf, " %q:=%q", a->name, a->val);
+			break;
+		}
+	}
+	return fmtstrcpy(fmt, buf+1);
+}
+
+Attr*
+_copyattr(Attr *a)
+{
+	Attr **la, *na;
+
+	na = nil;
+	la = &na;
+	for(; a; a=a->next){
+		*la = _mkattr(a->type, a->name, a->val, nil);
+		setmalloctag(*la, getcallerpc(&a));
+		la = &(*la)->next;
+	}
+	*la = nil;
+	return na;
+}
+
+Attr*
+_delattr(Attr *a, char *name)
+{
+	Attr *fa;
+	Attr **la;
+
+	for(la=&a; *la; ){
+		if(strcmp((*la)->name, name) == 0){
+			fa = *la;
+			*la = (*la)->next;
+			fa->next = nil;
+			_freeattr(fa);
+		}else
+			la=&(*la)->next;
+	}
+	return a;
+}
+
+Attr*
+_findattr(Attr *a, char *n)
+{
+	for(; a; a=a->next)
+		if(strcmp(a->name, n) == 0 && a->type != AttrQuery)
+			return a;
+	return nil;
+}
+
+void
+_freeattr(Attr *a)
+{
+	Attr *anext;
+
+	for(; a; a=anext){
+		anext = a->next;
+		free(a->name);
+		free(a->val);
+		a->name = (void*)~0;
+		a->val = (void*)~0;
+		a->next = (void*)~0;
+		free(a);
+	}
+}
+
+Attr*
+_mkattr(int type, char *name, char *val, Attr *next)
+{
+	Attr *a;
+
+	a = malloc(sizeof(*a));
+	if(a==nil)
+		sysfatal("_mkattr malloc: %r");
+	a->type = type;
+	a->name = strdup(name);
+	a->val = strdup(val);
+	if(a->name==nil || a->val==nil)
+		sysfatal("_mkattr malloc: %r");
+	a->next = next;
+	setmalloctag(a, getcallerpc(&type));
+	return a;
+}
+
+static Attr*
+cleanattr(Attr *a)
+{
+	Attr *fa;
+	Attr **la;
+
+	for(la=&a; *la; ){
+		if((*la)->type==AttrQuery && _findattr(a, (*la)->name)){
+			fa = *la;
+			*la = (*la)->next;
+			fa->next = nil;
+			_freeattr(fa);
+		}else
+			la=&(*la)->next;
+	}
+	return a;
+}
+
+Attr*
+_parseattr(char *s)
+{
+	char *p, *t, *tok[256];
+	int i, ntok, type;
+	Attr *a;
+
+	s = strdup(s);
+	if(s == nil)
+		sysfatal("_parseattr strdup: %r");
+
+	ntok = tokenize(s, tok, nelem(tok));
+	a = nil;
+	for(i=ntok-1; i>=0; i--){
+		t = tok[i];
+		if(p = strchr(t, '=')){
+			*p++ = '\0';
+		//	if(p-2 >= t && p[-2] == ':'){
+		//		p[-2] = '\0';
+		//		type = AttrDefault;
+		//	}else
+				type = AttrNameval;
+			a = _mkattr(type, t, p, a);
+			setmalloctag(a, getcallerpc(&s));
+		}
+		else if(t[strlen(t)-1] == '?'){
+			t[strlen(t)-1] = '\0';
+			a = _mkattr(AttrQuery, t, "", a);
+			setmalloctag(a, getcallerpc(&s));
+		}else{
+			/* really a syntax error, but better to provide some indication */
+			a = _mkattr(AttrNameval, t, "", a);
+			setmalloctag(a, getcallerpc(&s));
+		}
+	}
+	free(s);
+	return cleanattr(a);
+}
+
+char*
+_strfindattr(Attr *a, char *n)
+{
+	a = _findattr(a, n);
+	if(a == nil)
+		return nil;
+	return a->val;
+}
+