1 |
|
|
/* $OpenBSD: search.c,v 1.46 2017/08/06 04:39:45 bcallah Exp $ */ |
2 |
|
|
|
3 |
|
|
/* This file is in the public domain. */ |
4 |
|
|
|
5 |
|
|
/* |
6 |
|
|
* Search commands. |
7 |
|
|
* The functions in this file implement the search commands (both plain and |
8 |
|
|
* incremental searches are supported) and the query-replace command. |
9 |
|
|
* |
10 |
|
|
* The plain old search code is part of the original MicroEMACS "distribution". |
11 |
|
|
* The incremental search code and the query-replace code is by Rich Ellison. |
12 |
|
|
*/ |
13 |
|
|
|
14 |
|
|
#include <sys/queue.h> |
15 |
|
|
#include <ctype.h> |
16 |
|
|
#include <signal.h> |
17 |
|
|
#include <stdio.h> |
18 |
|
|
#include <string.h> |
19 |
|
|
|
20 |
|
|
#include "def.h" |
21 |
|
|
#include "macro.h" |
22 |
|
|
|
23 |
|
|
#define SRCH_BEGIN (0) /* Search sub-codes. */ |
24 |
|
|
#define SRCH_FORW (-1) |
25 |
|
|
#define SRCH_BACK (-2) |
26 |
|
|
#define SRCH_NOPR (-3) |
27 |
|
|
#define SRCH_ACCM (-4) |
28 |
|
|
#define SRCH_MARK (-5) |
29 |
|
|
|
30 |
|
|
struct srchcom { |
31 |
|
|
int s_code; |
32 |
|
|
struct line *s_dotp; |
33 |
|
|
int s_doto; |
34 |
|
|
int s_dotline; |
35 |
|
|
}; |
36 |
|
|
|
37 |
|
|
static int isearch(int); |
38 |
|
|
static void is_cpush(int); |
39 |
|
|
static void is_lpush(void); |
40 |
|
|
static void is_pop(void); |
41 |
|
|
static int is_peek(void); |
42 |
|
|
static void is_undo(int *, int *); |
43 |
|
|
static int is_find(int); |
44 |
|
|
static void is_prompt(int, int, int); |
45 |
|
|
static void is_dspl(char *, int); |
46 |
|
|
static int eq(int, int, int); |
47 |
|
|
|
48 |
|
|
static struct srchcom cmds[NSRCH]; |
49 |
|
|
static int cip; |
50 |
|
|
|
51 |
|
|
int srch_lastdir = SRCH_NOPR; /* Last search flags. */ |
52 |
|
|
|
53 |
|
|
/* |
54 |
|
|
* Search forward. Get a search string from the user, and search for it |
55 |
|
|
* starting at ".". If found, "." gets moved to just after the matched |
56 |
|
|
* characters, and display does all the hard stuff. If not found, it just |
57 |
|
|
* prints a message. |
58 |
|
|
*/ |
59 |
|
|
/* ARGSUSED */ |
60 |
|
|
int |
61 |
|
|
forwsearch(int f, int n) |
62 |
|
|
{ |
63 |
|
|
int s; |
64 |
|
|
|
65 |
|
|
if ((s = readpattern("Search")) != TRUE) |
66 |
|
|
return (s); |
67 |
|
|
if (forwsrch() == FALSE) { |
68 |
|
|
dobeep(); |
69 |
|
|
ewprintf("Search failed: \"%s\"", pat); |
70 |
|
|
return (FALSE); |
71 |
|
|
} |
72 |
|
|
srch_lastdir = SRCH_FORW; |
73 |
|
|
return (TRUE); |
74 |
|
|
} |
75 |
|
|
|
76 |
|
|
/* |
77 |
|
|
* Reverse search. Get a search string from the user, and search, starting |
78 |
|
|
* at "." and proceeding toward the front of the buffer. If found "." is |
79 |
|
|
* left pointing at the first character of the pattern [the last character |
80 |
|
|
* that was matched]. |
81 |
|
|
*/ |
82 |
|
|
/* ARGSUSED */ |
83 |
|
|
int |
84 |
|
|
backsearch(int f, int n) |
85 |
|
|
{ |
86 |
|
|
int s; |
87 |
|
|
|
88 |
|
|
if ((s = readpattern("Search backward")) != TRUE) |
89 |
|
|
return (s); |
90 |
|
|
if (backsrch() == FALSE) { |
91 |
|
|
dobeep(); |
92 |
|
|
ewprintf("Search failed: \"%s\"", pat); |
93 |
|
|
return (FALSE); |
94 |
|
|
} |
95 |
|
|
srch_lastdir = SRCH_BACK; |
96 |
|
|
return (TRUE); |
97 |
|
|
} |
98 |
|
|
|
99 |
|
|
/* |
100 |
|
|
* Search again, using the same search string and direction as the last |
101 |
|
|
* search command. The direction has been saved in "srch_lastdir", so you |
102 |
|
|
* know which way to go. |
103 |
|
|
*/ |
104 |
|
|
/* ARGSUSED */ |
105 |
|
|
int |
106 |
|
|
searchagain(int f, int n) |
107 |
|
|
{ |
108 |
|
|
if (srch_lastdir == SRCH_FORW) { |
109 |
|
|
if (forwsrch() == FALSE) { |
110 |
|
|
dobeep(); |
111 |
|
|
ewprintf("Search failed: \"%s\"", pat); |
112 |
|
|
return (FALSE); |
113 |
|
|
} |
114 |
|
|
return (TRUE); |
115 |
|
|
} |
116 |
|
|
if (srch_lastdir == SRCH_BACK) { |
117 |
|
|
if (backsrch() == FALSE) { |
118 |
|
|
dobeep(); |
119 |
|
|
ewprintf("Search failed: \"%s\"", pat); |
120 |
|
|
return (FALSE); |
121 |
|
|
} |
122 |
|
|
return (TRUE); |
123 |
|
|
} |
124 |
|
|
dobeep(); |
125 |
|
|
ewprintf("No last search"); |
126 |
|
|
return (FALSE); |
127 |
|
|
} |
128 |
|
|
|
129 |
|
|
/* |
130 |
|
|
* Use incremental searching, initially in the forward direction. |
131 |
|
|
* isearch ignores any explicit arguments. |
132 |
|
|
*/ |
133 |
|
|
/* ARGSUSED */ |
134 |
|
|
int |
135 |
|
|
forwisearch(int f, int n) |
136 |
|
|
{ |
137 |
|
|
if (macrodef || inmacro) |
138 |
|
|
/* We can't isearch in macro. Use search instead */ |
139 |
|
|
return (forwsearch(f,n)); |
140 |
|
|
else |
141 |
|
|
return (isearch(SRCH_FORW)); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
/* |
145 |
|
|
* Use incremental searching, initially in the reverse direction. |
146 |
|
|
* isearch ignores any explicit arguments. |
147 |
|
|
*/ |
148 |
|
|
/* ARGSUSED */ |
149 |
|
|
int |
150 |
|
|
backisearch(int f, int n) |
151 |
|
|
{ |
152 |
|
|
if (macrodef || inmacro) |
153 |
|
|
/* We can't isearch in macro. Use search instead */ |
154 |
|
|
return (backsearch(f,n)); |
155 |
|
|
else |
156 |
|
|
return (isearch(SRCH_BACK)); |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
/* |
160 |
|
|
* Incremental Search. |
161 |
|
|
* dir is used as the initial direction to search. |
162 |
|
|
* ^S switch direction to forward |
163 |
|
|
* ^R switch direction to reverse |
164 |
|
|
* ^Q quote next character (allows searching for ^N etc.) |
165 |
|
|
* <ESC> exit from Isearch |
166 |
|
|
* <DEL> undoes last character typed. (tricky job to do this correctly). |
167 |
|
|
* other ^ exit search, don't set mark |
168 |
|
|
* else accumulate into search string |
169 |
|
|
*/ |
170 |
|
|
static int |
171 |
|
|
isearch(int dir) |
172 |
|
|
{ |
173 |
|
|
struct line *clp; /* Saved line pointer */ |
174 |
|
|
int c; |
175 |
|
|
int cbo; /* Saved offset */ |
176 |
|
|
int success; |
177 |
|
|
int pptr; |
178 |
|
|
int firstc; |
179 |
|
|
int xcase; |
180 |
|
|
int i; |
181 |
|
|
char opat[NPAT]; |
182 |
|
|
int cdotline; /* Saved line number */ |
183 |
|
|
|
184 |
|
|
if (macrodef) { |
185 |
|
|
dobeep(); |
186 |
|
|
ewprintf("Can't isearch in macro"); |
187 |
|
|
return (FALSE); |
188 |
|
|
} |
189 |
|
|
for (cip = 0; cip < NSRCH; cip++) |
190 |
|
|
cmds[cip].s_code = SRCH_NOPR; |
191 |
|
|
|
192 |
|
|
(void)strlcpy(opat, pat, sizeof(opat)); |
193 |
|
|
cip = 0; |
194 |
|
|
pptr = -1; |
195 |
|
|
clp = curwp->w_dotp; |
196 |
|
|
cbo = curwp->w_doto; |
197 |
|
|
cdotline = curwp->w_dotline; |
198 |
|
|
is_lpush(); |
199 |
|
|
is_cpush(SRCH_BEGIN); |
200 |
|
|
success = TRUE; |
201 |
|
|
is_prompt(dir, TRUE, success); |
202 |
|
|
|
203 |
|
|
for (;;) { |
204 |
|
|
update(CMODE); |
205 |
|
|
|
206 |
|
|
switch (c = getkey(FALSE)) { |
207 |
|
|
case CCHR('['): |
208 |
|
|
/* |
209 |
|
|
* If new characters come in the next 300 msec, |
210 |
|
|
* we can assume that they belong to a longer |
211 |
|
|
* escaped sequence so we should ungetkey the |
212 |
|
|
* ESC to avoid writing out garbage. |
213 |
|
|
*/ |
214 |
|
|
if (ttwait(300) == FALSE) |
215 |
|
|
ungetkey(c); |
216 |
|
|
srch_lastdir = dir; |
217 |
|
|
curwp->w_markp = clp; |
218 |
|
|
curwp->w_marko = cbo; |
219 |
|
|
curwp->w_markline = cdotline; |
220 |
|
|
ewprintf("Mark set"); |
221 |
|
|
return (TRUE); |
222 |
|
|
case CCHR('G'): |
223 |
|
|
if (success != TRUE) { |
224 |
|
|
while (is_peek() == SRCH_ACCM) |
225 |
|
|
is_undo(&pptr, &dir); |
226 |
|
|
success = TRUE; |
227 |
|
|
is_prompt(dir, pptr < 0, success); |
228 |
|
|
break; |
229 |
|
|
} |
230 |
|
|
curwp->w_dotp = clp; |
231 |
|
|
curwp->w_doto = cbo; |
232 |
|
|
curwp->w_dotline = cdotline; |
233 |
|
|
curwp->w_rflag |= WFMOVE; |
234 |
|
|
srch_lastdir = dir; |
235 |
|
|
(void)ctrlg(FFRAND, 0); |
236 |
|
|
(void)strlcpy(pat, opat, sizeof(pat)); |
237 |
|
|
return (ABORT); |
238 |
|
|
case CCHR('S'): |
239 |
|
|
if (dir == SRCH_BACK) { |
240 |
|
|
dir = SRCH_FORW; |
241 |
|
|
is_lpush(); |
242 |
|
|
is_cpush(SRCH_FORW); |
243 |
|
|
success = TRUE; |
244 |
|
|
} |
245 |
|
|
if (success == FALSE && dir == SRCH_FORW) { |
246 |
|
|
/* wrap the search to beginning */ |
247 |
|
|
curwp->w_dotp = bfirstlp(curbp); |
248 |
|
|
curwp->w_doto = 0; |
249 |
|
|
curwp->w_dotline = 1; |
250 |
|
|
if (is_find(dir) != FALSE) { |
251 |
|
|
is_cpush(SRCH_MARK); |
252 |
|
|
success = TRUE; |
253 |
|
|
} |
254 |
|
|
ewprintf("Overwrapped I-search: %s", pat); |
255 |
|
|
break; |
256 |
|
|
} |
257 |
|
|
is_lpush(); |
258 |
|
|
pptr = strlen(pat); |
259 |
|
|
if (forwchar(FFRAND, 1) == FALSE) { |
260 |
|
|
dobeep(); |
261 |
|
|
success = FALSE; |
262 |
|
|
ewprintf("Failed I-search: %s", pat); |
263 |
|
|
} else { |
264 |
|
|
if (is_find(SRCH_FORW) != FALSE) |
265 |
|
|
is_cpush(SRCH_MARK); |
266 |
|
|
else { |
267 |
|
|
(void)backchar(FFRAND, 1); |
268 |
|
|
dobeep(); |
269 |
|
|
success = FALSE; |
270 |
|
|
ewprintf("Failed I-search: %s", pat); |
271 |
|
|
} |
272 |
|
|
} |
273 |
|
|
is_prompt(dir, pptr < 0, success); |
274 |
|
|
break; |
275 |
|
|
case CCHR('R'): |
276 |
|
|
if (dir == SRCH_FORW) { |
277 |
|
|
dir = SRCH_BACK; |
278 |
|
|
is_lpush(); |
279 |
|
|
is_cpush(SRCH_BACK); |
280 |
|
|
success = TRUE; |
281 |
|
|
} |
282 |
|
|
if (success == FALSE && dir == SRCH_BACK) { |
283 |
|
|
/* wrap the search to end */ |
284 |
|
|
curwp->w_dotp = blastlp(curbp); |
285 |
|
|
curwp->w_doto = llength(curwp->w_dotp); |
286 |
|
|
curwp->w_dotline = curwp->w_bufp->b_lines; |
287 |
|
|
if (is_find(dir) != FALSE) { |
288 |
|
|
is_cpush(SRCH_MARK); |
289 |
|
|
success = TRUE; |
290 |
|
|
} |
291 |
|
|
ewprintf("Overwrapped I-search: %s", pat); |
292 |
|
|
break; |
293 |
|
|
} |
294 |
|
|
is_lpush(); |
295 |
|
|
pptr = strlen(pat); |
296 |
|
|
if (backchar(FFRAND, 1) == FALSE) { |
297 |
|
|
dobeep(); |
298 |
|
|
success = FALSE; |
299 |
|
|
} else { |
300 |
|
|
if (is_find(SRCH_BACK) != FALSE) |
301 |
|
|
is_cpush(SRCH_MARK); |
302 |
|
|
else { |
303 |
|
|
(void)forwchar(FFRAND, 1); |
304 |
|
|
dobeep(); |
305 |
|
|
success = FALSE; |
306 |
|
|
} |
307 |
|
|
} |
308 |
|
|
is_prompt(dir, pptr < 0, success); |
309 |
|
|
break; |
310 |
|
|
case CCHR('W'): |
311 |
|
|
/* add the rest of the current word to the pattern */ |
312 |
|
|
clp = curwp->w_dotp; |
313 |
|
|
cbo = curwp->w_doto; |
314 |
|
|
firstc = 1; |
315 |
|
|
if (pptr == -1) |
316 |
|
|
pptr = 0; |
317 |
|
|
if (dir == SRCH_BACK) { |
318 |
|
|
/* when isearching backwards, cbo is the start of the pattern */ |
319 |
|
|
cbo += pptr; |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
/* if the search is case insensitive, add to pattern using lowercase */ |
323 |
|
|
xcase = 0; |
324 |
|
|
for (i = 0; pat[i]; i++) |
325 |
|
|
if (ISUPPER(CHARMASK(pat[i]))) |
326 |
|
|
xcase = 1; |
327 |
|
|
|
328 |
|
|
while (cbo < llength(clp)) { |
329 |
|
|
c = lgetc(clp, cbo++); |
330 |
|
|
if ((!firstc && !isalnum(c))) |
331 |
|
|
break; |
332 |
|
|
|
333 |
|
|
if (pptr == NPAT - 1) { |
334 |
|
|
dobeep(); |
335 |
|
|
break; |
336 |
|
|
} |
337 |
|
|
firstc = 0; |
338 |
|
|
if (!xcase && ISUPPER(c)) |
339 |
|
|
c = TOLOWER(c); |
340 |
|
|
|
341 |
|
|
pat[pptr++] = c; |
342 |
|
|
pat[pptr] = '\0'; |
343 |
|
|
/* cursor only moves when isearching forwards */ |
344 |
|
|
if (dir == SRCH_FORW) { |
345 |
|
|
curwp->w_doto = cbo; |
346 |
|
|
curwp->w_rflag |= WFMOVE; |
347 |
|
|
update(CMODE); |
348 |
|
|
} |
349 |
|
|
} |
350 |
|
|
is_prompt(dir, pptr < 0, success); |
351 |
|
|
break; |
352 |
|
|
case CCHR('H'): |
353 |
|
|
case CCHR('?'): |
354 |
|
|
is_undo(&pptr, &dir); |
355 |
|
|
if (is_peek() != SRCH_ACCM) |
356 |
|
|
success = TRUE; |
357 |
|
|
is_prompt(dir, pptr < 0, success); |
358 |
|
|
break; |
359 |
|
|
case CCHR('\\'): |
360 |
|
|
case CCHR('Q'): |
361 |
|
|
c = (char)getkey(FALSE); |
362 |
|
|
goto addchar; |
363 |
|
|
case CCHR('M'): |
364 |
|
|
c = CCHR('J'); |
365 |
|
|
goto addchar; |
366 |
|
|
default: |
367 |
|
|
if (ISCTRL(c)) { |
368 |
|
|
ungetkey(c); |
369 |
|
|
curwp->w_markp = clp; |
370 |
|
|
curwp->w_marko = cbo; |
371 |
|
|
curwp->w_markline = cdotline; |
372 |
|
|
ewprintf("Mark set"); |
373 |
|
|
curwp->w_rflag |= WFMOVE; |
374 |
|
|
return (TRUE); |
375 |
|
|
} |
376 |
|
|
/* FALLTHRU */ |
377 |
|
|
case CCHR('I'): |
378 |
|
|
case CCHR('J'): |
379 |
|
|
addchar: |
380 |
|
|
if (pptr == -1) |
381 |
|
|
pptr = 0; |
382 |
|
|
if (pptr == 0) |
383 |
|
|
success = TRUE; |
384 |
|
|
if (pptr == NPAT - 1) |
385 |
|
|
dobeep(); |
386 |
|
|
else { |
387 |
|
|
pat[pptr++] = c; |
388 |
|
|
pat[pptr] = '\0'; |
389 |
|
|
} |
390 |
|
|
is_lpush(); |
391 |
|
|
if (success != FALSE) { |
392 |
|
|
if (is_find(dir) != FALSE) |
393 |
|
|
is_cpush(c); |
394 |
|
|
else { |
395 |
|
|
success = FALSE; |
396 |
|
|
dobeep(); |
397 |
|
|
is_cpush(SRCH_ACCM); |
398 |
|
|
} |
399 |
|
|
} else |
400 |
|
|
is_cpush(SRCH_ACCM); |
401 |
|
|
is_prompt(dir, FALSE, success); |
402 |
|
|
} |
403 |
|
|
} |
404 |
|
|
/* NOTREACHED */ |
405 |
|
|
} |
406 |
|
|
|
407 |
|
|
static void |
408 |
|
|
is_cpush(int cmd) |
409 |
|
|
{ |
410 |
|
|
if (++cip >= NSRCH) |
411 |
|
|
cip = 0; |
412 |
|
|
cmds[cip].s_code = cmd; |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
static void |
416 |
|
|
is_lpush(void) |
417 |
|
|
{ |
418 |
|
|
int ctp; |
419 |
|
|
|
420 |
|
|
ctp = cip + 1; |
421 |
|
|
if (ctp >= NSRCH) |
422 |
|
|
ctp = 0; |
423 |
|
|
cmds[ctp].s_code = SRCH_NOPR; |
424 |
|
|
cmds[ctp].s_doto = curwp->w_doto; |
425 |
|
|
cmds[ctp].s_dotp = curwp->w_dotp; |
426 |
|
|
cmds[ctp].s_dotline = curwp->w_dotline; |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
static void |
430 |
|
|
is_pop(void) |
431 |
|
|
{ |
432 |
|
|
if (cmds[cip].s_code != SRCH_NOPR) { |
433 |
|
|
curwp->w_doto = cmds[cip].s_doto; |
434 |
|
|
curwp->w_dotp = cmds[cip].s_dotp; |
435 |
|
|
curwp->w_dotline = cmds[cip].s_dotline; |
436 |
|
|
curwp->w_rflag |= WFMOVE; |
437 |
|
|
cmds[cip].s_code = SRCH_NOPR; |
438 |
|
|
} |
439 |
|
|
if (--cip <= 0) |
440 |
|
|
cip = NSRCH - 1; |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
static int |
444 |
|
|
is_peek(void) |
445 |
|
|
{ |
446 |
|
|
return (cmds[cip].s_code); |
447 |
|
|
} |
448 |
|
|
|
449 |
|
|
/* this used to always return TRUE (the return value was checked) */ |
450 |
|
|
static void |
451 |
|
|
is_undo(int *pptr, int *dir) |
452 |
|
|
{ |
453 |
|
|
int redo = FALSE; |
454 |
|
|
|
455 |
|
|
switch (cmds[cip].s_code) { |
456 |
|
|
case SRCH_BEGIN: |
457 |
|
|
case SRCH_NOPR: |
458 |
|
|
*pptr = -1; |
459 |
|
|
break; |
460 |
|
|
case SRCH_MARK: |
461 |
|
|
break; |
462 |
|
|
case SRCH_FORW: |
463 |
|
|
*dir = SRCH_BACK; |
464 |
|
|
redo = TRUE; |
465 |
|
|
break; |
466 |
|
|
case SRCH_BACK: |
467 |
|
|
*dir = SRCH_FORW; |
468 |
|
|
redo = TRUE; |
469 |
|
|
break; |
470 |
|
|
case SRCH_ACCM: |
471 |
|
|
default: |
472 |
|
|
*pptr -= 1; |
473 |
|
|
if (*pptr < 0) |
474 |
|
|
*pptr = 0; |
475 |
|
|
pat[*pptr] = '\0'; |
476 |
|
|
break; |
477 |
|
|
} |
478 |
|
|
is_pop(); |
479 |
|
|
if (redo) |
480 |
|
|
is_undo(pptr, dir); |
481 |
|
|
} |
482 |
|
|
|
483 |
|
|
static int |
484 |
|
|
is_find(int dir) |
485 |
|
|
{ |
486 |
|
|
int plen, odoto, odotline; |
487 |
|
|
struct line *odotp; |
488 |
|
|
|
489 |
|
|
odoto = curwp->w_doto; |
490 |
|
|
odotp = curwp->w_dotp; |
491 |
|
|
odotline = curwp->w_dotline; |
492 |
|
|
plen = strlen(pat); |
493 |
|
|
if (plen != 0) { |
494 |
|
|
if (dir == SRCH_FORW) { |
495 |
|
|
(void)backchar(FFARG | FFRAND, plen); |
496 |
|
|
if (forwsrch() == FALSE) { |
497 |
|
|
curwp->w_doto = odoto; |
498 |
|
|
curwp->w_dotp = odotp; |
499 |
|
|
curwp->w_dotline = odotline; |
500 |
|
|
return (FALSE); |
501 |
|
|
} |
502 |
|
|
return (TRUE); |
503 |
|
|
} |
504 |
|
|
if (dir == SRCH_BACK) { |
505 |
|
|
(void)forwchar(FFARG | FFRAND, plen); |
506 |
|
|
if (backsrch() == FALSE) { |
507 |
|
|
curwp->w_doto = odoto; |
508 |
|
|
curwp->w_dotp = odotp; |
509 |
|
|
curwp->w_dotline = odotline; |
510 |
|
|
return (FALSE); |
511 |
|
|
} |
512 |
|
|
return (TRUE); |
513 |
|
|
} |
514 |
|
|
dobeep(); |
515 |
|
|
ewprintf("bad call to is_find"); |
516 |
|
|
return (FALSE); |
517 |
|
|
} |
518 |
|
|
return (FALSE); |
519 |
|
|
} |
520 |
|
|
|
521 |
|
|
/* |
522 |
|
|
* If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used |
523 |
|
|
* to print an error message. It also used to return TRUE or FALSE, depending |
524 |
|
|
* on if it liked the "dir". However, none of the callers looked at the |
525 |
|
|
* status, so I just made the checking vanish. |
526 |
|
|
*/ |
527 |
|
|
static void |
528 |
|
|
is_prompt(int dir, int flag, int success) |
529 |
|
|
{ |
530 |
|
|
if (dir == SRCH_FORW) { |
531 |
|
|
if (success != FALSE) |
532 |
|
|
is_dspl("I-search", flag); |
533 |
|
|
else |
534 |
|
|
is_dspl("Failing I-search", flag); |
535 |
|
|
} else if (dir == SRCH_BACK) { |
536 |
|
|
if (success != FALSE) |
537 |
|
|
is_dspl("I-search backward", flag); |
538 |
|
|
else |
539 |
|
|
is_dspl("Failing I-search backward", flag); |
540 |
|
|
} else |
541 |
|
|
ewprintf("Broken call to is_prompt"); |
542 |
|
|
} |
543 |
|
|
|
544 |
|
|
/* |
545 |
|
|
* Prompt writing routine for the incremental search. The "i_prompt" is just |
546 |
|
|
* a string. The "flag" determines whether pat should be printed. |
547 |
|
|
*/ |
548 |
|
|
static void |
549 |
|
|
is_dspl(char *i_prompt, int flag) |
550 |
|
|
{ |
551 |
|
|
if (flag != FALSE) |
552 |
|
|
ewprintf("%s: ", i_prompt); |
553 |
|
|
else |
554 |
|
|
ewprintf("%s: %s", i_prompt, pat); |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
/* |
558 |
|
|
* Query Replace. |
559 |
|
|
* Replace strings selectively. Does a search and replace operation. |
560 |
|
|
*/ |
561 |
|
|
/* ARGSUSED */ |
562 |
|
|
int |
563 |
|
|
queryrepl(int f, int n) |
564 |
|
|
{ |
565 |
|
|
int s; |
566 |
|
|
int rcnt = 0; /* replacements made so far */ |
567 |
|
|
int plen; /* length of found string */ |
568 |
|
|
char news[NPAT], *rep; /* replacement string */ |
569 |
|
|
|
570 |
|
|
if (macrodef) { |
571 |
|
|
dobeep(); |
572 |
|
|
ewprintf("Can't query replace in macro"); |
573 |
|
|
return (FALSE); |
574 |
|
|
} |
575 |
|
|
|
576 |
|
|
if ((s = readpattern("Query replace")) != TRUE) |
577 |
|
|
return (s); |
578 |
|
|
if ((rep = eread("Query replace %s with: ", news, NPAT, |
579 |
|
|
EFNUL | EFNEW | EFCR, pat)) == NULL) |
580 |
|
|
return (ABORT); |
581 |
|
|
else if (rep[0] == '\0') |
582 |
|
|
news[0] = '\0'; |
583 |
|
|
ewprintf("Query replacing %s with %s:", pat, news); |
584 |
|
|
plen = strlen(pat); |
585 |
|
|
|
586 |
|
|
/* |
587 |
|
|
* Search forward repeatedly, checking each time whether to insert |
588 |
|
|
* or not. The "!" case makes the check always true, so it gets put |
589 |
|
|
* into a tighter loop for efficiency. |
590 |
|
|
*/ |
591 |
|
|
while (forwsrch() == TRUE) { |
592 |
|
|
retry: |
593 |
|
|
update(CMODE); |
594 |
|
|
switch (getkey(FALSE)) { |
595 |
|
|
case 'y': |
596 |
|
|
case ' ': |
597 |
|
|
if (lreplace((RSIZE)plen, news) == FALSE) |
598 |
|
|
return (FALSE); |
599 |
|
|
rcnt++; |
600 |
|
|
break; |
601 |
|
|
case '.': |
602 |
|
|
if (lreplace((RSIZE)plen, news) == FALSE) |
603 |
|
|
return (FALSE); |
604 |
|
|
rcnt++; |
605 |
|
|
goto stopsearch; |
606 |
|
|
/* ^G, CR or ESC */ |
607 |
|
|
case CCHR('G'): |
608 |
|
|
(void)ctrlg(FFRAND, 0); |
609 |
|
|
goto stopsearch; |
610 |
|
|
case CCHR('['): |
611 |
|
|
case CCHR('M'): |
612 |
|
|
goto stopsearch; |
613 |
|
|
case '!': |
614 |
|
|
do { |
615 |
|
|
if (lreplace((RSIZE)plen, news) == FALSE) |
616 |
|
|
return (FALSE); |
617 |
|
|
rcnt++; |
618 |
|
|
} while (forwsrch() == TRUE); |
619 |
|
|
goto stopsearch; |
620 |
|
|
case 'n': |
621 |
|
|
case CCHR('H'): |
622 |
|
|
/* To not replace */ |
623 |
|
|
case CCHR('?'): |
624 |
|
|
break; |
625 |
|
|
default: |
626 |
|
|
ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit"); |
627 |
|
|
goto retry; |
628 |
|
|
} |
629 |
|
|
} |
630 |
|
|
stopsearch: |
631 |
|
|
curwp->w_rflag |= WFFULL; |
632 |
|
|
update(CMODE); |
633 |
|
|
if (rcnt == 1) |
634 |
|
|
ewprintf("Replaced 1 occurrence"); |
635 |
|
|
else |
636 |
|
|
ewprintf("Replaced %d occurrences", rcnt); |
637 |
|
|
return (TRUE); |
638 |
|
|
} |
639 |
|
|
|
640 |
|
|
/* |
641 |
|
|
* Replace string globally without individual prompting. |
642 |
|
|
*/ |
643 |
|
|
/* ARGSUSED */ |
644 |
|
|
int |
645 |
|
|
replstr(int f, int n) |
646 |
|
|
{ |
647 |
|
|
char news[NPAT]; |
648 |
|
|
int s, plen, rcnt = 0; |
649 |
|
|
char *r; |
650 |
|
|
|
651 |
|
|
if ((s = readpattern("Replace string")) != TRUE) |
652 |
|
|
return s; |
653 |
|
|
|
654 |
|
|
r = eread("Replace string %s with: ", news, NPAT, |
655 |
|
|
EFNUL | EFNEW | EFCR, pat); |
656 |
|
|
if (r == NULL) |
657 |
|
|
return (ABORT); |
658 |
|
|
|
659 |
|
|
plen = strlen(pat); |
660 |
|
|
while (forwsrch() == TRUE) { |
661 |
|
|
update(CMODE); |
662 |
|
|
if (lreplace((RSIZE)plen, news) == FALSE) |
663 |
|
|
return (FALSE); |
664 |
|
|
|
665 |
|
|
rcnt++; |
666 |
|
|
} |
667 |
|
|
|
668 |
|
|
curwp->w_rflag |= WFFULL; |
669 |
|
|
update(CMODE); |
670 |
|
|
|
671 |
|
|
if (rcnt == 1) |
672 |
|
|
ewprintf("Replaced 1 occurrence"); |
673 |
|
|
else |
674 |
|
|
ewprintf("Replaced %d occurrences", rcnt); |
675 |
|
|
|
676 |
|
|
return (TRUE); |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
/* |
680 |
|
|
* This routine does the real work of a forward search. The pattern is sitting |
681 |
|
|
* in the external variable "pat". If found, dot is updated, the window system |
682 |
|
|
* is notified of the change, and TRUE is returned. If the string isn't found, |
683 |
|
|
* FALSE is returned. |
684 |
|
|
*/ |
685 |
|
|
int |
686 |
|
|
forwsrch(void) |
687 |
|
|
{ |
688 |
|
|
struct line *clp, *tlp; |
689 |
|
|
int cbo, tbo, c, i, xcase = 0; |
690 |
|
|
char *pp; |
691 |
|
|
int nline; |
692 |
|
|
|
693 |
|
|
clp = curwp->w_dotp; |
694 |
|
|
cbo = curwp->w_doto; |
695 |
|
|
nline = curwp->w_dotline; |
696 |
|
|
for (i = 0; pat[i]; i++) |
697 |
|
|
if (ISUPPER(CHARMASK(pat[i]))) |
698 |
|
|
xcase = 1; |
699 |
|
|
for (;;) { |
700 |
|
|
if (cbo == llength(clp)) { |
701 |
|
|
if ((clp = lforw(clp)) == curbp->b_headp) |
702 |
|
|
break; |
703 |
|
|
nline++; |
704 |
|
|
cbo = 0; |
705 |
|
|
c = CCHR('J'); |
706 |
|
|
} else |
707 |
|
|
c = lgetc(clp, cbo++); |
708 |
|
|
if (eq(c, pat[0], xcase) != FALSE) { |
709 |
|
|
tlp = clp; |
710 |
|
|
tbo = cbo; |
711 |
|
|
pp = &pat[1]; |
712 |
|
|
while (*pp != 0) { |
713 |
|
|
if (tbo == llength(tlp)) { |
714 |
|
|
tlp = lforw(tlp); |
715 |
|
|
if (tlp == curbp->b_headp) |
716 |
|
|
goto fail; |
717 |
|
|
tbo = 0; |
718 |
|
|
c = CCHR('J'); |
719 |
|
|
if (eq(c, *pp++, xcase) == FALSE) |
720 |
|
|
goto fail; |
721 |
|
|
nline++; |
722 |
|
|
} else { |
723 |
|
|
c = lgetc(tlp, tbo++); |
724 |
|
|
if (eq(c, *pp++, xcase) == FALSE) |
725 |
|
|
goto fail; |
726 |
|
|
} |
727 |
|
|
} |
728 |
|
|
curwp->w_dotp = tlp; |
729 |
|
|
curwp->w_doto = tbo; |
730 |
|
|
curwp->w_dotline = nline; |
731 |
|
|
curwp->w_rflag |= WFMOVE; |
732 |
|
|
return (TRUE); |
733 |
|
|
} |
734 |
|
|
fail: ; |
735 |
|
|
} |
736 |
|
|
return (FALSE); |
737 |
|
|
} |
738 |
|
|
|
739 |
|
|
/* |
740 |
|
|
* This routine does the real work of a backward search. The pattern is |
741 |
|
|
* sitting in the external variable "pat". If found, dot is updated, the |
742 |
|
|
* window system is notified of the change, and TRUE is returned. If the |
743 |
|
|
* string isn't found, FALSE is returned. |
744 |
|
|
*/ |
745 |
|
|
int |
746 |
|
|
backsrch(void) |
747 |
|
|
{ |
748 |
|
|
struct line *clp, *tlp; |
749 |
|
|
int cbo, tbo, c, i, xcase = 0; |
750 |
|
|
char *epp, *pp; |
751 |
|
|
int nline, pline; |
752 |
|
|
|
753 |
|
|
for (epp = &pat[0]; epp[1] != 0; ++epp); |
754 |
|
|
clp = curwp->w_dotp; |
755 |
|
|
cbo = curwp->w_doto; |
756 |
|
|
nline = curwp->w_dotline; |
757 |
|
|
for (i = 0; pat[i]; i++) |
758 |
|
|
if (ISUPPER(CHARMASK(pat[i]))) |
759 |
|
|
xcase = 1; |
760 |
|
|
for (;;) { |
761 |
|
|
if (cbo == 0) { |
762 |
|
|
clp = lback(clp); |
763 |
|
|
if (clp == curbp->b_headp) |
764 |
|
|
return (FALSE); |
765 |
|
|
nline--; |
766 |
|
|
cbo = llength(clp) + 1; |
767 |
|
|
} |
768 |
|
|
if (--cbo == llength(clp)) |
769 |
|
|
c = CCHR('J'); |
770 |
|
|
else |
771 |
|
|
c = lgetc(clp, cbo); |
772 |
|
|
if (eq(c, *epp, xcase) != FALSE) { |
773 |
|
|
tlp = clp; |
774 |
|
|
tbo = cbo; |
775 |
|
|
pp = epp; |
776 |
|
|
pline = nline; |
777 |
|
|
while (pp != &pat[0]) { |
778 |
|
|
if (tbo == 0) { |
779 |
|
|
tlp = lback(tlp); |
780 |
|
|
if (tlp == curbp->b_headp) |
781 |
|
|
goto fail; |
782 |
|
|
nline--; |
783 |
|
|
tbo = llength(tlp) + 1; |
784 |
|
|
} |
785 |
|
|
if (--tbo == llength(tlp)) |
786 |
|
|
c = CCHR('J'); |
787 |
|
|
else |
788 |
|
|
c = lgetc(tlp, tbo); |
789 |
|
|
if (eq(c, *--pp, xcase) == FALSE) { |
790 |
|
|
nline = pline; |
791 |
|
|
goto fail; |
792 |
|
|
} |
793 |
|
|
} |
794 |
|
|
curwp->w_dotp = tlp; |
795 |
|
|
curwp->w_doto = tbo; |
796 |
|
|
curwp->w_dotline = nline; |
797 |
|
|
curwp->w_rflag |= WFMOVE; |
798 |
|
|
return (TRUE); |
799 |
|
|
} |
800 |
|
|
fail: ; |
801 |
|
|
} |
802 |
|
|
/* NOTREACHED */ |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
/* |
806 |
|
|
* Compare two characters. The "bc" comes from the buffer. It has its case |
807 |
|
|
* folded out. The "pc" is from the pattern. |
808 |
|
|
*/ |
809 |
|
|
static int |
810 |
|
|
eq(int bc, int pc, int xcase) |
811 |
|
|
{ |
812 |
|
|
bc = CHARMASK(bc); |
813 |
|
|
pc = CHARMASK(pc); |
814 |
|
|
if (bc == pc) |
815 |
|
|
return (TRUE); |
816 |
|
|
if (xcase) |
817 |
|
|
return (FALSE); |
818 |
|
|
if (ISUPPER(bc)) |
819 |
|
|
return (TOLOWER(bc) == pc); |
820 |
|
|
if (ISUPPER(pc)) |
821 |
|
|
return (bc == TOLOWER(pc)); |
822 |
|
|
return (FALSE); |
823 |
|
|
} |
824 |
|
|
|
825 |
|
|
/* |
826 |
|
|
* Read a pattern. Stash it in the external variable "pat". The "pat" is not |
827 |
|
|
* updated if the user types in an empty line. If the user typed an empty |
828 |
|
|
* line, and there is no old pattern, it is an error. Display the old pattern, |
829 |
|
|
* in the style of Jeff Lomicka. There is some do-it-yourself control |
830 |
|
|
* expansion. |
831 |
|
|
*/ |
832 |
|
|
int |
833 |
|
|
readpattern(char *r_prompt) |
834 |
|
|
{ |
835 |
|
|
char tpat[NPAT], *rep; |
836 |
|
|
int retval; |
837 |
|
|
|
838 |
|
|
if (pat[0] == '\0') |
839 |
|
|
rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt); |
840 |
|
|
else |
841 |
|
|
rep = eread("%s (default %s): ", tpat, NPAT, |
842 |
|
|
EFNUL | EFNEW | EFCR, r_prompt, pat); |
843 |
|
|
|
844 |
|
|
/* specified */ |
845 |
|
|
if (rep == NULL) { |
846 |
|
|
retval = ABORT; |
847 |
|
|
} else if (rep[0] != '\0') { |
848 |
|
|
(void)strlcpy(pat, tpat, sizeof(pat)); |
849 |
|
|
retval = TRUE; |
850 |
|
|
} else if (pat[0] != '\0') { |
851 |
|
|
retval = TRUE; |
852 |
|
|
} else |
853 |
|
|
retval = FALSE; |
854 |
|
|
return (retval); |
855 |
|
|
} |