|  | #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(uint showerr, 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==1 && q1==t->file->b.nc) // 6 goes to end of 5-line file | 
|  | break; | 
|  | if(line > 0) | 
|  | goto Rescue; | 
|  | break; | 
|  | case Fore: | 
|  | if(q1 > 0) | 
|  | while(q1<t->file->b.nc && 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; | 
|  | } | 
|  | /* :1-1 is :0 = #0, but :1-2 is an error */ | 
|  | if(line > 1) | 
|  | goto Rescue; | 
|  | while(q0>0 && textreadc(t, q0-1)!='\n') | 
|  | --q0; | 
|  | } | 
|  | *evalp = TRUE; | 
|  | return range(q0, q1); | 
|  |  | 
|  | Rescue: | 
|  | if(showerr) | 
|  | warning(nil, "address out of range\n"); | 
|  | *evalp = FALSE; | 
|  | return r; | 
|  | } | 
|  |  | 
|  |  | 
|  | Range | 
|  | regexp(uint showerr, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp) | 
|  | { | 
|  | int found; | 
|  | Rangeset sel; | 
|  | int q; | 
|  |  | 
|  | if(pat[0] == '\0' && rxnull()){ | 
|  | if(showerr) | 
|  | warning(nil, "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 && showerr) | 
|  | warning(nil, "no match for regexp\n"); | 
|  | *foundp = found; | 
|  | return sel.r[0]; | 
|  | } | 
|  |  | 
|  | Range | 
|  | address(uint showerr, 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(showerr, 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(showerr, 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(showerr, 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(showerr, t, lim, r, pat, dir, evalp); | 
|  | free(pat); | 
|  | dir = None; | 
|  | size = Line; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if(*evalp && dir != None) | 
|  | r = number(showerr, t, r, 1, dir, Line, evalp);	/* do previous one */ | 
|  | *qp = q; | 
|  | return r; | 
|  | } |