Bad remote file systems can change mtime unexpectedly, and then there is the problem that git rebase and similar operations like to change the files and then change them back, modifying the mtimes but not the content. Avoid spurious Put errors on both of those by checking file content. (False positive "modified since last read" make the real ones difficult to notice.)
275 lines
4.9 KiB
C
275 lines
4.9 KiB
C
#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 <libsec.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;
|
|
}
|