Blob
- Date:
- Message:
- acme: Apply each -/+ only once (#156) When plumbing an address like `3-`, Acme selects line 1, and similarly `3+` selects line 5. The same problem can be observed for character addresses (`#123+`) but _not_ for ones like `+`, `.+` or `/foo/+`: The problem only occurs when a number is followed by a direction (`-`/`+`). Following along with the example `3-` through `address` (in addr.c): We read `3` into `c` and match the `case` on line 239. The `while` loop on line 242ff reads additional digits into `c` and puts the first non-digit back by decrementing the index `q`. Then we find the range for line 3 on line 251 and continue. On the next iteration, we set `prevc` to the last `c`, but since that part read ahead _into `c`_, `c` is currently the _next_ character we will read, `-`, and now `prevc` is too. Then in the case block (line 210) the condition on line 211 holds and Acme believes that it has read two `-` in sequence and modifies the range to account for the “first” `-`. The “second” `-` gets applied after the loop is done, on line 292. So the general problem is: While reading numbers, Acme reads the next character after the number into `c`. It decrements the counter to ensure it will read it again on the next iteration, but it still uses it to update `prevc`. This change solves the problem by reading digits into `nc` instead. This variable is used to similar effect in the block for directions (line 212) and fills the role of “local `c` that we can safely use to read ahead” nicely.
- Actions:
- History | Blame | Raw File
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"15 enum16 {17 None = 0,18 Fore = '+',19 Back = '-'20 };22 enum23 {24 Char,25 Line26 };28 int29 isaddrc(int r)30 {31 if(r && utfrune("0123456789+-/$.#,;?", r)!=nil)32 return TRUE;33 return FALSE;34 }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 space39 */40 int41 isregexc(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 }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 past55 // the end of the current line.56 // It returns the final position.57 long58 nlcounttopos(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 }73 Range74 number(uint showerr, Text *t, Range r, int line, int dir, int size, int *evalp)75 {76 uint q0, q1;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 file103 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);134 Rescue:135 if(showerr)136 warning(nil, "address out of range\n");137 *evalp = FALSE;138 return r;139 }142 Range143 regexp(uint showerr, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp)144 {145 int found;146 Rangeset sel;147 int q;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 else165 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 }174 Range175 address(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;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 else226 r = range(t->file->b.nc, t->file->b.nc);227 if(q < q1)228 dir = Fore;229 else230 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 nc = (*getc)(a, q++);244 if(nc<'0' || '9'<nc){245 q--;246 break;247 }248 n = n*10+(nc-'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 }