2019-11-14 23:15:48 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <draw.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <cursor.h>
|
|
|
|
#include <mouse.h>
|
|
|
|
#include <keyboard.h>
|
2022-02-22 05:52:56 +00:00
|
|
|
#include "libframe/frame.h"
|
2019-11-14 23:15:48 +00:00
|
|
|
#include <fcall.h>
|
|
|
|
#include <plumb.h>
|
|
|
|
#include <libsec.h>
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
enum { None = 0, Fore = '+', Back = '-' };
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
enum { Char, Line };
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
int isaddrc(int r) {
|
|
|
|
if (r && utfrune("0123456789+-/$.#,;?", r) != nil)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-06-13 05:54:54 +00:00
|
|
|
* 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
|
2019-11-14 23:15:48 +00:00
|
|
|
*/
|
2021-06-13 05:54:54 +00:00
|
|
|
int isregexc(int r) {
|
|
|
|
if (r == 0)
|
|
|
|
return FALSE;
|
|
|
|
if (isalnum(r))
|
|
|
|
return TRUE;
|
|
|
|
if (utfrune("^+-.*?#,;[]()$", r) != nil)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// nlcounttopos starts at q0 and advances nl lines,
|
|
|
|
// being careful not to walk past the end of the text,
|
|
|
|
// and then nr chars, being careful not to walk past
|
|
|
|
// the end of the current line.
|
|
|
|
// It returns the final position.
|
2021-06-13 05:54:54 +00:00
|
|
|
long nlcounttopos(Text* t, long q0, long nl, long nr) {
|
|
|
|
while (nl > 0 && q0 < t->file->b.nc) {
|
|
|
|
if (textreadc(t, q0++) == '\n')
|
|
|
|
nl--;
|
|
|
|
}
|
|
|
|
if (nl > 0)
|
|
|
|
return q0;
|
|
|
|
while (nr > 0 && q0 < t->file->b.nc && textreadc(t, q0) != '\n') {
|
|
|
|
q0++;
|
|
|
|
nr--;
|
|
|
|
}
|
|
|
|
return q0;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
Range number(
|
|
|
|
uint showerr, Text* t, Range r, int line, int dir, int size, int* evalp) {
|
|
|
|
uint q0, q1;
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
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);
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
Rescue:
|
|
|
|
if (showerr)
|
|
|
|
warning(nil, "address out of range\n");
|
|
|
|
*evalp = FALSE;
|
|
|
|
return r;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
Range regexp(
|
|
|
|
uint showerr, Text* t, Range lim, Range r, Rune* pat, int dir, int* foundp) {
|
|
|
|
int found;
|
|
|
|
Rangeset sel;
|
|
|
|
int q;
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
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];
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
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;
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
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) {
|
|
|
|
nc = (*getc)(a, q++);
|
|
|
|
if (nc < '0' || '9' < nc) {
|
|
|
|
q--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
n = n * 10 + (nc - '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;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|