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