1 /* e_search.cpp
2 *
3 * Copyright (c) 1994-1996, Marko Macek
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Artistic License, as specified in the README file.
7 *
8 */
9
10 #include "c_history.h"
11 #include "e_tags.h"
12 #include "i_modelview.h"
13 #include "i_view.h"
14 #include "o_buflist.h"
15 #include "s_util.h"
16 #include "sysdep.h"
17
18 #include <ctype.h>
19
SearchReplaceOptions()20 SearchReplaceOptions::SearchReplaceOptions()
21 {
22 memset(this, 0, sizeof(*this));
23 }
24
25 // *INDENT-OFF*
ParseSearchOption(int replace,char c,unsigned long & opt)26 int ParseSearchOption(int replace, char c, unsigned long &opt) {
27 switch (tolower(c)) {
28 case 'a': opt |= SEARCH_ALL; break; // search all occurances
29 case 'b': opt |= SEARCH_BLOCK; break; // search in block only
30 case 'c': opt &= ~SEARCH_NEXT; break; // search from current position
31 case 'd': opt |= SEARCH_DELETE; break; // delete found line
32 case 'g': opt |= SEARCH_GLOBAL; break; // search globally
33 case 'i': opt |= SEARCH_NCASE; break; // don't match case
34 case 'j': opt |= SEARCH_JOIN; break; // join found line
35 case 'r': opt |= SEARCH_BACK; break; // search reverse
36 case 'w': opt |= SEARCH_WORDBEG | SEARCH_WORDEND; break;
37 case '<': opt |= SEARCH_WORDBEG; break;
38 case '>': opt |= SEARCH_WORDEND; break;
39 case 'x': opt |= SEARCH_RE; break; // search using regexps
40 default:
41 if (!replace) return 0;
42 switch (c) {
43 case 'n': opt |= SEARCH_NASK; break; // don't ask before replacing
44 default:
45 return 0;
46 }
47 }
48 return 1;
49 }
50
UnquoteString(char * str)51 static int UnquoteString(char *str) {
52 char *s, *d;
53
54 s = str;
55 d = str;
56 while (*s) {
57 if (*s == '\\') {
58 s++;
59 if (*s == 0) return 0;
60 }
61 *d++ = *s++;
62 }
63 *d = 0;
64 return 1;
65 }
66
ParseSearchOptions(int replace,const char * str,unsigned long & Options)67 int ParseSearchOptions(int replace, const char *str, unsigned long &Options) {
68 const char *p = str;
69
70 Options = SEARCH_NEXT;
71 while (*p) {
72 if (ParseSearchOption(replace, *p, Options) == 0) return 0;
73 p++;
74 }
75 return 1;
76 }
77
ParseSearchReplace(EBuffer * B,const char * str,int replace,SearchReplaceOptions & opt)78 int ParseSearchReplace(EBuffer *B, const char *str, int replace, SearchReplaceOptions &opt) {
79 int where = 0;
80 int len = 0;
81 const char *p = str;
82
83 memset((void *)&opt, 0, sizeof(opt));
84 if (p == 0) return 0;
85 if (replace) {
86 if (ParseSearchOptions(replace, BFS(B, BFS_DefFindReplaceOpt), opt.Options) == 0) return 0;
87 opt.Options |= SEARCH_REPLACE;
88 } else {
89 if (ParseSearchOptions(replace, BFS(B, BFS_DefFindOpt), opt.Options) == 0) return 0;
90 }
91
92 while (*p) {
93 switch (*p) {
94 case '\\':
95 if (where == 0) {
96 opt.strSearch[len++] = *p++;
97 if (*p == 0) return 0;
98 opt.strSearch[len++] = *p++;
99 } else if (where == 1) {
100 opt.strReplace[len++] = *p++;
101 if (*p == 0) return 0;
102 opt.strReplace[len++] = *p++;
103 } else
104 return 0;
105 break;
106 case '/':
107 where++;
108 if (!replace && where == 1) where++;
109 if (where == 2)
110 opt.Options = SEARCH_NEXT;
111 if (where > 2) return 0;
112 len = 0;
113 p++;
114 break;
115 default:
116 if (where == 0)
117 opt.strSearch[len++] = *p++;
118 else if (where == 1)
119 opt.strReplace[len++] = *p++;
120 else {
121 char c = *p;
122
123 p++;
124 if (ParseSearchOption(replace, c, opt.Options) == 0) return 0;
125 }
126 }
127 }
128 if (opt.Options & SEARCH_RE);
129 else {
130 if (UnquoteString(opt.strSearch) == 0) return 0;
131 if (opt.Options & SEARCH_REPLACE)
132 if (UnquoteString(opt.strReplace) == 0) return 0;
133 }
134
135 opt.ok = 1;
136 return 1;
137 }
138
FindStr(const char * Data,int Len,int Options)139 int EBuffer::FindStr(const char *Data, int Len, int Options) {
140 SearchReplaceOptions opt;
141
142 opt.Options = Options;
143
144 return FindStr(Data, Len, opt);
145 }
146
FindStr(const char * Data,int Len,SearchReplaceOptions & opt)147 int EBuffer::FindStr(const char *Data, int Len, SearchReplaceOptions &opt) {
148 int Options = opt.Options;
149 int LLen, Start, End;
150 int C, L;
151 PELine X;
152 char *P;
153
154 if (Options & SEARCH_RE)
155 return 0;
156 if (Len <= 0)
157 return 0;
158
159 if (Options & SEARCH_NOPOS) {
160 C = Match.Col;
161 L = Match.Row;
162 } else {
163 C = CP.Col;
164 L = VToR(CP.Row);
165 }
166 if (Match.Row != -1)
167 Draw(Match.Row, Match.Row);
168 Match.Row = -1;
169 Match.Col = -1;
170 X = RLine(L);
171 C = CharOffset(X, C);
172
173 if (Options & SEARCH_NEXT) {
174 int CC = MatchCount ? 1 : 0;
175
176 if (Options & SEARCH_BACK) {
177 C -= CC;
178 if (C < 0) {
179 if (L == 0) return 0;
180 L--;
181 X = RLine(L);
182 C = X->Count;
183 }
184 } else {
185 if (Options & SEARCH_REPLACE &&
186 opt.lastInsertLen > 0)
187 {
188 C += CC * opt.lastInsertLen; // 0 or opt.lastInsertLen
189 } else
190 {
191 C += CC;
192 }
193
194 if (C >= X->Count) {
195 C = 0;
196 L++;
197 if (L == RCount) return 0;
198 }
199 }
200 }
201 MatchLen = 0;
202 MatchCount = 0;
203
204 if (Options & SEARCH_BLOCK) {
205 if (Options & SEARCH_BACK) {
206 if (BlockMode == bmStream) {
207 if (L > BE.Row) {
208 L = BE.Row;
209 C = BE.Col;
210 }
211 if (L == BE.Row && C > BE.Col)
212 C = BE.Col;
213 } else {
214 if (L >= BE.Row && BE.Row > 0) {
215 L = BE.Row - 1;
216 C = RLine(L)->Count;
217 }
218 if (BlockMode == bmColumn)
219 if (L == BE.Row - 1 && C >= BE.Col)
220 C = BE.Col;
221 }
222 } else {
223 if (L < BB.Row) {
224 L = BB.Row;
225 C = 0;
226 }
227 if (L == BB.Row && C < BB.Col)
228 C = BB.Col;
229 }
230 }
231 while (1) {
232 if (Options & SEARCH_BLOCK) {
233 if (BlockMode == bmStream) {
234 if (L > BE.Row || L < BB.Row) break;
235 } else
236 if (L >= BE.Row || L < BB.Row) break;
237 } else
238 if (L >= RCount || L < 0) break;
239
240 X = RLine(L);
241
242 LLen = X->Count;
243 P = X->Chars;
244 Start = 0;
245 End = LLen;
246
247 if (Options & SEARCH_BLOCK) {
248 if (BlockMode == bmColumn) {
249 Start = CharOffset(X, BB.Col);
250 End = CharOffset(X, BE.Col);
251 } else if (BlockMode == bmStream) {
252 if (L == BB.Row)
253 Start = CharOffset(X, BB.Col);
254 if (L == BE.Row)
255 End = CharOffset(X, BE.Col);
256 }
257 }
258 if (Options & SEARCH_BACK) {
259 if (C >= End - Len)
260 C = End - Len;
261 } else {
262 if (C < Start)
263 C = Start;
264 }
265
266 while (((!(Options & SEARCH_BACK)) && (C <= End - Len))
267 || ((Options & SEARCH_BACK) && (C >= Start))) {
268 if ((!(Options & SEARCH_WORDBEG)
269 || (C == 0)
270 || (WGETBIT(Flags.WordChars, P[C - 1]) == 0))
271 &&
272 (!(Options & SEARCH_WORDEND)
273 || (C + Len >= End)
274 || (WGETBIT(Flags.WordChars, P[C + Len]) == 0))
275 &&
276 ((!(Options & SEARCH_NCASE)
277 && (P[C] == Data[0])
278 && (memcmp(P + C, Data, Len) == 0))
279 ||
280 ((Options & SEARCH_NCASE)
281 && (toupper(P[C]) == toupper(Data[0]))
282 && (strnicmp(P + C, Data, Len) == 0))) /* && BOL | EOL */
283 )
284 {
285 Match.Col = ScreenPos(X, C);
286 Match.Row = L;
287 MatchCount = Len;
288 MatchLen = ScreenPos(X, C + Len) - Match.Col;
289 if (!(Options & SEARCH_NOPOS)) {
290 if (Options & SEARCH_CENTER)
291 CenterPosR(Match.Col, Match.Row);
292 else
293 SetPosR(Match.Col, Match.Row);
294 }
295 Draw(L, L);
296 return 1;
297 }
298 if (Options & SEARCH_BACK) C--; else C++;
299 }
300 if (Options & SEARCH_BACK) {
301 L--;
302 if (L >= 0)
303 C = RLine(L)->Count;
304 } else {
305 C = 0;
306 L++;
307 }
308 }
309 //SetPos(OC, OL);
310 return 0;
311 }
312
FindRx(RxNode * Rx,SearchReplaceOptions & opt)313 int EBuffer::FindRx(RxNode *Rx, SearchReplaceOptions &opt) {
314 int Options = opt.Options;
315 int LLen, Start, End;
316 int C, L;
317 char *P;
318 PELine X;
319 RxMatchRes b;
320
321 if (!(Options & SEARCH_RE))
322 return 0;
323 if (Options & SEARCH_BACK) { // not supported
324 View->MView->Win->Choice(GPC_ERROR, "FindRx", 1, "O&K", "Reverse regexp search not supported.");
325 return 0;
326 }
327 if (Rx == 0)
328 return 0;
329
330 if (Match.Row != -1)
331 Draw(Match.Row, Match.Row);
332 Match.Row = -1;
333 Match.Col = -1;
334
335 C = CP.Col;
336 L = VToR(CP.Row);
337 X = RLine(L);
338 C = CharOffset(X, C);
339
340 if (Options & SEARCH_NEXT) {
341 int CC = MatchCount ? MatchCount : 1;
342
343 if (Options & SEARCH_BACK) {
344 C -= CC;
345 if (Options & SEARCH_BLOCK) {
346 if (C < BB.Col && L == BB.Row)
347 return 0;
348 L--;
349 X = RLine(L);
350 C = X->Count;
351 if (BlockMode == bmColumn)
352 if (BE.Col < C)
353 C = BE.Col;
354 } else {
355 if (C < 0 && L == 0)
356 return 0;
357 L--;
358 X = RLine(L);
359 C = X->Count;
360 }
361 } else {
362 C += CC;
363 if (Options & SEARCH_BLOCK) {
364 if (BlockMode == bmStream || BlockMode == bmLine) {
365 if (C >= X->Count) {
366 C = 0;
367 L++;
368 if (BlockMode == bmLine) {
369 if (L == BE.Row) return 0;
370 } else
371 if (L == BE.Row && (C >= BE.Col || C >= X->Count))
372 return 0;
373 }
374 } else if (BlockMode == bmColumn) {
375 if (C >= X->Count || C >= BE.Col) {
376 C = BB.Col;
377 L++;
378 if (L == BE.Row) return 0;
379 }
380 }
381 } else {
382 if (C >= X->Count) {
383 C = 0;
384 L++;
385 if (L == RCount) return 0;
386 }
387 }
388 }
389 }
390 MatchLen = 0;
391 MatchCount = 0;
392
393 if (Options & SEARCH_BLOCK) {
394 if (Options & SEARCH_BACK) {
395 if (BlockMode == bmStream) {
396 if (L > BE.Row) {
397 L = BE.Row;
398 C = BE.Col;
399 }
400 if (L == BE.Row && C > BE.Col)
401 C = BE.Col;
402 } else {
403 if (L >= BE.Row && BE.Row > 0) {
404 L = BE.Row - 1;
405 C = RLine(L)->Count;
406 }
407 if (BlockMode == bmColumn)
408 if (L == BE.Row - 1 && C >= BE.Col)
409 C = BE.Col;
410 }
411 } else {
412 if (L < BB.Row) {
413 L = BB.Row;
414 C = 0;
415 }
416 if (L == BB.Row && C < BB.Col)
417 C = BB.Col;
418 }
419 }
420
421 while (1) {
422 if (Options & SEARCH_BLOCK) {
423 if (BlockMode == bmStream) {
424 if (L > BE.Row || L < BB.Row) break;
425 } else
426 if (L >= BE.Row || L < BB.Row) break;
427 } else
428 if (L >= RCount || L < 0) break;
429
430 X = RLine(L);
431 LLen = X->Count;
432 P = X->Chars;
433 Start = 0;
434 End = LLen;
435
436 if (Options & SEARCH_BLOCK) {
437 if (BlockMode == bmColumn) {
438 Start = CharOffset(X, BB.Col);
439 End = CharOffset(X, BE.Col);
440 } else if (BlockMode == bmStream) {
441 if (L == BB.Row)
442 Start = CharOffset(X, BB.Col);
443 if (L == BE.Row)
444 End = CharOffset(X, BE.Col);
445 }
446 if (End > LLen)
447 End = LLen;
448 }
449 if (Options & SEARCH_BACK) {
450 if (C >= End)
451 C = End;
452 } else {
453 if (C < Start)
454 C = Start;
455 }
456
457 if (Start <= End) {
458 if (RxExec(Rx, P + Start, End - Start, P + C, &b, (Options & SEARCH_NCASE) ? 0 : RX_CASE) == 1) {
459 C = ScreenPos(X, b.Open[0] + Start);
460 Match.Col = C;
461 Match.Row = L;
462 MatchCount = b.Close[0] - b.Open[0];
463 MatchLen = ScreenPos(X, b.Close[0] + Start) - C;
464 for (int mm = 0; mm < NSEXPS; mm++) {
465 b.Open[mm] += Start;
466 b.Close[mm] += Start;
467 }
468 MatchRes = b;
469 if (!(Options & SEARCH_NOPOS)) {
470 if (Options & SEARCH_CENTER)
471 CenterPosR(C, L);
472 else
473 SetPosR(C, L);
474 }
475 Draw(L, L);
476 return 1;
477 }
478 }
479 C = 0;
480 L++;
481 }
482 //SetPos(OC, OL);
483 return 0;
484
485 }
486
Find(SearchReplaceOptions & opt)487 int EBuffer::Find(SearchReplaceOptions &opt) {
488 size_t slen = strlen(opt.strSearch);
489 int Options = opt.Options;
490 size_t rlen = strlen(opt.strReplace);
491 RxNode *R = NULL;
492
493 opt.resCount = -1;
494 opt.lastInsertLen = 0;
495
496 if (slen == 0) return 0;
497 if (Options & SEARCH_BLOCK) {
498 if (CheckBlock() == 0) return 0;
499 }
500 if (Options & SEARCH_RE) {
501 R = RxCompile(opt.strSearch);
502 if (R == 0) {
503 View->MView->Win->Choice(GPC_ERROR, "Find", 1, "O&K", "Invalid regular expression.");
504 return 0;
505 }
506 }
507 if (Options & SEARCH_GLOBAL) {
508 if (Options & SEARCH_BLOCK) {
509 if (Options & SEARCH_BACK) {
510 if (SetPosR(BE.Col, BE.Row) == 0) goto error;
511 } else {
512 if (SetPosR(BB.Col, BB.Row) == 0) goto error;
513 }
514 } else {
515 if (Options & SEARCH_BACK) {
516 if (RCount < 1) goto error;
517 if (SetPosR(LineLen(RCount - 1), RCount - 1) == 0) goto error;
518 } else {
519 if (SetPosR(0, 0) == 0) goto error;
520 }
521 }
522 }
523 opt.resCount = 0;
524 while (1) {
525 if (Options & SEARCH_RE) {
526 if (FindRx(R, opt) == 0) goto end;
527 } else {
528 if (FindStr(opt.strSearch, slen, opt) == 0) goto end;
529 }
530 opt.resCount++;
531
532 if (opt.Options & SEARCH_REPLACE) {
533 char ask = 'A';
534
535 if (!(Options & SEARCH_NASK)) {
536 char ch;
537
538 while (1) {
539 Draw(VToR(CP.Row), 1);
540 Redraw();
541 switch (View->MView->Win->Choice(0, "Replace",
542 5,
543 "&Yes",
544 "&All",
545 "&Once",
546 "&Skip",
547 "&Cancel",
548 "Replace with %s?", opt.strReplace))
549 {
550 case 0: ch = 'Y'; break;
551 case 1: ch = 'A'; break;
552 case 2: ch = 'O'; break;
553 case 3: ch = 'N'; break;
554 case 4:
555 case -1:
556 default:
557 ch = 'Q'; break;
558 }
559 if (ch == 'Y') { ask = 'Y'; goto ok_rep; }
560 if (ch == 'N') { ask = 'N'; goto ok_rep; }
561 if (ch == 'Q') { ask = 'Q'; goto ok_rep; }
562 if (ch == 'A') { ask = 'A'; goto ok_rep; }
563 if (ch == 'O') { ask = 'O'; goto ok_rep; }
564 }
565 ok_rep:
566 if (ask == 'N') goto try_join;
567 if (ask == 'Q') goto end;
568 if (ask == 'A') Options |= SEARCH_NASK;
569 }
570
571 if (Options & SEARCH_RE) {
572 PELine L = RLine(Match.Row);
573 int P, R;
574 char *PR = 0;
575 size_t LR = 0;
576
577 R = Match.Row;
578 P = Match.Col;
579 P = CharOffset(L, P);
580
581 if (0 == RxReplace(opt.strReplace, L->Chars, L->Count, MatchRes, &PR, &LR)) {
582 if (DelText(R, Match.Col, MatchLen) == 0) goto error;
583 if (PR && LR > 0)
584 if (InsText(R, Match.Col, LR, PR) == 0) goto error;
585 if (PR)
586 free(PR);
587 rlen = LR;
588 }
589 } else {
590 if (DelText(Match.Row, Match.Col, MatchLen) == 0) goto error;
591 if (InsText(Match.Row, Match.Col, rlen, opt.strReplace) == 0) goto error;
592
593 // Cursor remains at start of inserted string. If there is recursive
594 // replace pattern, fte can go it infinite loop.
595 // Workaround: Move cursor to end of inserted string
596 opt.lastInsertLen = strlen(opt.strReplace);
597 }
598 if (!(Options & SEARCH_BACK)) {
599 MatchLen = rlen;
600 MatchCount = rlen;
601 }
602 if (ask == 'O')
603 goto end;
604 }
605 try_join:
606 if (Options & SEARCH_JOIN) {
607 char ask = 'A';
608
609 if (!(Options & SEARCH_NASK)) {
610 char ch;
611
612 while (1) {
613 Draw(VToR(CP.Row), 1);
614 Redraw();
615 switch (View->MView->Win->Choice(0, "Join Line",
616 5,
617 "&Yes",
618 "&All",
619 "&Once",
620 "&Skip",
621 "&Cancel",
622 "Join lines %d and %d?", 1 + VToR(CP.Row), 1 + VToR(CP.Row) + 1))
623 {
624 case 0: ch = 'Y'; break;
625 case 1: ch = 'A'; break;
626 case 2: ch = 'O'; break;
627 case 3: ch = 'N'; break;
628 case 4:
629 case -1:
630 default:
631 ch = 'Q'; break;
632 }
633 if (ch == 'Y') { ask = 'Y'; goto ok_join; }
634 if (ch == 'N') { ask = 'N'; goto ok_join; }
635 if (ch == 'Q') { ask = 'Q'; goto ok_join; }
636 if (ch == 'A') { ask = 'A'; goto ok_join; }
637 if (ch == 'O') { ask = 'O'; goto ok_join; }
638 }
639 ok_join:
640 if (ask == 'N') goto try_delete;
641 if (ask == 'Q') goto end;
642 if (ask == 'A') Options |= SEARCH_NASK;
643 }
644
645 if (JoinLine(Match.Row, Match.Col) == 0) goto error;
646
647 if (ask == 'O')
648 goto end;
649 }
650 try_delete:
651 if (Options & SEARCH_DELETE) {
652 char ask = 'A';
653
654 if (!(Options & SEARCH_NASK)) {
655 char ch;
656
657 while (1) {
658 Draw(VToR(CP.Row), 1);
659 Redraw();
660 switch (View->MView->Win->Choice(0, "Delete Line",
661 5,
662 "&Yes",
663 "&All",
664 "&Once",
665 "&Skip",
666 "&Cancel",
667 "Delete line %d?", VToR(CP.Row)))
668 {
669 case 0: ch = 'Y'; break;
670 case 1: ch = 'A'; break;
671 case 2: ch = 'O'; break;
672 case 3: ch = 'N'; break;
673 case 4:
674 case -1:
675 default:
676 ch = 'Q'; break;
677 }
678 if (ch == 'Y') { ask = 'Y'; goto ok_delete; }
679 if (ch == 'N') { ask = 'N'; goto ok_delete; }
680 if (ch == 'Q') { ask = 'Q'; goto ok_delete; }
681 if (ch == 'A') { ask = 'A'; goto ok_delete; }
682 if (ch == 'O') { ask = 'O'; goto ok_delete; }
683 }
684 ok_delete:
685 if (ask == 'N') goto next;
686 if (ask == 'Q') goto end;
687 if (ask == 'A') Options |= SEARCH_NASK;
688 }
689
690 if (Match.Row == RCount - 1) {
691 if (DelText(Match.Row, 0, LineLen()) == 0) goto error;
692 } else
693 if (DelLine(Match.Row) == 0) goto error;
694
695 if (ask == 'O')
696 goto end;
697 if (!(Options & SEARCH_ALL))
698 break;
699 goto last;
700 }
701 next:
702 if (!(Options & SEARCH_ALL))
703 break;
704 Options |= SEARCH_NEXT;
705 last:
706 ;
707 }
708 end:
709 // end of search
710 if (R)
711 RxFree(R);
712
713 if (Options & SEARCH_ALL)
714 Msg(S_INFO, "%d match(es) found.", opt.resCount);
715 else {
716 if (opt.resCount == 0) {
717 Msg(S_INFO, "[%s] not found", opt.strSearch);
718 return 0;
719 }
720 }
721 return 1;
722 error:
723
724 if (R)
725 {
726 RxFree(R);
727 }
728 View->MView->Win->Choice(GPC_ERROR, "Find", 1, "O&K", "Error in search/replace.");
729 return 0;
730 }
731
732
CompleteWord()733 int EBuffer::CompleteWord() {
734 #ifdef CONFIG_I_COMPLETE
735 return View->MView->Win->ICompleteWord(View);
736 #else
737 PELine L = VLine(CP.Row), M;
738 int C, P, X, P1, N, xlen, XL;
739 EPoint O = CP;
740
741 if (CP.Col == 0) return 0;
742
743 if (SetPos(CP.Col, CP.Row) == 0) return 0;
744
745 C = CP.Col;
746 P = CharOffset(L, C);
747 P1 = P;
748 N = VToR(CP.Row);
749
750 if (P > L->Count) return 0;
751
752 if (P > 0) {
753 while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_'))) P--;
754 if (P == P1) return 0;
755 xlen = P1 - P;
756 C = ScreenPos(L, P);
757 Match.Row = N;
758 Match.Col = C;
759 //if (SetPos(C, CP.Row) == 0) return 0;
760 //again:
761 if (FindStr(L->Chars + P, xlen, SEARCH_BACK | SEARCH_NEXT | SEARCH_NOPOS | SEARCH_WORDBEG) == 1) {
762 M = RLine(Match.Row);
763 X = CharOffset(M, Match.Col);
764 XL = X;
765 //if ((XL > 0) && ((ChClass(M->Chars[XL - 1]) == 1) || (M->Chars[XL - 1] == '_'))) goto again;
766 while ((XL < M->Count) && ((ChClass(M->Chars[XL]) == 1) || (M->Chars[XL] == '_'))) XL++;
767 {
768 char *pp = (char *)malloc(XL - X - xlen);
769
770 if (pp) {
771 memcpy(pp, M->Chars + X + xlen, XL - X - xlen);
772
773 if (InsText(N, O.Col,
774 XL - X - xlen,
775 pp,
776 1) == 0) return 0;
777 free(pp);
778 }
779 }
780 if (SetPos(O.Col + XL - X - xlen, O.Row) == 0) return 0;
781 Draw(Match.Row, Match.Row);
782 Match.Row = -1;
783 Match.Col = -1;
784 MatchLen = 0;
785 MatchCount = 0;
786 return 1;
787 }
788 }
789 if (Match.Row != -1)
790 Draw(Match.Row, Match.Row);
791 Match.Col = Match.Row = -1;
792 MatchLen = 0;
793 MatchCount = 0;
794 return 0;
795 #endif
796 }
797
Search(ExState & State,const char * aString,int Options,int)798 int EBuffer::Search(ExState &State, const char *aString, int Options, int /*CanResume*/) {
799 char find[MAXSEARCH+1] = "";
800 int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
801 int Next = 0;
802 int erc = 0;
803 //int Changed;
804
805 if (aString)
806 strcpy(find, aString);
807 else
808 if (State.GetStrParam(View, find, sizeof(find)) == 0)
809 if ((erc = View->MView->Win->GetStr("Find", sizeof(find), find, HIST_SEARCH)) == 0) return 0;
810 if (strlen(find) == 0) return 0;
811
812 if (erc == 2)
813 Case ^= SEARCH_NCASE;
814
815 //if (Changed == 0 && CanResume)
816 // Next |= SEARCH_NEXT;
817
818 LSearch.ok = 0;
819 strcpy(LSearch.strSearch, find);
820 LSearch.Options = Case | Next | (Options & ~SEARCH_NCASE);
821 LSearch.ok = 1;
822 if (Find(LSearch) == 0) return 0;
823 return 1;
824 }
825
SearchAgain(ExState &,unsigned int Options)826 int EBuffer::SearchAgain(ExState &/*State*/, unsigned int Options) {
827 if (LSearch.ok == 0) return 0;
828 LSearch.Options |= SEARCH_NEXT;
829 if ((Options & SEARCH_BACK) != (LSearch.Options & SEARCH_BACK))
830 LSearch.Options ^= SEARCH_BACK;
831 if (Find(LSearch) == 0) return 0;
832 return 1;
833 }
834
SearchReplace(ExState & State,const char * aString,const char * aReplaceString,int Options)835 int EBuffer::SearchReplace(ExState &State, const char *aString, const char *aReplaceString, int Options) {
836 char find[MAXSEARCH+1] = "";
837 char replace[MAXSEARCH+1] = "";
838 int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
839
840 if (aString)
841 strcpy(find, aString);
842 else
843 if (State.GetStrParam(View, find, sizeof(find)) == 0)
844 if (View->MView->Win->GetStr("Find", sizeof(find), find, HIST_SEARCH) == 0) return 0;
845 if (strlen(find) == 0) return 0;
846 if (aReplaceString)
847 strcpy(replace, aReplaceString);
848 else
849 if (State.GetStrParam(View, replace, sizeof(replace)) == 0)
850 if (View->MView->Win->GetStr("Replace", sizeof(replace), replace, HIST_SEARCH) == 0) return 0;
851
852 LSearch.ok = 0;
853 strcpy(LSearch.strSearch, find);
854 strcpy(LSearch.strReplace, replace);
855 LSearch.Options = Case | (Options & ~SEARCH_NCASE) | SEARCH_ALL | SEARCH_REPLACE;
856 LSearch.ok = 1;
857 if (Find(LSearch) == 0) return 0;
858 return 1;
859 }
860
Search(ExState & State)861 int EBuffer::Search(ExState &State) { return Search(State, 0, 0, 1); }
SearchB(ExState & State)862 int EBuffer::SearchB(ExState &State) { return Search(State, 0, SEARCH_BACK, 1); }
SearchRx(ExState & State)863 int EBuffer::SearchRx(ExState &State) { return Search(State, 0, SEARCH_RE, 1); }
SearchAgain(ExState & State)864 int EBuffer::SearchAgain(ExState &State) { return SearchAgain(State, 0); }
SearchAgainB(ExState & State)865 int EBuffer::SearchAgainB(ExState &State) { return SearchAgain(State, SEARCH_BACK); }
SearchReplace(ExState & State)866 int EBuffer::SearchReplace(ExState &State) { return SearchReplace(State, 0, 0, 0); }
SearchReplaceB(ExState & State)867 int EBuffer::SearchReplaceB(ExState &State) { return SearchReplace(State, 0, 0, SEARCH_BACK); }
SearchReplaceRx(ExState & State)868 int EBuffer::SearchReplaceRx(ExState &State) { return SearchReplace(State, 0, 0, SEARCH_RE); }
869
870 #ifdef CONFIG_OBJ_ROUTINE
ScanForRoutines()871 int EBuffer::ScanForRoutines() {
872 RxNode *regx;
873 int line;
874 PELine L;
875 RxMatchRes res;
876
877 if (BFS(this, BFS_RoutineRegexp) == 0) {
878 View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "No routine regexp.");
879 return 0;
880 }
881 regx = RxCompile(BFS(this, BFS_RoutineRegexp));
882 if (regx == 0) {
883 View->MView->Win->Choice(GPC_ERROR, "Error", 1, "O&K", "Failed to compile regexp '%s'", BFS(this, BFS_RoutineRegexp));
884 return 0;
885 }
886
887 if (rlst.Lines) {
888 free(rlst.Lines);
889 rlst.Lines = 0;
890 }
891 rlst.Lines = 0;
892 rlst.Count = 0;
893
894 Msg(S_BUSY, "Matching %s", BFS(this, BFS_RoutineRegexp));
895 for (line = 0; line < RCount; line++) {
896 L = RLine(line);
897 if (RxExec(regx, L->Chars, L->Count, L->Chars, &res) == 1) {
898 rlst.Count++;
899 rlst.Lines = (int *) realloc((void *) rlst.Lines, sizeof(int) * (rlst.Count | 0x1F));
900 rlst.Lines[rlst.Count - 1] = line;
901 Msg(S_BUSY, "Routines: %d", rlst.Count);
902 }
903 }
904 RxFree(regx);
905 return 1;
906 }
907 #endif
908
ShowPosition()909 int EBuffer::ShowPosition() {
910 int CLine, NLines;
911 int CAct, NAct;
912 int CColumn, NColumns;
913 int CCharPos, NChars;
914 #ifdef HEAPWALK
915 unsigned long MemUsed = 0, MemFree = 0, BlkUsed = 0, BlkFree = 0, BigFree = 0, BigUsed = 0;
916 #endif
917
918 if (!View)
919 return 0;
920
921 CLine = CP.Row + 1;
922 NLines = VCount;
923 CAct = VToR(CP.Row) + 1;
924 NAct = RCount;
925 CColumn = CP.Col + 1;
926 NColumns = LineLen(CP.Row);
927 CCharPos = CharOffset(VLine(CP.Row), CP.Col) + 1;
928 NChars = VLine(CP.Row)->Count;
929
930 #ifdef HEAPWALK
931 if (_heapchk() != _HEAPOK) {
932 MemUsed = -1;
933 } else {
934 _HEAPINFO hi;
935
936 hi._pentry = NULL;
937 while (_heapwalk(&hi) == _HEAPOK) {
938 if (hi._useflag == _USEDENTRY) {
939 BlkUsed++;
940 MemUsed += hi._size;
941 if (hi._size > BigUsed)
942 BigUsed = hi._size;
943 //fprintf(stderr, "USED %d\n", hi._size);
944 } else {
945 BlkFree++;
946 MemFree += hi._size;
947 if (hi._size > BigFree)
948 BigFree = hi._size;
949 //fprintf(stderr, "FREE %d\n", hi._size);
950 }
951 }
952 }
953 #endif
954
955 #ifdef CONFIG_UNDOREDO
956 int NN = -1;
957 if (US.UndoPtr > 0)
958 NN = US.Top[US.UndoPtr - 1];
959 #endif
960 Msg(S_INFO,
961 #ifdef HEAPWALK
962 "M%ld,%ld B%ld,%ld S%ld,%ld"
963 #endif
964 "L%d/%d G%d/%d/%d A%d/%d C%d/%d P%d/%d "
965 #ifdef CONFIG_UNDOREDO
966 "U%d/%d/%d "
967 #endif
968 "H%d/%d/%d",
969 #ifdef HEAPWALK
970 MemUsed, MemFree, BlkUsed, BlkFree, BigUsed, BigFree,
971 #endif
972 CLine, NLines,
973 RGap, RCount, RAllocated,
974 CAct, NAct,
975 CColumn, NColumns,
976 CCharPos, NChars,
977 #ifdef CONFIG_UNDOREDO
978 US.UndoPtr, US.Num, NN,
979 #endif
980 StartHilit, MinRedraw, MaxRedraw);
981 return 1;
982 }
983
984 #ifdef CONFIG_BOOKMARKS
PlaceBookmark(const char * Name,const EPoint & P)985 int EBuffer::PlaceBookmark(const char *Name, const EPoint &P) {
986 assert(P.Row >= 0 && P.Row < RCount && P.Col >= 0);
987
988 vector_iterate(EBookmark*, BMarks, it)
989 if ((*it)->IsName(Name)) {
990 (*it)->SetPoint(P);
991 return 1;
992 }
993
994 EBookmark* b = new EBookmark(Name, P);
995 BMarks.push_back(b);
996
997 return 1;
998 }
999
RemoveBookmark(const char * Name)1000 int EBuffer::RemoveBookmark(const char *Name) {
1001 vector_iterate(EBookmark*, BMarks, it)
1002 if ((*it)->IsName(Name)) {
1003 delete (*it);
1004 BMarks.erase(it);
1005 return 1;
1006 }
1007
1008 View->MView->Win->Choice(GPC_ERROR, "RemoveBookmark", 1, "O&K", "Bookmark %s not found.", Name);
1009 return 0;
1010 }
1011
GetBookmark(const char * Name,EPoint & P)1012 int EBuffer::GetBookmark(const char *Name, EPoint &P) {
1013 vector_iterate(EBookmark*, BMarks, it)
1014 if ((*it)->IsName(Name)) {
1015 P = (*it)->GetPoint();
1016 return 1;
1017 }
1018 return 0;
1019 }
1020
1021 /*
1022 * Searches bookmark list starting at given index (searchFrom) for next
1023 * bookmark for line searchForLine. It then returns its name and position
1024 * and index (used for next search) or -1 if none found. Name is pointer
1025 * directly into bookmark structure (not copied!). If searchForLine is -1,
1026 * this function returns any next bookmark -> can be used to enumerate
1027 * bookmarks.
1028 */
GetBookmarkForLine(int searchFrom,int searchForLine,const EBookmark * & b)1029 int EBuffer::GetBookmarkForLine(int searchFrom, int searchForLine, const EBookmark* &b) {
1030 for (int i = searchFrom; i < (int)BMarks.size(); i++)
1031 if (searchForLine == -1 || BMarks[i]->GetPoint().Row == searchForLine) {
1032 b = BMarks[i];
1033 return i + 1;
1034 }
1035 return -1;
1036 }
1037
GotoBookmark(const char * Name)1038 int EBuffer::GotoBookmark(const char *Name) {
1039 vector_iterate(EBookmark*, BMarks, it)
1040 if ((*it)->IsName(Name))
1041 return CenterNearPosR((*it)->GetPoint().Col, (*it)->GetPoint().Row);
1042
1043 View->MView->Win->Choice(GPC_ERROR, "GotoBookmark", 1, "O&K", "Bookmark %s not found.", Name);
1044 return 0;
1045 }
1046 #endif
1047
GetMatchBrace(EPoint & M,int MinLine,int MaxLine,int show)1048 int EBuffer::GetMatchBrace(EPoint &M, int MinLine, int MaxLine, int show) {
1049 int StateLen;
1050 hsState *StateMap = 0;
1051 int Pos;
1052 PELine L = VLine(M.Row);
1053 int dir = 0;
1054 hsState State;
1055 char Ch1, Ch2;
1056 int CountX = 0;
1057 int StateRow = -1;
1058
1059 M.Row = VToR(CP.Row);
1060
1061 Pos = CharOffset(L, M.Col);
1062 if (Pos >= L->Count) return 0;
1063 switch(L->Chars[Pos]) {
1064 case '{': dir = +1; Ch1 = '{'; Ch2 = '}'; break;
1065 case '[': dir = +1; Ch1 = '['; Ch2 = ']'; break;
1066 case '<': dir = +1; Ch1 = '<'; Ch2 = '>'; break;
1067 case '(': dir = +1; Ch1 = '('; Ch2 = ')'; break;
1068 case '}': dir = -1; Ch1 = '}'; Ch2 = '{'; break;
1069 case ']': dir = -1; Ch1 = ']'; Ch2 = '['; break;
1070 case '>': dir = -1; Ch1 = '>'; Ch2 = '<'; break;
1071 case ')': dir = -1; Ch1 = ')'; Ch2 = '('; break;
1072 default:
1073 return 0;
1074 }
1075 StateMap = 0;
1076 if (GetMap(M.Row, &StateLen, &StateMap) == 0) return 0;
1077 State = StateMap[Pos];
1078 StateRow = M.Row;
1079
1080 while (M.Row >= MinLine && M.Row < MaxLine) {
1081 while (Pos >= 0 && Pos < L->Count) {
1082 if (L->Chars[Pos] == Ch1 || L->Chars[Pos] == Ch2) {
1083 // update syntax state if needed
1084 if (StateRow != M.Row) {
1085 free(StateMap);
1086 StateMap = 0;
1087 GetMap(M.Row, &StateLen, &StateMap);
1088 if (StateMap == 0) return 0;
1089 StateRow = M.Row;
1090 }
1091 if (StateMap[Pos] == State) {
1092 if (L->Chars[Pos] == Ch1) CountX++;
1093 if (L->Chars[Pos] == Ch2) CountX--;
1094 if (CountX == 0) {
1095 M.Col = ScreenPos(L, Pos);
1096 free(StateMap);
1097 return 1;
1098 }
1099 }
1100 }
1101 Pos += dir;
1102 }
1103 M.Row += dir;
1104 if (M.Row >= 0 && M.Row < RCount) {
1105 L = RLine(M.Row);
1106 Pos = (dir == 1) ? 0 : (L->Count - 1);
1107 }
1108 }
1109 if (StateMap) free(StateMap);
1110 if (show)
1111 Msg(S_INFO, "No match (%d missing).", CountX);
1112 return 0;
1113 }
1114
MatchBracket()1115 int EBuffer::MatchBracket() {
1116 EPoint M = CP;
1117
1118 if (GetMatchBrace(M, 0, RCount, 1) == 1)
1119 return SetPosR(M.Col, M.Row);
1120 return 0;
1121 }
1122
HilitMatchBracket()1123 int EBuffer::HilitMatchBracket() {
1124 EPoint M = CP;
1125
1126 if (View == 0)
1127 return 0;
1128
1129 int Min = VToR(GetVPort()->TP.Row);
1130 int Max = GetVPort()->TP.Row + GetVPort()->Rows;
1131 if (Max >= VCount)
1132 Max = RCount;
1133 else
1134 Max = VToR(Max);
1135 if (Min < 0)
1136 Min = 0;
1137 if (Max < Min)
1138 return 0;
1139 if (GetMatchBrace(M, Min, Max, 0) == 1) {
1140 Match = M;
1141 MatchLen = 1;
1142 MatchCount = 1;
1143 Draw(Match.Row, Match.Row);
1144 return 1;
1145 }
1146 return 0;
1147 }
1148
SearchWord(int SearchFlags)1149 int EBuffer::SearchWord(int SearchFlags) {
1150 char word[MAXSEARCH + 1];
1151 PELine L = VLine(CP.Row);
1152 int P, len = 0;
1153 int Case = BFI(this, BFI_MatchCase) ? 0 : SEARCH_NCASE;
1154
1155 P = CharOffset(L, CP.Col);
1156 while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_')))
1157 P--;
1158 while (len < int(sizeof(word)) && P < L->Count && (ChClass(L->Chars[P]) == 1 || L->Chars[P] == '_'))
1159 word[len++] = L->Chars[P++];
1160 word[len] = 0;
1161 if (len == 0)
1162 return 0;
1163
1164 return FindStr(word, len, Case | SearchFlags | SEARCH_WORD);
1165 }
1166
FindTagWord(ExState & State)1167 int EBuffer::FindTagWord(ExState &State) {
1168 char word[MAXSEARCH + 1];
1169 PELine L = VLine(CP.Row);
1170 int P, len = 0;
1171
1172 P = CharOffset(L, CP.Col);
1173 while ((P > 0) && ((ChClass(L->Chars[P - 1]) == 1) || (L->Chars[P - 1] == '_')))
1174 P--;
1175 while (len < int(sizeof(word)) && P < L->Count && (ChClass(L->Chars[P]) == 1 || L->Chars[P] == '_'))
1176 word[len++] = L->Chars[P++];
1177 word[len] = 0;
1178 if (len == 0) {
1179 Msg(S_INFO, "No word at cursor.");
1180 return 0;
1181 }
1182
1183 for (int j = 2; j; j--) {
1184 if (TagFind(this, View, word))
1185 return 1;
1186
1187 if (j == 2) {
1188 /* Try autoload tags */
1189 if (!View->ExecCommand(ExTagLoad, State))
1190 break;
1191 } else {
1192 Msg(S_INFO, "Tag '%s' not found.", word);
1193 break;
1194 }
1195 }
1196
1197 return 0;
1198 }
1199 // *INDENT-ON*
1200