| #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; |
| } |