master addr.c
ohmu  ·  2026-09-13
  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
 15enum
 16{
 17	None = 0,
 18	Fore = '+',
 19	Back = '-'
 20};
 21
 22enum
 23{
 24	Char,
 25	Line
 26};
 27
 28int
 29isaddrc(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 */
 40int
 41isregexc(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.
 57long
 58nlcounttopos(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
 73Range
 74number(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
142Range
143regexp(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
174Range
175address(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				c = (*getc)(a, q++);
244				if(c<'0' || '9'<c){
245					q--;
246					break;
247				}
248				n = n*10+(c-'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}