More files related to user-level file servers.
Also add acme!
diff --git a/src/cmd/acme/addr.c b/src/cmd/acme/addr.c
new file mode 100644
index 0000000..d64db61
--- /dev/null
+++ b/src/cmd/acme/addr.c
@@ -0,0 +1,269 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include <plumb.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+	None = 0,
+	Fore = '+',
+	Back = '-',
+};
+
+enum
+{
+	Char,
+	Line,
+};
+
+int
+isaddrc(int r)
+{
+	if(r && utfrune("0123456789+-/$.#,;", r)!=nil)
+		return TRUE;
+	return FALSE;
+}
+
+/*
+ * quite hard: could be almost anything but white space, but we are a little conservative,
+ * aiming for regular expressions of alphanumerics and no white space
+ */
+int
+isregexc(int r)
+{
+	if(r == 0)
+		return FALSE;
+	if(isalnum(r))
+		return TRUE;
+	if(utfrune("^+-.*?#,;[]()$", r)!=nil)
+		return TRUE;
+	return FALSE;
+}
+
+Range
+number(Mntdir *md, Text *t, Range r, int line, int dir, int size, int *evalp)
+{
+	uint q0, q1;
+
+	if(size == Char){
+		if(dir == Fore)
+			line = r.q1+line;
+		else if(dir == Back){
+			if(r.q0==0 && line>0)
+				r.q0 = t->file->b.nc;
+			line = r.q0 - line;
+		}
+		if(line<0 || line>t->file->b.nc)
+			goto Rescue;
+		*evalp = TRUE;
+		return (Range){line, line};
+	}
+	q0 = r.q0;
+	q1 = r.q1;
+	switch(dir){
+	case None:
+		q0 = 0;
+		q1 = 0;
+	Forward:
+		while(line>0 && q1<t->file->b.nc)
+			if(textreadc(t, q1++) == '\n' || q1==t->file->b.nc)
+				if(--line > 0)
+					q0 = q1;
+		if(line > 0)
+			goto Rescue;
+		break;
+	case Fore:
+		if(q1 > 0)
+			while(textreadc(t, q1-1) != '\n')
+				q1++;
+		q0 = q1;
+		goto Forward;
+	case Back:
+		if(q0 < t->file->b.nc)
+			while(q0>0 && textreadc(t, q0-1)!='\n')
+				q0--;
+		q1 = q0;
+		while(line>0 && q0>0){
+			if(textreadc(t, q0-1) == '\n'){
+				if(--line >= 0)
+					q1 = q0;
+			}
+			--q0;
+		}
+		if(line > 0)
+			goto Rescue;
+		while(q0>0 && textreadc(t, q0-1)!='\n')
+			--q0;
+	}
+	*evalp = TRUE;
+	return (Range){q0, q1};
+
+    Rescue:
+	if(md != nil)
+		warning(nil, "address out of range\n");
+	*evalp = FALSE;
+	return r;
+}
+
+
+Range
+regexp(Mntdir *md, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp)
+{
+	int found;
+	Rangeset sel;
+	int q;
+
+	if(pat[0] == '\0' && rxnull()){
+		warning(md, "no previous regular expression\n");
+		*foundp = FALSE;
+		return r;
+	}
+	if(pat[0] && rxcompile(pat) == FALSE){
+		*foundp = FALSE;
+		return r;
+	}
+	if(dir == Back)
+		found = rxbexecute(t, r.q0, &sel);
+	else{
+		if(lim.q0 < 0)
+			q = Infinity;
+		else
+			q = lim.q1;
+		found = rxexecute(t, nil, r.q1, q, &sel);
+	}
+	if(!found && md==nil)
+		warning(nil, "no match for regexp\n");
+	*foundp = found;
+	return sel.r[0];
+}
+
+Range
+address(Mntdir *md, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint),  int *evalp, uint *qp)
+{
+	int dir, size, npat;
+	int prevc, c, nc, n;
+	uint q;
+	Rune *pat;
+	Range r, nr;
+
+	r = ar;
+	q = q0;
+	dir = None;
+	size = Line;
+	c = 0;
+	while(q < q1){
+		prevc = c;
+		c = (*getc)(a, q++);
+		switch(c){
+		default:
+			*qp = q-1;
+			return r;
+		case ';':
+			ar = r;
+			/* fall through */
+		case ',':
+			if(prevc == 0)	/* lhs defaults to 0 */
+				r.q0 = 0;
+			if(q>=q1 && t!=nil && t->file!=nil)	/* rhs defaults to $ */
+				r.q1 = t->file->b.nc;
+			else{
+				nr = address(md, t, lim, ar, a, q, q1, getc, evalp, &q);
+				r.q1 = nr.q1;
+			}
+			*qp = q;
+			return r;
+		case '+':
+		case '-':
+			if(*evalp && (prevc=='+' || prevc=='-'))
+				if((nc=(*getc)(a, q))!='#' && nc!='/' && nc!='?')
+					r = number(md, t, r, 1, prevc, Line, evalp);	/* do previous one */
+			dir = c;
+			break;
+		case '.':
+		case '$':
+			if(q != q0+1){
+				*qp = q-1;
+				return r;
+			}
+			if(*evalp)
+				if(c == '.')
+					r = ar;
+				else
+					r = (Range){t->file->b.nc, t->file->b.nc};
+			if(q < q1)
+				dir = Fore;
+			else
+				dir = None;
+			break;
+		case '#':
+			if(q==q1 || (c=(*getc)(a, q++))<'0' || '9'<c){
+				*qp = q-1;
+				return r;
+			}
+			size = Char;
+			/* fall through */
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			n = c -'0';
+			while(q<q1){
+				c = (*getc)(a, q++);
+				if(c<'0' || '9'<c){
+					q--;
+					break;
+				}
+				n = n*10+(c-'0');
+			}
+			if(*evalp)
+				r = number(md, t, r, n, dir, size, evalp);
+			dir = None;
+			size = Line;
+			break;
+		case '?':
+			dir = Back;
+			/* fall through */
+		case '/':
+			npat = 0;
+			pat = nil;
+			while(q<q1){
+				c = (*getc)(a, q++);
+				switch(c){
+				case '\n':
+					--q;
+					goto out;
+				case '\\':
+					pat = runerealloc(pat, npat+1);
+					pat[npat++] = c;
+					if(q == q1)
+						goto out;
+					c = (*getc)(a, q++);
+					break;
+				case '/':
+					goto out;
+				}
+				pat = runerealloc(pat, npat+1);
+				pat[npat++] = c;
+			}
+		    out:
+			pat = runerealloc(pat, npat+1);
+			pat[npat] = 0;
+			if(*evalp)
+				r = regexp(md, t, lim, r, pat, dir, evalp);
+			free(pat);
+			dir = None;
+			size = Line;
+			break;
+		}
+	}
+	if(*evalp && dir != None)
+		r = number(md, t, r, 1, dir, Line, evalp);	/* do previous one */
+	*qp = q;
+	return r;
+}