2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * Program: Searching routines
18 * The functions in this file implement commands that search in the forward
19 * and backward directions. There are no special characters in the search
20 * strings. Probably should have a regular expression search, or something
28 int expandp(UCS
*, UCS
*, int);
29 int readnumpat(char *);
30 void get_pat_cases(UCS
*, UCS
*);
31 int srpat(char *, UCS
*, size_t, int, int);
32 int readpattern(char *, int, int);
33 int replace_pat(UCS
*, int *, int);
34 int replace_all(UCS
*, UCS
*, int);
35 void reverse_line(LINE
*);
36 void reverse_buffer(void);
37 void reverse_ucs4(UCS
*);
38 void reverse_all(UCS
*, int);
39 void supdate(UCS
*, int);
40 char *sucs4_to_utf8_cpystr(UCS
*, int);
42 #define FWS_RETURN(RV) { \
44 curwp->w_flag |= WFMODE; \
49 /* The search menu leaves a number of keys free, some are taken
50 * as subcommands of the search command, and some are taken are
51 * editing commands. This leaves the following keys open:
52 * ^J, ^N, ^O, ^P, ^R, ^T, ^U, ^V, ^W, ^X and ^Y.
53 * Out of these keys, ^J, ^N, ^P and ^X are not defined as commands, however,
54 * in some patches ^N, ^P and ^X are defined. ^N is defined as part of
55 * an editing command, ^P and ^X are defined to delete paragraphs and to
56 * remove text to the end of file, so only ^J is undefined.
59 #define REPLACE_KEY 2 /* the location of the replace key in the array below */
61 EXTRAKEYS menu_srchpat
[] = {
62 {"^Y", N_("FirstLine"), (CTRL
|'Y')},
63 {"^V", N_("LastLine"), (CTRL
|'V')},
64 {"^R", N_("No Replace"), (CTRL
|'R')},
65 {"^^", N_("Optns Menu"), (CTRL
|'^')}, /* toggle this menu or options menu */
66 {"^T", N_("LineNumber"), (CTRL
|'T')},
67 {"^W", N_("Start of Para"), (CTRL
|'W')},
68 {"^O", N_("End of Para"), (CTRL
|'O')},
69 {"^U", N_("FullJustify"), (CTRL
|'U')},
74 #define EXACTSR_KEY 1 /* toggle an exact or approximate search */
75 #define BGNLINE_KEY 3 /* the location of Bgn Line command in the array below */
76 #define ENDLINE_KEY 4 /* the location of End Line command in the array below */
77 #define BSEARCH_KEY 5 /* the location of the bsearch key in the array below */
78 EXTRAKEYS menu_srchopt
[] = {
79 {"^^", N_("Orig Menu"), (CTRL
|'^')}, /* toggle original or options menu */
80 {"^X", N_("Exact"), (CTRL
|'X')}, /* toggle exact vs non exact */
81 {"^R", N_("No Replace"), (CTRL
|'R')}, /* toggle replace or not replace */
82 {"^V", N_("Bgn Line"), (CTRL
|'V')}, /* toggle Bgn Line or anywhere */
83 {"^N", N_("End Line"), (CTRL
|'N')}, /* toggle End Line or anywhere */
84 {"^P", N_("BackSearch"), (CTRL
|'P')}, /* toggle Backward or forward */
92 * Search forward. Get a search string from the user, and search, beginning at
93 * ".", for the string. If found, reset the "." to be just after the match
94 * string, and [perhaps] repaint the display. Bound to "C-S".
97 /* string search input parameters */
99 #define PTBEG 1 /* leave the point at the beginning on search */
100 #define PTEND 2 /* leave the point at the end on search */
102 #define NPMT (2*NLINE+32)
105 static char *SearchHelpText
[] = {
106 /* TRANSLATORS: Some help text that goes together in a group. */
107 N_("Help for Search Command"),
109 N_(" Enter the words or characters you would like to search"),
110 N_("~ for, then press ~R~e~t~u~r~n. The search then takes place."),
111 N_(" When the characters or words that you entered "),
112 N_(" are found, the buffer will be redisplayed with the cursor "),
113 N_(" at the beginning of the selected text."),
115 N_(" The most recent string for which a search was made is"),
116 N_(" displayed in the \"Search\" prompt between the square"),
117 N_(" brackets. This string is the default search prompt."),
118 N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"),
119 N_(" search to be made with the default value."),
121 N_(" The text search is not case sensitive, and will examine the"),
122 N_(" entire message."),
124 N_(" Should the search fail, a message will be displayed."),
126 N_("End of Search Help."),
133 * Compare two characters. The "bc" comes from the buffer. It has it's case
134 * folded out. The "pc" is from the pattern.
139 if ((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0){
140 if (bc
>='a' && bc
<='z')
143 if (pc
>='a' && pc
<='z')
152 forwsearch(int f
, int n
)
155 int wrapt
= FALSE
, wrapt2
= FALSE
;
156 int repl_mode
= FALSE
;
162 /* resolve the repeat count */
166 if (n
< 1) /* search backwards */
170 /* defaults: usual menu, search forward, not case sensitive */
172 flags
= SR_ORIGMEN
| SR_FORWARD
;
174 /* exact search is sticky -- that is, once one is done, so will be
175 * the next ones. This is consistent with all all searches being
176 * case insensitive by default.
178 if((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0)
183 /* ask the user for the text of a pattern */
186 if (gmode
& MDREPLACE
)
187 status
= srpat("Search", defpat
, NPAT
, repl_mode
, flags
);
189 status
= readpattern("Search", TRUE
, flags
);
192 case TRUE
: /* user typed something */
196 case HELPCH
: /* help requested */
198 VARS_TO_SAVE
*saved_state
;
200 saved_state
= save_pico_state();
201 (*Pmaster
->helper
)(Pmaster
->search_help
,
202 _("Help for Searching"), 1);
204 restore_pico_state(saved_state
);
205 free_pico_state(saved_state
);
209 pico_help(SearchHelpText
, _("Help for Searching"), 1);
211 case (CTRL
|'L'): /* redraw requested */
212 pico_refresh(FALSE
, 1);
218 if(flags
& SR_ORIGMEN
){
219 /* Undefined still */
221 if(flags
& SR_OPTNMEN
){
222 if(flags
& SR_FORWARD
){
223 flags
&= ~SR_FORWARD
;
226 flags
&= ~SR_BACKWRD
;
233 if(flags
& SR_ORIGMEN
){
237 } else if (flags
& SR_OPTNMEN
){
238 if(flags
& SR_ENDLINE
)
239 flags
&= ~SR_ENDLINE
;
240 if(flags
& SR_BEGLINE
)
241 flags
&= ~SR_BEGLINE
;
248 if(flags
& SR_ORIGMEN
){
249 /* undefined still */
250 } else if (flags
& SR_OPTNMEN
){
251 if(flags
& SR_BEGLINE
)
252 flags
&= ~SR_BEGLINE
;
253 if(flags
& SR_ENDLINE
)
254 flags
&= ~SR_ENDLINE
;
261 if(flags
& SR_ORIGMEN
){
268 if (flags
& SR_ORIGMEN
){
269 flags
&= ~SR_ORIGMEN
;
272 flags
&= ~SR_OPTNMEN
;
278 if(flags
& SR_OPTNMEN
){
279 if (flags
& SR_NOEXACT
){
280 flags
&= ~SR_NOEXACT
;
283 flags
&= ~SR_EXACTSR
;
286 if((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0)
287 curwp
->w_bufp
->b_mode
|= MDEXACT
;
289 curwp
->w_bufp
->b_mode
&= ~MDEXACT
;
294 if(flags
& SR_ORIGMEN
){
295 switch(status
= readnumpat(_("Search to Line Number : "))){
297 emlwrite(_("Search to Line Number Cancelled"), NULL
);
301 emlwrite(_("Line number must be greater than zero"), NULL
);
305 emlwrite(_("Line number must contain only digits"), NULL
);
320 if(flags
& SR_ORIGMEN
){
321 LINE
*linep
= curwp
->w_dotp
;
322 int offset
= curwp
->w_doto
;
328 * if we're asked to backup and we're already
331 if((lastflag
& CFSRCH
)
332 && linep
== curwp
->w_dotp
333 && offset
== curwp
->w_doto
334 && !(offset
== 0 && lback(linep
) == curbp
->b_linep
)){
345 if(flags
& SR_ORIGMEN
){
346 if(curwp
->w_dotp
!= curbp
->b_linep
){
356 if(flags
& SR_ORIGMEN
){
363 case (CTRL
|'R'): /* toggle replacement option */
364 repl_mode
= !repl_mode
;
369 emlwrite(_("Search Cancelled"), NULL
);
376 /* replace option is disabled */
377 if (!(gmode
& MDREPLACE
)){
378 ucs4_strncpy(defpat
, pat
, NPAT
);
379 defpat
[NPAT
-1] = '\0';
382 else if (search
){ /* search now */
383 ucs4_strncpy(pat
, defpat
, NPAT
); /* remember this search for the future */
389 reverse_all(defpat
, flags
& SR_BACKWRD
);
392 * This code is kind of dumb. What I want is successive C-W 's to
393 * move dot to successive occurrences of the pattern. So, if dot is
394 * already sitting at the beginning of the pattern, then we'll move
395 * forward a char before beginning the search. We'll let the
396 * automatic wrapping handle putting the dot back in the right
399 status
= 0; /* using "status" as int temporarily! */
401 if(defpat
[status
] == '\0'){
406 if(status
+ curwp
->w_doto
>= llength(curwp
->w_dotp
) ||
407 !eq(defpat
[status
],lgetc(curwp
->w_dotp
, curwp
->w_doto
+ status
).c
))
412 /* search for the pattern */
415 if((status
= forscan(&wrapt
,defpat
, flags
, NULL
,0,PTBEG
)) == FALSE
)
419 /* and complain if not there */
420 if (status
== FALSE
){
426 utf8
= sucs4_to_utf8_cpystr(defpat
? defpat
: x
, flags
& SR_BACKWRD
);
427 /* TRANSLATORS: reporting the result of a failed search */
429 emlwrite(_("\"%s\" not found"), &eml
);
431 fs_give((void **) &utf8
);
433 else if((gmode
& MDREPLACE
) && repl_mode
== TRUE
){
434 status
= replace_pat(defpat
, &wrapt2
, flags
& SR_BACKWRD
); /* replace pattern */
435 if (wrapt
== TRUE
|| wrapt2
== TRUE
){
436 eml
.s
= (status
== ABORT
) ? "cancelled but wrapped" : "Wrapped";
437 emlwrite("Replacement %s", &eml
);
440 else if(wrapt
== TRUE
){
441 emlwrite("Search Wrapped", NULL
);
443 else if(status
== TRUE
){
447 reverse_all(defpat
, flags
& SR_BACKWRD
);
448 if(curwp
->w_doto
== -1){
450 curwp
->w_flag
|= WFMOVE
;
456 /* Replace a pattern with the pattern the user types in one or more times. */
458 replace_pat(UCS
*defpat
, int *wrapt
, int bsearch
)
461 UCS lpat
[NPAT
], origpat
[NPAT
]; /* case sensitive pattern */
462 EXTRAKEYS menu_pat
[12];
463 int repl_all
= FALSE
;
472 curwp
->w_doto
-= ucs4_strlen(defpat
) - 1;
474 else flags
= SR_FORWARD
;
476 forscan(wrapt
, defpat
, flags
, NULL
, 0, PTBEG
); /* go to word to be replaced */
479 memset((void *)&menu_pat
, 0, sizeof(menu_pat
));
480 /* additional 'replace all' menu option */
481 menu_pat
[0].name
= "^X";
482 menu_pat
[0].key
= (CTRL
|'X');
483 menu_pat
[0].label
= N_("Repl All");
484 KS_OSDATASET(&menu_pat
[0], KS_NONE
);
488 /* we need to reverse the buffer back to its original state, so that
489 * the user will not see that we reversed it under them. The cursor
490 * is at the beginning of the reverse string, that is at the end
491 * of the string. Move it back to the beginning.
494 reverse_all(defpat
, bsearch
); /* reverse for normal view */
497 get_pat_cases(origpat
, defpat
);
498 pputs(origpat
, 1); /* highlight word */
501 snprintf(utf8tmp
, NPMT
, "Replace%s \"", repl_all
? " every" : "");
502 b
= utf8_to_ucs4_cpystr(utf8tmp
);
504 ucs4_strncpy(prompt
, b
, NPMT
);
505 prompt
[NPMT
-1] = '\0';
506 fs_give((void **) &b
);
509 promptp
= &prompt
[ucs4_strlen(prompt
)];
511 expandp(defpat
, promptp
, NPMT
-(promptp
-prompt
));
512 prompt
[NPMT
-1] = '\0';
513 promptp
+= ucs4_strlen(promptp
);
515 b
= utf8_to_ucs4_cpystr("\" with");
517 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
518 promptp
+= ucs4_strlen(promptp
);
519 prompt
[NPMT
-1] = '\0';
520 fs_give((void **) &b
);
524 if((promptp
-prompt
) < NPMT
-2){
530 expandp(rpat
, promptp
, NPMT
-(promptp
-prompt
));
531 prompt
[NPMT
-1] = '\0';
532 promptp
+= ucs4_strlen(promptp
);
534 if((promptp
-prompt
) < NPMT
-1){
540 if((promptp
-prompt
) < NPMT
-3){
547 prompt
[NPMT
-1] = '\0';
549 status
= mlreplyd(prompt
, lpat
, NPAT
, QDEFLT
, menu_pat
);
551 curwp
->w_flag
|= WFMOVE
;
553 reverse_all(defpat
, bsearch
); /* reverse for internal use */
560 ucs4_strncpy(rpat
, lpat
, NPAT
); /* remember default */
564 ucs4_strncpy(lpat
, rpat
, NPAT
); /* use default */
569 status
= replace_all(defpat
, lpat
, bsearch
);
573 curwp
->w_doto
-= ucs4_strlen(defpat
) - 1;
574 chword(defpat
, lpat
, bsearch
); /* replace word */
575 /* after substitution the cursor is past the end of the
576 * replaced string, so we backdown in backward search,
577 * to make it appear at the beginning of the replaced string.
578 * We make this choice in case the next search is backward,
579 * because if we put the cursor at the end, the next backward
580 * search might hit the substituted string, and we want to
581 * avoid that, because we probably do not want to substitute
582 * the new string, but old text.
584 if(bsearch
&& (lback(curwp
->w_dotp
) != curbp
->b_linep
585 || curwp
->w_doto
> 0))
587 supdate(defpat
, bsearch
);
596 case HELPCH
: /* help requested */
598 VARS_TO_SAVE
*saved_state
;
600 saved_state
= save_pico_state();
601 (*Pmaster
->helper
)(Pmaster
->search_help
,
602 _("Help for Searching"), 1);
604 restore_pico_state(saved_state
);
605 free_pico_state(saved_state
);
609 pico_help(SearchHelpText
, _("Help for Searching"), 1);
611 case (CTRL
|'L'): /* redraw requested */
612 pico_refresh(FALSE
, 1);
616 case (CTRL
|'X'): /* toggle replace all option */
619 /* TRANSLATORS: abbreviation for Replace All occurrences */
620 menu_pat
[0].label
= N_("Repl All");
624 /* TRANSLATORS: Replace just one occurence */
625 menu_pat
[0].label
= N_("Repl One");
632 emlwrite(_("Replacement Cancelled"), NULL
);
633 reverse_all(defpat
, bsearch
); /* undo reverse buffer and pattern */
634 pico_refresh(FALSE
, 1);
635 reverse_all(defpat
, bsearch
); /* reverse buffer and pattern */
639 chword(defpat
, origpat
, bsearch
);
642 supdate(defpat
, bsearch
);
649 /* Since the search is not case sensitive, we must obtain the actual pattern
650 that appears in the text, so that we can highlight (and unhighlight) it
651 without using the wrong cases */
653 get_pat_cases(UCS
*realpat
, UCS
*searchpat
)
655 int i
, searchpatlen
, curoff
;
657 curoff
= curwp
->w_doto
;
658 searchpatlen
= ucs4_strlen(searchpat
);
660 for (i
= 0; i
< searchpatlen
; i
++)
661 realpat
[i
] = lgetc(curwp
->w_dotp
, curoff
++).c
;
663 realpat
[searchpatlen
] = '\0';
667 /* Ask the user about every occurence of orig pattern and replace it with a
668 repl pattern if the response is affirmative. */
670 replace_all(UCS
*orig
, UCS
*repl
, int bsearch
)
672 register int status
= 0;
679 LINE
*stop_line
= curwp
->w_dotp
;
680 int stop_offset
= curwp
->w_doto
;
684 /* similar to replace_pat. When we come here, if bsearch is set,
685 * the cursor is at the end of the match, so we must bring it back
690 curwp
->w_doto
-= ucs4_strlen(orig
) - 1;
691 curwp
->w_flag
|= WFMOVE
;
696 stop_offset
= curwp
->w_doto
;
698 if (forscan(&wrapt
, orig
, flags
, stop_line
, stop_offset
, PTBEG
)){
699 curwp
->w_flag
|= WFMOVE
; /* put cursor back */
701 reverse_all(orig
, bsearch
); /* undo reverse buffer and pattern */
704 get_pat_cases(realpat
, orig
);
705 pputs(realpat
, 1); /* highlight word */
709 snprintf(utf8tmp
, NPMT
, "Replace \"");
710 b
= utf8_to_ucs4_cpystr(utf8tmp
);
712 ucs4_strncpy(prompt
, b
, NPMT
);
713 prompt
[NPMT
-1] = '\0';
714 fs_give((void **) &b
);
717 promptp
= &prompt
[ucs4_strlen(prompt
)];
719 expandp(orig
, promptp
, NPMT
-(promptp
-prompt
));
720 reverse_all(orig
, bsearch
); /* reverse for internal use */
721 prompt
[NPMT
-1] = '\0';
722 promptp
+= ucs4_strlen(promptp
);
724 b
= utf8_to_ucs4_cpystr("\" with \"");
726 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
727 promptp
+= ucs4_strlen(promptp
);
728 prompt
[NPMT
-1] = '\0';
729 fs_give((void **) &b
);
732 expandp(repl
, promptp
, NPMT
-(promptp
-prompt
));
733 prompt
[NPMT
-1] = '\0';
734 promptp
+= ucs4_strlen(promptp
);
736 if((promptp
-prompt
) < NPMT
-1){
741 prompt
[NPMT
-1] = '\0';
743 status
= mlyesno(prompt
, TRUE
); /* ask user */
746 curwp
->w_doto
-= ucs4_strlen(realpat
) - 1;
747 curwp
->w_flag
|= WFMOVE
;
751 chword(realpat
, repl
, bsearch
); /* replace word */
752 supdate(realpat
, bsearch
);
754 chword(realpat
, realpat
, bsearch
); /* replace word by itself */
755 supdate(realpat
, bsearch
);
756 if(status
== ABORT
){ /* if cancelled return */
758 emlwrite("Replace All cancelled after %s changes", &eml
);
759 return (ABORT
); /* ... else keep looking */
766 utf8
= sucs4_to_utf8_cpystr(orig
, bsearch
);
769 emlwrite(_("No more matches for \"%s\""), &eml
);
770 fs_give((void **) &utf8
);
773 emlwrite(_("No more matches"), NULL
);
780 /* Read a replacement pattern. Modeled after readpattern(). */
782 srpat(char *utf8prompt
, UCS
*defpat
, size_t defpatlen
, int repl_mode
, int flags
)
786 int toggle
, bsearch
, bol
, eol
, exact
;
790 EXTRAKEYS menu_pat
[12];
792 bsearch
= flags
& SR_BACKWRD
;
793 bol
= flags
& SR_BEGLINE
;
794 eol
= flags
& SR_ENDLINE
;
795 exact
= flags
& SR_EXACTSR
;
796 toggle
= 0; /* reserved for future use */
798 memset(&menu_pat
, 0, sizeof(menu_pat
));
799 /* add exceptions here based on the location of the items in the menu */
800 for(i
= 0; i
< 10; i
++){
801 if(flags
& SR_ORIGMEN
){
802 menu_pat
[i
].name
= menu_srchpat
[10*toggle
+ i
].name
;
803 menu_pat
[i
].label
= menu_srchpat
[10*toggle
+ i
].label
;
804 menu_pat
[i
].key
= menu_srchpat
[10*toggle
+ i
].key
;
806 if (i
== REPLACE_KEY
)
807 menu_pat
[i
].label
= repl_mode
? N_("No Replace")
810 } else if(flags
& SR_OPTNMEN
){
811 menu_pat
[i
].name
= menu_srchopt
[i
].name
;
812 menu_pat
[i
].label
= menu_srchopt
[i
].label
;
813 menu_pat
[i
].key
= menu_srchopt
[i
].key
;
816 menu_pat
[i
].label
= exact
? N_("No Exact")
820 menu_pat
[i
].label
= repl_mode
? N_("No Replace")
824 menu_pat
[i
].label
= bsearch
? N_("Srch Fwd")
828 menu_pat
[i
].label
= bol
? N_("Anywhere")
832 menu_pat
[i
].label
= eol
? N_("Anywhere")
838 KS_OSDATASET(&menu_pat
[i
], KS_NONE
);
841 b
= utf8_to_ucs4_cpystr(utf8prompt
);
843 ucs4_strncpy(prompt
, b
, NPMT
);
844 prompt
[NPMT
-1] = '\0';
846 fs_give((void **) &b
);
847 b
= utf8_to_ucs4_cpystr(N_(" backward"));
848 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
849 prompt
[NPMT
-1] = '\0';
852 fs_give((void **) &b
);
853 b
= utf8_to_ucs4_cpystr(N_(" at start of line"));
854 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
855 prompt
[NPMT
-1] = '\0';
857 fs_give((void **) &b
);
858 b
= utf8_to_ucs4_cpystr(N_(" at end of line"));
859 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
860 prompt
[NPMT
-1] = '\0';
863 fs_give((void **) &b
);
864 b
= utf8_to_ucs4_cpystr(N_(" exactly for"));
865 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
866 prompt
[NPMT
-1] = '\0';
868 if(b
) fs_give((void **) &b
);
871 promptp
= &prompt
[ucs4_strlen(prompt
)];
874 b
= utf8_to_ucs4_cpystr(" (to replace)");
876 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
877 promptp
+= ucs4_strlen(promptp
);
878 prompt
[NPMT
-1] = '\0';
879 fs_give((void **) &b
);
884 if((promptp
-prompt
) < NPMT
-2){
890 expandp(pat
, promptp
, NPMT
-(promptp
-prompt
));
891 prompt
[NPMT
-1] = '\0';
892 promptp
+= ucs4_strlen(promptp
);
894 if((promptp
-prompt
) < NPMT
-1){
900 if((promptp
-prompt
) < NPMT
-2){
906 prompt
[NPMT
-1] = '\0';
908 s
= mlreplyd(prompt
, defpat
, defpatlen
, QDEFLT
, menu_pat
);
910 if (s
== TRUE
|| s
== FALSE
){ /* changed or not, they're done */
911 if(!defpat
[0]){ /* use default */
912 ucs4_strncpy(defpat
, pat
, defpatlen
);
913 defpat
[defpatlen
-1] = '\0';
915 else if(ucs4_strcmp(pat
, defpat
)){ /* Specified */
916 ucs4_strncpy(pat
, defpat
, NPAT
);
921 s
= TRUE
; /* let caller know to proceed */
929 * Read a pattern. Stash it in the external variable "pat". The "pat" is not
930 * updated if the user types in an empty line. If the user typed an empty line,
931 * and there is no old pattern, it is an error. Display the old pattern, in the
932 * style of Jeff Lomicka. There is some do-it-yourself control expansion.
933 * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
938 readnumpat(char *utf8prompt
)
942 EXTRAKEYS menu_pat
[12];
944 memset(&menu_pat
, 0, 10*sizeof(EXTRAKEYS
));
945 menu_pat
[i
= 0].name
= "^T";
946 menu_pat
[i
].label
= N_("No Line Number");
947 menu_pat
[i
].key
= (CTRL
|'T');
948 KS_OSDATASET(&menu_pat
[i
++], KS_NONE
);
950 menu_pat
[i
].name
= NULL
;
954 switch(mlreplyd_utf8(utf8prompt
, numpat
, NPMT
, QNORML
, menu_pat
)){
957 for(i
= n
= 0; numpat
[i
]; i
++)
958 if(strchr("0123456789", numpat
[i
])){
959 n
= (n
* 10) + (numpat
[i
] - '0');
982 readpattern(char *utf8prompt
, int text_mode
, int flags
)
986 int toggle
, bsearch
, bol
, eol
, exact
;
990 EXTRAKEYS menu_pat
[12];
992 bsearch
= flags
& SR_BACKWRD
;
993 bol
= flags
& SR_BEGLINE
;
994 eol
= flags
& SR_ENDLINE
;
995 exact
= flags
& SR_EXACTSR
;
996 toggle
= 0; /* reserved for future use */
998 memset(&menu_pat
, 0, sizeof(menu_pat
));
999 /* add exceptions here based on the location of the items in the menu */
1000 for(i
= 0; i
< 10; i
++){
1001 if(flags
& SR_ORIGMEN
){
1002 menu_pat
[i
].name
= menu_srchpat
[10*toggle
+ i
].name
;
1003 menu_pat
[i
].label
= menu_srchpat
[10*toggle
+ i
].label
;
1004 menu_pat
[i
].key
= menu_srchpat
[10*toggle
+ i
].key
;
1006 if (i
== REPLACE_KEY
)
1007 memset(&menu_pat
[i
], 0, sizeof(EXTRAKEYS
));
1008 if (i
> REPLACE_KEY
&& !text_mode
)
1009 memset(&menu_pat
[i
], 0, sizeof(EXTRAKEYS
));
1011 } else if(flags
& SR_OPTNMEN
){
1012 menu_pat
[i
].name
= menu_srchopt
[i
].name
;
1013 menu_pat
[i
].label
= menu_srchopt
[i
].label
;
1014 menu_pat
[i
].key
= menu_srchopt
[i
].key
;
1017 menu_pat
[i
].label
= exact
? N_("No Exact")
1021 menu_pat
[i
].label
= bsearch
? N_("Srch Fwd")
1025 menu_pat
[i
].label
= bol
? N_("Anywhere")
1029 menu_pat
[i
].label
= eol
? N_("Anywhere")
1034 if(menu_pat
[i
].name
)
1035 KS_OSDATASET(&menu_pat
[i
], KS_NONE
);
1039 b
= utf8_to_ucs4_cpystr(utf8prompt
);
1041 ucs4_strncpy(tpat
, b
, NPAT
+20);
1042 tpat
[NPAT
+20-1] = '\0';
1044 fs_give((void **) &b
);
1045 b
= utf8_to_ucs4_cpystr(N_(" backward"));
1046 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1047 tpat
[NPAT
+20-1] = '\0';
1050 fs_give((void **) &b
);
1051 b
= utf8_to_ucs4_cpystr(N_(" at start of line"));
1052 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1053 tpat
[NPAT
+20-1] = '\0';
1055 fs_give((void **) &b
);
1056 b
= utf8_to_ucs4_cpystr(N_(" at end of line"));
1057 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1058 tpat
[NPAT
+20-1] = '\0';
1061 fs_give((void **) &b
);
1062 b
= utf8_to_ucs4_cpystr(N_(" exactly for"));
1063 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1064 tpat
[NPAT
+20-1] = '\0';
1066 if(b
) fs_give((void **) &b
);
1069 tpatp
= &tpat
[ucs4_strlen(tpat
)];
1072 if((tpatp
-tpat
) < NPAT
+20-2){
1078 expandp(pat
, tpatp
, NPAT
+20-(tpatp
-tpat
));
1079 tpat
[NPAT
+20-1] = '\0';
1080 tpatp
+= ucs4_strlen(tpatp
);
1082 if((tpatp
-tpat
) < NPAT
+20-1){
1088 if((tpatp
-tpat
) < NPAT
+20-3){
1095 tpat
[NPAT
+20-1] = '\0';
1097 s
= mlreplyd(tpat
, tpat
, NPAT
, QNORML
, menu_pat
);
1099 if ((s
== TRUE
) && ucs4_strcmp(pat
,tpat
)){ /* Specified */
1100 ucs4_strncpy(pat
, tpat
, NPAT
);
1104 else if (s
== FALSE
&& pat
[0] != '\0') /* CR, but old one */
1110 /* given a line, reverse its content */
1112 reverse_line(LINE
*l
)
1117 if(l
== NULL
) return;
1119 for(i
= 0; i
< j
; i
++, j
--){
1120 u
= lgetc(l
, j
).c
; /* reverse the text */
1121 lgetc(l
, j
).c
= lgetc(l
, i
).c
;
1123 a
= lgetc(l
, j
).a
; /* and the attribute */
1124 lgetc(l
, j
).a
= lgetc(l
, i
).a
;
1130 reverse_all(UCS
*pat
, int bsearch
)
1139 reverse_buffer(void)
1143 for(l
= lforw(curbp
->b_linep
); l
!= curbp
->b_linep
; l
= lforw(l
))
1146 /* reverse links in buffer */
1149 lforw(l
) = lback(l
);
1151 } while(l
!= curbp
->b_linep
);
1155 lback(lforw(l
)) = l
;
1157 } while (l
!= curbp
->b_linep
);
1159 curwp
->w_doto
= llength(curwp
->w_dotp
) - 1 - curwp
->w_doto
;
1164 /* given a UCS4 string reverse its content */
1166 reverse_ucs4(UCS
*s
)
1171 j
= ucs4_strlen(s
) - 1;
1172 for(i
= 0; i
< j
; i
++, j
--){
1180 /* search forward for a <patrn>.
1181 * A backward search is a forward search in backward lines with backward
1185 forscan(int *wrapt
, /* boolean indicating search wrapped */
1186 UCS
*patrn
, /* string to scan for */
1187 int flags
, /* direction and position */
1188 LINE
*limitp
, /* stop searching if reached */
1189 int limito
, /* stop searching if reached */
1190 int leavep
) /* place to leave point
1191 PTBEG = beginning of match
1192 PTEND = at end of match */
1195 LINE
*curline
; /* current line during scan */
1196 int curoff
; /* position within current line */
1197 LINE
*lastline
; /* last line position during scan */
1198 int lastoff
; /* position within last line */
1199 UCS c
; /* character at current position */
1200 LINE
*matchline
; /* current line during matching */
1201 int matchoff
; /* position in matching line */
1202 UCS
*patptr
; /* pointer into pattern */
1203 int stopoff
; /* offset to stop search */
1204 LINE
*stopline
; /* line to stop search */
1205 int ftest
= 0; /* position of first character of test */
1210 bsearch
= flags
& SR_BACKWRD
;
1211 bol
= flags
& SR_BEGLINE
;
1212 eol
= flags
& SR_ENDLINE
;
1215 /* if bsearch is set we return the match at the beginning of the
1216 * matching string, so we make this algorithm return the end of
1217 * the string, so that when we reverse we be at the beginning.
1220 leavep
= leavep
== PTBEG
? PTEND
: PTBEG
;
1223 * the idea is to set the character to end the search at the
1224 * next character in the buffer. thus, let the search wrap
1225 * completely around the buffer.
1227 * first, test to see if we are at the end of the line,
1228 * otherwise start searching on the next character.
1230 if(curwp
->w_doto
== llength(curwp
->w_dotp
)){
1232 * dot is not on end of a line
1233 * start at 0 offset of the next line
1235 stopoff
= curoff
= 0;
1236 stopline
= curline
= lforw(curwp
->w_dotp
);
1237 if (curwp
->w_dotp
== curbp
->b_linep
)
1241 stopoff
= curoff
= curwp
->w_doto
;
1242 stopline
= curline
= curwp
->w_dotp
;
1245 /* scan each character until we hit the head link record */
1248 * maybe wrapping is a good idea
1252 if(curline
== curbp
->b_linep
)
1255 /* save the current position in case we need to
1256 restore it on a match */
1261 /* get the current character resolving EOLs */
1262 if (curoff
== llength(curline
)) { /* if at EOL */
1263 curline
= lforw(curline
); /* skip to next line */
1265 c
= '\n'; /* and return a <NL> */
1267 else if(curoff
== -1){
1268 stopoff
= curoff
= 0;
1273 c
= lgetc(curline
, curoff
++).c
; /* get the char */
1276 ftest
= bsearch
== 0 ? 1 : llength(curline
) - ucs4_strlen(patrn
) + 1;
1278 ftest
= bsearch
== 0 ? llength(curline
) - ucs4_strlen(patrn
) + 1 : 1;
1279 /* test it against first char in pattern */
1280 if (eq(c
, patrn
[0]) != FALSE
/* if we find it..*/
1281 && ((bol
== 0 && eol
== 0) /* ...and if it is anywhere */
1282 || (bol
!= 0 /* ...or is at the begin or line */
1283 && ((bsearch
== 0 && curoff
== ftest
) /* and search forward and found at beginning of line */
1284 || (bsearch
!= 0 && curoff
== ftest
))) /* or search backward and found at end of line */
1285 || (eol
!= 0 /* ...or is at the end or line */
1286 && ((bsearch
== 0 && curoff
== ftest
) /* and search forward and found at beginning of line */
1287 || (bsearch
!= 0 && curoff
== ftest
))) /* or search backward and found at end of line */
1289 /* setup match pointers */
1290 matchline
= curline
;
1294 /* scan through patrn for a match */
1295 while (*++patptr
!= '\0') {
1296 /* advance all the pointers */
1297 if (matchoff
>= llength(matchline
)) {
1298 /* advance past EOL */
1299 matchline
= lforw(matchline
);
1303 c
= lgetc(matchline
, matchoff
++).c
;
1305 if(matchline
== limitp
&& matchoff
== limito
)
1308 /* and test it against the pattern */
1309 if (eq(*patptr
, c
) == FALSE
)
1313 /* A SUCCESSFUL MATCH!!! */
1314 /* reset the global "." pointers */
1315 if (leavep
== PTEND
) { /* at end of string */
1316 curwp
->w_dotp
= matchline
;
1317 curwp
->w_doto
= matchoff
- 1;
1319 else { /* at beginning of string */
1320 curwp
->w_dotp
= lastline
;
1321 curwp
->w_doto
= lastoff
;
1324 curwp
->w_flag
|= WFMOVE
; /* flag that we have moved */
1328 fail
:; /* continue to search */
1329 if(((curline
== stopline
) && (curoff
== stopoff
))
1330 || (curline
== limitp
&& curoff
== limito
))
1331 break; /* searched everywhere... */
1333 /* we could not find a match */
1340 /* expandp: expand control key sequences for output */
1342 expandp(UCS
*srcstr
, /* string to expand */
1343 UCS
*deststr
, /* destination of expanded string */
1344 int maxlength
) /* maximum chars in destination */
1346 UCS c
; /* current char to translate */
1348 /* scan through the string */
1349 while ((c
= *srcstr
++) != 0) {
1350 if (c
== '\n') { /* its an EOL */
1356 } else if (c
< 0x20 || c
== 0x7f) { /* control character */
1358 *deststr
++ = c
^ 0x40;
1360 } else if (c
== '%') {
1364 } else { /* any other character */
1369 /* check for maxlength */
1370 if (maxlength
< 4) {
1383 * chword() - change the given word, wp, pointed to by the curwp->w_dot
1384 * pointers to the word in cb
1385 * if bsearch is set, then cb is supposed to come unreversed, while
1386 * the buffer is supposed to be reversed, so we must reverse cb before
1390 chword(UCS
*wb
, UCS
*cb
, int bsearch
)
1393 ldelete(ucs4_strlen(wb
), NULL
); /* not saved in kill buffer */
1394 if(bsearch
) reverse_ucs4(cb
);
1395 for(u
= cb
; *u
!= '\0'; u
++)
1397 if(bsearch
) reverse_ucs4(cb
);
1399 curwp
->w_flag
|= WFEDIT
;
1403 supdate(UCS
*pat
, int bsearch
)
1405 reverse_all(pat
, bsearch
); /* undo reverse buffer and pattern */
1407 reverse_all(pat
, bsearch
); /* reverse buffer and pattern */
1411 sucs4_to_utf8_cpystr(UCS
*orig
, int bsearch
)
1414 if(bsearch
) reverse_ucs4(orig
);
1415 utf8
= ucs4_to_utf8_cpystr(orig
);
1416 if(bsearch
) reverse_ucs4(orig
);