xref: /plan9/sys/src/cmd/acme/addr.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include "dat.h"
12 #include "fns.h"
13 
14 enum
15 {
16 	None = 0,
17 	Fore = '+',
18 	Back = '-',
19 };
20 
21 enum
22 {
23 	Char,
24 	Line,
25 };
26 
27 int
28 isaddrc(int r)
29 {
30 	if(r && utfrune("0123456789+-/$.#,;", r)!=nil)
31 		return TRUE;
32 	return FALSE;
33 }
34 
35 /*
36  * quite hard: could be almost anything but white space, but we are a little conservative,
37  * aiming for regular expressions of alphanumerics and no white space
38  */
39 int
40 isregexc(int r)
41 {
42 	if(r == 0)
43 		return FALSE;
44 	if(isalnum(r))
45 		return TRUE;
46 	if(utfrune("^+-.*?#,;[]()$", r)!=nil)
47 		return TRUE;
48 	return FALSE;
49 }
50 
51 Range
52 number(Mntdir *md, Text *t, Range r, int line, int dir, int size, int *evalp)
53 {
54 	uint q0, q1;
55 
56 	if(size == Char){
57 		if(dir == Fore)
58 			line = r.q1+line;
59 		else if(dir == Back){
60 			if(r.q0==0 && line>0)
61 				r.q0 = t->file->nc;
62 			line = r.q0 - line;
63 		}
64 		if(line<0 || line>t->file->nc)
65 			goto Rescue;
66 		*evalp = TRUE;
67 		return (Range){line, line};
68 	}
69 	q0 = r.q0;
70 	q1 = r.q1;
71 	switch(dir){
72 	case None:
73 		q0 = 0;
74 		q1 = 0;
75 	Forward:
76 		while(line>0 && q1<t->file->nc)
77 			if(textreadc(t, q1++) == '\n' || q1==t->file->nc)
78 				if(--line > 0)
79 					q0 = q1;
80 		if(line > 0)
81 			goto Rescue;
82 		break;
83 	case Fore:
84 		if(q1 > 0)
85 			while(q1<t->file->nc && textreadc(t, q1-1) != '\n')
86 				q1++;
87 		q0 = q1;
88 		goto Forward;
89 	case Back:
90 		if(q0 < t->file->nc)
91 			while(q0>0 && textreadc(t, q0-1)!='\n')
92 				q0--;
93 		q1 = q0;
94 		while(line>0 && q0>0){
95 			if(textreadc(t, q0-1) == '\n'){
96 				if(--line >= 0)
97 					q1 = q0;
98 			}
99 			--q0;
100 		}
101 		if(line > 0)
102 			goto Rescue;
103 		while(q0>0 && textreadc(t, q0-1)!='\n')
104 			--q0;
105 	}
106 	*evalp = TRUE;
107 	return (Range){q0, q1};
108 
109     Rescue:
110 	if(md != nil)
111 		warning(nil, "address out of range\n");
112 	*evalp = FALSE;
113 	return r;
114 }
115 
116 
117 Range
118 regexp(Mntdir *md, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp)
119 {
120 	int found;
121 	Rangeset sel;
122 	int q;
123 
124 	if(pat[0] == '\0' && rxnull()){
125 		warning(md, "no previous regular expression\n");
126 		*foundp = FALSE;
127 		return r;
128 	}
129 	if(pat[0] && rxcompile(pat) == FALSE){
130 		*foundp = FALSE;
131 		return r;
132 	}
133 	if(dir == Back)
134 		found = rxbexecute(t, r.q0, &sel);
135 	else{
136 		if(lim.q0 < 0)
137 			q = Infinity;
138 		else
139 			q = lim.q1;
140 		found = rxexecute(t, nil, r.q1, q, &sel);
141 	}
142 	if(!found && md==nil)
143 		warning(nil, "no match for regexp\n");
144 	*foundp = found;
145 	return sel.r[0];
146 }
147 
148 Range
149 address(Mntdir *md, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint),  int *evalp, uint *qp)
150 {
151 	int dir, size, npat;
152 	int prevc, c, nc, n;
153 	uint q;
154 	Rune *pat;
155 	Range r, nr;
156 
157 	r = ar;
158 	q = q0;
159 	dir = None;
160 	size = Line;
161 	c = 0;
162 	while(q < q1){
163 		prevc = c;
164 		c = (*getc)(a, q++);
165 		switch(c){
166 		default:
167 			*qp = q-1;
168 			return r;
169 		case ';':
170 			ar = r;
171 			/* fall through */
172 		case ',':
173 			if(prevc == 0)	/* lhs defaults to 0 */
174 				r.q0 = 0;
175 			if(q>=q1 && t!=nil && t->file!=nil)	/* rhs defaults to $ */
176 				r.q1 = t->file->nc;
177 			else{
178 				nr = address(md, t, lim, ar, a, q, q1, getc, evalp, &q);
179 				r.q1 = nr.q1;
180 			}
181 			*qp = q;
182 			return r;
183 		case '+':
184 		case '-':
185 			if(*evalp && (prevc=='+' || prevc=='-'))
186 				if((nc=(*getc)(a, q))!='#' && nc!='/' && nc!='?')
187 					r = number(md, t, r, 1, prevc, Line, evalp);	/* do previous one */
188 			dir = c;
189 			break;
190 		case '.':
191 		case '$':
192 			if(q != q0+1){
193 				*qp = q-1;
194 				return r;
195 			}
196 			if(*evalp)
197 				if(c == '.')
198 					r = ar;
199 				else
200 					r = (Range){t->file->nc, t->file->nc};
201 			if(q < q1)
202 				dir = Fore;
203 			else
204 				dir = None;
205 			break;
206 		case '#':
207 			if(q==q1 || (c=(*getc)(a, q++))<'0' || '9'<c){
208 				*qp = q-1;
209 				return r;
210 			}
211 			size = Char;
212 			/* fall through */
213 		case '0': case '1': case '2': case '3': case '4':
214 		case '5': case '6': case '7': case '8': case '9':
215 			n = c -'0';
216 			while(q<q1){
217 				c = (*getc)(a, q++);
218 				if(c<'0' || '9'<c){
219 					q--;
220 					break;
221 				}
222 				n = n*10+(c-'0');
223 			}
224 			if(*evalp)
225 				r = number(md, t, r, n, dir, size, evalp);
226 			dir = None;
227 			size = Line;
228 			break;
229 		case '?':
230 			dir = Back;
231 			/* fall through */
232 		case '/':
233 			npat = 0;
234 			pat = nil;
235 			while(q<q1){
236 				c = (*getc)(a, q++);
237 				switch(c){
238 				case '\n':
239 					--q;
240 					goto out;
241 				case '\\':
242 					pat = runerealloc(pat, npat+1);
243 					pat[npat++] = c;
244 					if(q == q1)
245 						goto out;
246 					c = (*getc)(a, q++);
247 					break;
248 				case '/':
249 					goto out;
250 				}
251 				pat = runerealloc(pat, npat+1);
252 				pat[npat++] = c;
253 			}
254 		    out:
255 			pat = runerealloc(pat, npat+1);
256 			pat[npat] = 0;
257 			if(*evalp)
258 				r = regexp(md, t, lim, r, pat, dir, evalp);
259 			free(pat);
260 			dir = None;
261 			size = Line;
262 			break;
263 		}
264 	}
265 	if(*evalp && dir != None)
266 		r = number(md, t, r, 1, dir, Line, evalp);	/* do previous one */
267 	*qp = q;
268 	return r;
269 }
270