1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: search.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * Program: Searching routines
22 * The functions in this file implement commands that search in the forward
23 * and backward directions. There are no special characters in the search
24 * strings. Probably should have a regular expression search, or something
32 int expandp(UCS
*, UCS
*, int);
33 int readnumpat(char *);
34 void get_pat_cases(UCS
*, UCS
*);
35 int srpat(char *, UCS
*, size_t, int, int);
36 int readpattern(char *, int, int);
37 int replace_pat(UCS
*, int *, int);
38 int replace_all(UCS
*, UCS
*, int);
39 void reverse_line(LINE
*);
40 void reverse_buffer(void);
41 void reverse_ucs4(UCS
*);
42 void reverse_all(UCS
*, int);
43 void supdate(UCS
*, int);
44 char *sucs4_to_utf8_cpystr(UCS
*, int);
46 #define FWS_RETURN(RV) { \
48 curwp->w_flag |= WFMODE; \
53 /* The search menu leaves a number of keys free, some are taken
54 * as subcommands of the search command, and some are taken are
55 * editing commands. This leaves the following keys open:
56 * ^J, ^N, ^O, ^P, ^R, ^T, ^U, ^V, ^W, ^X and ^Y.
57 * Out of these keys, ^J, ^N, ^P and ^X are not defined as commands, however,
58 * in some patches ^N, ^P and ^X are defined. ^N is defined as part of
59 * an editing command, ^P and ^X are defined to delete paragraphs and to
60 * remove text to the end of file, so only ^J is undefined.
63 #define REPLACE_KEY 2 /* the location of the replace key in the array below */
65 EXTRAKEYS menu_srchpat
[] = {
66 {"^Y", N_("FirstLine"), (CTRL
|'Y')},
67 {"^V", N_("LastLine"), (CTRL
|'V')},
68 {"^R", N_("No Replace"), (CTRL
|'R')},
69 {"^^", N_("Optns Menu"), (CTRL
|'^')}, /* toggle this menu or options menu */
70 {"^T", N_("LineNumber"), (CTRL
|'T')},
71 {"^W", N_("Start of Para"), (CTRL
|'W')},
72 {"^O", N_("End of Para"), (CTRL
|'O')},
73 {"^U", N_("FullJustify"), (CTRL
|'U')},
78 #define EXACTSR_KEY 1 /* toggle an exact or approximate search */
79 #define BGNLINE_KEY 3 /* the location of Bgn Line command in the array below */
80 #define ENDLINE_KEY 4 /* the location of End Line command in the array below */
81 #define BSEARCH_KEY 5 /* the location of the bsearch key in the array below */
82 EXTRAKEYS menu_srchopt
[] = {
83 {"^^", N_("Orig Menu"), (CTRL
|'^')}, /* toggle original or options menu */
84 {"^X", N_("Exact"), (CTRL
|'X')}, /* toggle exact vs non exact */
85 {"^R", N_("No Replace"), (CTRL
|'R')}, /* toggle replace or not replace */
86 {"^V", N_("Bgn Line"), (CTRL
|'V')}, /* toggle Bgn Line or anywhere */
87 {"^N", N_("End Line"), (CTRL
|'N')}, /* toggle End Line or anywhere */
88 {"^P", N_("BackSearch"), (CTRL
|'P')}, /* toggle Backward or forward */
96 * Search forward. Get a search string from the user, and search, beginning at
97 * ".", for the string. If found, reset the "." to be just after the match
98 * string, and [perhaps] repaint the display. Bound to "C-S".
101 /* string search input parameters */
103 #define PTBEG 1 /* leave the point at the begining on search */
104 #define PTEND 2 /* leave the point at the end on search */
106 #define NPMT (2*NLINE+32)
109 static char *SearchHelpText
[] = {
110 /* TRANSLATORS: Some help text that goes together in a group. */
111 N_("Help for Search Command"),
113 N_(" Enter the words or characters you would like to search"),
114 N_("~ for, then press ~R~e~t~u~r~n. The search then takes place."),
115 N_(" When the characters or words that you entered "),
116 N_(" are found, the buffer will be redisplayed with the cursor "),
117 N_(" at the beginning of the selected text."),
119 N_(" The most recent string for which a search was made is"),
120 N_(" displayed in the \"Search\" prompt between the square"),
121 N_(" brackets. This string is the default search prompt."),
122 N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"),
123 N_(" search to be made with the default value."),
125 N_(" The text search is not case sensitive, and will examine the"),
126 N_(" entire message."),
128 N_(" Should the search fail, a message will be displayed."),
130 N_("End of Search Help."),
137 * Compare two characters. The "bc" comes from the buffer. It has it's case
138 * folded out. The "pc" is from the pattern.
143 if ((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0){
144 if (bc
>='a' && bc
<='z')
147 if (pc
>='a' && pc
<='z')
156 forwsearch(int f
, int n
)
159 int wrapt
= FALSE
, wrapt2
= FALSE
;
160 int repl_mode
= FALSE
;
166 /* resolve the repeat count */
170 if (n
< 1) /* search backwards */
174 /* defaults: usual menu, search forward, not case sensitive */
176 flags
= SR_ORIGMEN
| SR_FORWARD
;
178 /* exact search is sticky -- that is, once one is done, so will be
179 * the next ones. This is consistent with all all searches being
180 * case insensitive by default.
182 if((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0)
187 /* ask the user for the text of a pattern */
190 if (gmode
& MDREPLACE
)
191 status
= srpat("Search", defpat
, NPAT
, repl_mode
, flags
);
193 status
= readpattern("Search", TRUE
, flags
);
196 case TRUE
: /* user typed something */
200 case HELPCH
: /* help requested */
202 VARS_TO_SAVE
*saved_state
;
204 saved_state
= save_pico_state();
205 (*Pmaster
->helper
)(Pmaster
->search_help
,
206 _("Help for Searching"), 1);
208 restore_pico_state(saved_state
);
209 free_pico_state(saved_state
);
213 pico_help(SearchHelpText
, _("Help for Searching"), 1);
215 case (CTRL
|'L'): /* redraw requested */
216 pico_refresh(FALSE
, 1);
222 if(flags
& SR_ORIGMEN
){
223 /* Undefined still */
225 if(flags
& SR_OPTNMEN
){
226 if(flags
& SR_FORWARD
){
227 flags
&= ~SR_FORWARD
;
230 flags
&= ~SR_BACKWRD
;
237 if(flags
& SR_ORIGMEN
){
241 } else if (flags
& SR_OPTNMEN
){
242 if(flags
& SR_ENDLINE
)
243 flags
&= ~SR_ENDLINE
;
244 if(flags
& SR_BEGLINE
)
245 flags
&= ~SR_BEGLINE
;
252 if(flags
& SR_ORIGMEN
){
253 /* undefined still */
254 } else if (flags
& SR_OPTNMEN
){
255 if(flags
& SR_BEGLINE
)
256 flags
&= ~SR_BEGLINE
;
257 if(flags
& SR_ENDLINE
)
258 flags
&= ~SR_ENDLINE
;
265 if(flags
& SR_ORIGMEN
){
272 if (flags
& SR_ORIGMEN
){
273 flags
&= ~SR_ORIGMEN
;
276 flags
&= ~SR_OPTNMEN
;
282 if(flags
& SR_OPTNMEN
){
283 if (flags
& SR_NOEXACT
){
284 flags
&= ~SR_NOEXACT
;
287 flags
&= ~SR_EXACTSR
;
290 if((curwp
->w_bufp
->b_mode
& MDEXACT
) == 0)
291 curwp
->w_bufp
->b_mode
|= MDEXACT
;
293 curwp
->w_bufp
->b_mode
&= ~MDEXACT
;
298 if(flags
& SR_ORIGMEN
){
299 switch(status
= readnumpat(_("Search to Line Number : "))){
301 emlwrite(_("Search to Line Number Cancelled"), NULL
);
305 emlwrite(_("Line number must be greater than zero"), NULL
);
309 emlwrite(_("Line number must contain only digits"), NULL
);
324 if(flags
& SR_ORIGMEN
){
325 LINE
*linep
= curwp
->w_dotp
;
326 int offset
= curwp
->w_doto
;
332 * if we're asked to backup and we're already
335 if((lastflag
& CFSRCH
)
336 && linep
== curwp
->w_dotp
337 && offset
== curwp
->w_doto
338 && !(offset
== 0 && lback(linep
) == curbp
->b_linep
)){
349 if(flags
& SR_ORIGMEN
){
350 if(curwp
->w_dotp
!= curbp
->b_linep
){
360 if(flags
& SR_ORIGMEN
){
367 case (CTRL
|'R'): /* toggle replacement option */
368 repl_mode
= !repl_mode
;
373 emlwrite(_("Search Cancelled"), NULL
);
380 /* replace option is disabled */
381 if (!(gmode
& MDREPLACE
)){
382 ucs4_strncpy(defpat
, pat
, NPAT
);
383 defpat
[NPAT
-1] = '\0';
386 else if (search
){ /* search now */
387 ucs4_strncpy(pat
, defpat
, NPAT
); /* remember this search for the future */
393 reverse_all(defpat
, flags
& SR_BACKWRD
);
396 * This code is kind of dumb. What I want is successive C-W 's to
397 * move dot to successive occurences of the pattern. So, if dot is
398 * already sitting at the beginning of the pattern, then we'll move
399 * forward a char before beginning the search. We'll let the
400 * automatic wrapping handle putting the dot back in the right
403 status
= 0; /* using "status" as int temporarily! */
405 if(defpat
[status
] == '\0'){
410 if(status
+ curwp
->w_doto
>= llength(curwp
->w_dotp
) ||
411 !eq(defpat
[status
],lgetc(curwp
->w_dotp
, curwp
->w_doto
+ status
).c
))
416 /* search for the pattern */
419 if((status
= forscan(&wrapt
,defpat
, flags
, NULL
,0,PTBEG
)) == FALSE
)
423 /* and complain if not there */
424 if (status
== FALSE
){
430 utf8
= sucs4_to_utf8_cpystr(defpat
? defpat
: x
, flags
& SR_BACKWRD
);
431 /* TRANSLATORS: reporting the result of a failed search */
433 emlwrite(_("\"%s\" not found"), &eml
);
435 fs_give((void **) &utf8
);
437 else if((gmode
& MDREPLACE
) && repl_mode
== TRUE
){
438 status
= replace_pat(defpat
, &wrapt2
, flags
& SR_BACKWRD
); /* replace pattern */
439 if (wrapt
== TRUE
|| wrapt2
== TRUE
){
440 eml
.s
= (status
== ABORT
) ? "cancelled but wrapped" : "Wrapped";
441 emlwrite("Replacement %s", &eml
);
444 else if(wrapt
== TRUE
){
445 emlwrite("Search Wrapped", NULL
);
447 else if(status
== TRUE
){
451 reverse_all(defpat
, flags
& SR_BACKWRD
);
452 if(curwp
->w_doto
== -1){
454 curwp
->w_flag
|= WFMOVE
;
460 /* Replace a pattern with the pattern the user types in one or more times. */
462 replace_pat(UCS
*defpat
, int *wrapt
, int bsearch
)
465 UCS lpat
[NPAT
], origpat
[NPAT
]; /* case sensitive pattern */
466 EXTRAKEYS menu_pat
[12];
467 int repl_all
= FALSE
;
476 curwp
->w_doto
-= ucs4_strlen(defpat
) - 1;
478 else flags
= SR_FORWARD
;
480 forscan(wrapt
, defpat
, flags
, NULL
, 0, PTBEG
); /* go to word to be replaced */
483 memset((void *)&menu_pat
, 0, sizeof(menu_pat
));
484 /* additional 'replace all' menu option */
485 menu_pat
[0].name
= "^X";
486 menu_pat
[0].key
= (CTRL
|'X');
487 menu_pat
[0].label
= N_("Repl All");
488 KS_OSDATASET(&menu_pat
[0], KS_NONE
);
492 /* we need to reverse the buffer back to its original state, so that
493 * the user will not see that we reversed it under them. The cursor
494 * is at the beginning of the reverse string, that is at the end
495 * of the string. Move it back to the beginning.
498 reverse_all(defpat
, bsearch
); /* reverse for normal view */
501 get_pat_cases(origpat
, defpat
);
502 pputs(origpat
, 1); /* highlight word */
505 snprintf(utf8tmp
, NPMT
, "Replace%s \"", repl_all
? " every" : "");
506 b
= utf8_to_ucs4_cpystr(utf8tmp
);
508 ucs4_strncpy(prompt
, b
, NPMT
);
509 prompt
[NPMT
-1] = '\0';
510 fs_give((void **) &b
);
513 promptp
= &prompt
[ucs4_strlen(prompt
)];
515 expandp(defpat
, promptp
, NPMT
-(promptp
-prompt
));
516 prompt
[NPMT
-1] = '\0';
517 promptp
+= ucs4_strlen(promptp
);
519 b
= utf8_to_ucs4_cpystr("\" with");
521 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
522 promptp
+= ucs4_strlen(promptp
);
523 prompt
[NPMT
-1] = '\0';
524 fs_give((void **) &b
);
528 if((promptp
-prompt
) < NPMT
-2){
534 expandp(rpat
, promptp
, NPMT
-(promptp
-prompt
));
535 prompt
[NPMT
-1] = '\0';
536 promptp
+= ucs4_strlen(promptp
);
538 if((promptp
-prompt
) < NPMT
-1){
544 if((promptp
-prompt
) < NPMT
-3){
551 prompt
[NPMT
-1] = '\0';
553 status
= mlreplyd(prompt
, lpat
, NPAT
, QDEFLT
, menu_pat
);
555 curwp
->w_flag
|= WFMOVE
;
557 reverse_all(defpat
, bsearch
); /* reverse for internal use */
564 ucs4_strncpy(rpat
, lpat
, NPAT
); /* remember default */
568 ucs4_strncpy(lpat
, rpat
, NPAT
); /* use default */
573 status
= replace_all(defpat
, lpat
, bsearch
);
577 curwp
->w_doto
-= ucs4_strlen(defpat
) - 1;
578 chword(defpat
, lpat
, bsearch
); /* replace word */
579 /* after substitution the cursor is past the end of the
580 * replaced string, so we backdown in backward search,
581 * to make it appear at the beginning of the replaced string.
582 * We make this choice in case the next search is backward,
583 * because if we put the cursor at the end, the next backward
584 * search might hit the substituted string, and we want to
585 * avoid that, because we probably do not want to substitute
586 * the new string, but old text.
588 if(bsearch
&& (lback(curwp
->w_dotp
) != curbp
->b_linep
589 || curwp
->w_doto
> 0))
591 supdate(defpat
, bsearch
);
600 case HELPCH
: /* help requested */
602 VARS_TO_SAVE
*saved_state
;
604 saved_state
= save_pico_state();
605 (*Pmaster
->helper
)(Pmaster
->search_help
,
606 _("Help for Searching"), 1);
608 restore_pico_state(saved_state
);
609 free_pico_state(saved_state
);
613 pico_help(SearchHelpText
, _("Help for Searching"), 1);
615 case (CTRL
|'L'): /* redraw requested */
616 pico_refresh(FALSE
, 1);
620 case (CTRL
|'X'): /* toggle replace all option */
623 /* TRANSLATORS: abbreviation for Replace All occurences */
624 menu_pat
[0].label
= N_("Repl All");
628 /* TRANSLATORS: Replace just one occurence */
629 menu_pat
[0].label
= N_("Repl One");
636 emlwrite(_("Replacement Cancelled"), NULL
);
637 reverse_all(defpat
, bsearch
); /* undo reverse buffer and pattern */
638 pico_refresh(FALSE
, 1);
639 reverse_all(defpat
, bsearch
); /* reverse buffer and pattern */
643 chword(defpat
, origpat
, bsearch
);
646 supdate(defpat
, bsearch
);
653 /* Since the search is not case sensitive, we must obtain the actual pattern
654 that appears in the text, so that we can highlight (and unhighlight) it
655 without using the wrong cases */
657 get_pat_cases(UCS
*realpat
, UCS
*searchpat
)
659 int i
, searchpatlen
, curoff
;
661 curoff
= curwp
->w_doto
;
662 searchpatlen
= ucs4_strlen(searchpat
);
664 for (i
= 0; i
< searchpatlen
; i
++)
665 realpat
[i
] = lgetc(curwp
->w_dotp
, curoff
++).c
;
667 realpat
[searchpatlen
] = '\0';
671 /* Ask the user about every occurence of orig pattern and replace it with a
672 repl pattern if the response is affirmative. */
674 replace_all(UCS
*orig
, UCS
*repl
, int bsearch
)
676 register int status
= 0;
683 LINE
*stop_line
= curwp
->w_dotp
;
684 int stop_offset
= curwp
->w_doto
;
688 /* similar to replace_pat. When we come here, if bsearch is set,
689 * the cursor is at the end of the match, so we must bring it back
694 curwp
->w_doto
-= ucs4_strlen(orig
) - 1;
695 curwp
->w_flag
|= WFMOVE
;
700 stop_offset
= curwp
->w_doto
;
702 if (forscan(&wrapt
, orig
, flags
, stop_line
, stop_offset
, PTBEG
)){
703 curwp
->w_flag
|= WFMOVE
; /* put cursor back */
705 reverse_all(orig
, bsearch
); /* undo reverse buffer and pattern */
708 get_pat_cases(realpat
, orig
);
709 pputs(realpat
, 1); /* highlight word */
713 snprintf(utf8tmp
, NPMT
, "Replace \"");
714 b
= utf8_to_ucs4_cpystr(utf8tmp
);
716 ucs4_strncpy(prompt
, b
, NPMT
);
717 prompt
[NPMT
-1] = '\0';
718 fs_give((void **) &b
);
721 promptp
= &prompt
[ucs4_strlen(prompt
)];
723 expandp(orig
, promptp
, NPMT
-(promptp
-prompt
));
724 reverse_all(orig
, bsearch
); /* reverse for internal use */
725 prompt
[NPMT
-1] = '\0';
726 promptp
+= ucs4_strlen(promptp
);
728 b
= utf8_to_ucs4_cpystr("\" with \"");
730 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
731 promptp
+= ucs4_strlen(promptp
);
732 prompt
[NPMT
-1] = '\0';
733 fs_give((void **) &b
);
736 expandp(repl
, promptp
, NPMT
-(promptp
-prompt
));
737 prompt
[NPMT
-1] = '\0';
738 promptp
+= ucs4_strlen(promptp
);
740 if((promptp
-prompt
) < NPMT
-1){
745 prompt
[NPMT
-1] = '\0';
747 status
= mlyesno(prompt
, TRUE
); /* ask user */
750 curwp
->w_doto
-= ucs4_strlen(realpat
) - 1;
751 curwp
->w_flag
|= WFMOVE
;
755 chword(realpat
, repl
, bsearch
); /* replace word */
756 supdate(realpat
, bsearch
);
758 chword(realpat
, realpat
, bsearch
); /* replace word by itself */
759 supdate(realpat
, bsearch
);
760 if(status
== ABORT
){ /* if cancelled return */
762 emlwrite("Replace All cancelled after %s changes", &eml
);
763 return (ABORT
); /* ... else keep looking */
770 utf8
= sucs4_to_utf8_cpystr(orig
, bsearch
);
773 emlwrite(_("No more matches for \"%s\""), &eml
);
774 fs_give((void **) &utf8
);
777 emlwrite(_("No more matches"), NULL
);
784 /* Read a replacement pattern. Modeled after readpattern(). */
786 srpat(char *utf8prompt
, UCS
*defpat
, size_t defpatlen
, int repl_mode
, int flags
)
790 int toggle
, bsearch
, bol
, eol
, exact
;
794 EXTRAKEYS menu_pat
[12];
796 bsearch
= flags
& SR_BACKWRD
;
797 bol
= flags
& SR_BEGLINE
;
798 eol
= flags
& SR_ENDLINE
;
799 exact
= flags
& SR_EXACTSR
;
800 toggle
= 0; /* reserved for future use */
802 memset(&menu_pat
, 0, sizeof(menu_pat
));
803 /* add exceptions here based on the location of the items in the menu */
804 for(i
= 0; i
< 10; i
++){
805 if(flags
& SR_ORIGMEN
){
806 menu_pat
[i
].name
= menu_srchpat
[10*toggle
+ i
].name
;
807 menu_pat
[i
].label
= menu_srchpat
[10*toggle
+ i
].label
;
808 menu_pat
[i
].key
= menu_srchpat
[10*toggle
+ i
].key
;
810 if (i
== REPLACE_KEY
)
811 menu_pat
[i
].label
= repl_mode
? N_("No Replace")
814 } else if(flags
& SR_OPTNMEN
){
815 menu_pat
[i
].name
= menu_srchopt
[i
].name
;
816 menu_pat
[i
].label
= menu_srchopt
[i
].label
;
817 menu_pat
[i
].key
= menu_srchopt
[i
].key
;
820 menu_pat
[i
].label
= exact
? N_("No Exact")
824 menu_pat
[i
].label
= repl_mode
? N_("No Replace")
828 menu_pat
[i
].label
= bsearch
? N_("Srch Fwd")
832 menu_pat
[i
].label
= bol
? N_("Anywhere")
836 menu_pat
[i
].label
= eol
? N_("Anywhere")
842 KS_OSDATASET(&menu_pat
[i
], KS_NONE
);
845 b
= utf8_to_ucs4_cpystr(utf8prompt
);
847 ucs4_strncpy(prompt
, b
, NPMT
);
848 prompt
[NPMT
-1] = '\0';
850 fs_give((void **) &b
);
851 b
= utf8_to_ucs4_cpystr(N_(" backward"));
852 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
853 prompt
[NPMT
-1] = '\0';
856 fs_give((void **) &b
);
857 b
= utf8_to_ucs4_cpystr(N_(" at start of line"));
858 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
859 prompt
[NPMT
-1] = '\0';
861 fs_give((void **) &b
);
862 b
= utf8_to_ucs4_cpystr(N_(" at end of line"));
863 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
864 prompt
[NPMT
-1] = '\0';
867 fs_give((void **) &b
);
868 b
= utf8_to_ucs4_cpystr(N_(" exactly for"));
869 if(b
) ucs4_strncat(prompt
, b
, ucs4_strlen(b
));
870 prompt
[NPMT
-1] = '\0';
872 if(b
) fs_give((void **) &b
);
875 promptp
= &prompt
[ucs4_strlen(prompt
)];
878 b
= utf8_to_ucs4_cpystr(" (to replace)");
880 ucs4_strncpy(promptp
, b
, NPMT
-(promptp
-prompt
));
881 promptp
+= ucs4_strlen(promptp
);
882 prompt
[NPMT
-1] = '\0';
883 fs_give((void **) &b
);
888 if((promptp
-prompt
) < NPMT
-2){
894 expandp(pat
, promptp
, NPMT
-(promptp
-prompt
));
895 prompt
[NPMT
-1] = '\0';
896 promptp
+= ucs4_strlen(promptp
);
898 if((promptp
-prompt
) < NPMT
-1){
904 if((promptp
-prompt
) < NPMT
-2){
910 prompt
[NPMT
-1] = '\0';
912 s
= mlreplyd(prompt
, defpat
, defpatlen
, QDEFLT
, menu_pat
);
914 if (s
== TRUE
|| s
== FALSE
){ /* changed or not, they're done */
915 if(!defpat
[0]){ /* use default */
916 ucs4_strncpy(defpat
, pat
, defpatlen
);
917 defpat
[defpatlen
-1] = '\0';
919 else if(ucs4_strcmp(pat
, defpat
)){ /* Specified */
920 ucs4_strncpy(pat
, defpat
, NPAT
);
925 s
= TRUE
; /* let caller know to proceed */
933 * Read a pattern. Stash it in the external variable "pat". The "pat" is not
934 * updated if the user types in an empty line. If the user typed an empty line,
935 * and there is no old pattern, it is an error. Display the old pattern, in the
936 * style of Jeff Lomicka. There is some do-it-yourself control expansion.
937 * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
942 readnumpat(char *utf8prompt
)
946 EXTRAKEYS menu_pat
[12];
948 memset(&menu_pat
, 0, 10*sizeof(EXTRAKEYS
));
949 menu_pat
[i
= 0].name
= "^T";
950 menu_pat
[i
].label
= N_("No Line Number");
951 menu_pat
[i
].key
= (CTRL
|'T');
952 KS_OSDATASET(&menu_pat
[i
++], KS_NONE
);
954 menu_pat
[i
].name
= NULL
;
958 switch(mlreplyd_utf8(utf8prompt
, numpat
, NPMT
, QNORML
, menu_pat
)){
961 for(i
= n
= 0; numpat
[i
]; i
++)
962 if(strchr("0123456789", numpat
[i
])){
963 n
= (n
* 10) + (numpat
[i
] - '0');
986 readpattern(char *utf8prompt
, int text_mode
, int flags
)
990 int toggle
, bsearch
, bol
, eol
, exact
;
994 EXTRAKEYS menu_pat
[12];
996 bsearch
= flags
& SR_BACKWRD
;
997 bol
= flags
& SR_BEGLINE
;
998 eol
= flags
& SR_ENDLINE
;
999 exact
= flags
& SR_EXACTSR
;
1000 toggle
= 0; /* reserved for future use */
1002 memset(&menu_pat
, 0, sizeof(menu_pat
));
1003 /* add exceptions here based on the location of the items in the menu */
1004 for(i
= 0; i
< 10; i
++){
1005 if(flags
& SR_ORIGMEN
){
1006 menu_pat
[i
].name
= menu_srchpat
[10*toggle
+ i
].name
;
1007 menu_pat
[i
].label
= menu_srchpat
[10*toggle
+ i
].label
;
1008 menu_pat
[i
].key
= menu_srchpat
[10*toggle
+ i
].key
;
1010 if (i
== REPLACE_KEY
)
1011 memset(&menu_pat
[i
], 0, sizeof(EXTRAKEYS
));
1012 if (i
> REPLACE_KEY
&& !text_mode
)
1013 memset(&menu_pat
[i
], 0, sizeof(EXTRAKEYS
));
1015 } else if(flags
& SR_OPTNMEN
){
1016 menu_pat
[i
].name
= menu_srchopt
[i
].name
;
1017 menu_pat
[i
].label
= menu_srchopt
[i
].label
;
1018 menu_pat
[i
].key
= menu_srchopt
[i
].key
;
1021 menu_pat
[i
].label
= exact
? N_("No Exact")
1025 menu_pat
[i
].label
= bsearch
? N_("Srch Fwd")
1029 menu_pat
[i
].label
= bol
? N_("Anywhere")
1033 menu_pat
[i
].label
= eol
? N_("Anywhere")
1038 if(menu_pat
[i
].name
)
1039 KS_OSDATASET(&menu_pat
[i
], KS_NONE
);
1043 b
= utf8_to_ucs4_cpystr(utf8prompt
);
1045 ucs4_strncpy(tpat
, b
, NPAT
+20);
1046 tpat
[NPAT
+20-1] = '\0';
1048 fs_give((void **) &b
);
1049 b
= utf8_to_ucs4_cpystr(N_(" backward"));
1050 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1051 tpat
[NPAT
+20-1] = '\0';
1054 fs_give((void **) &b
);
1055 b
= utf8_to_ucs4_cpystr(N_(" at start of line"));
1056 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1057 tpat
[NPAT
+20-1] = '\0';
1059 fs_give((void **) &b
);
1060 b
= utf8_to_ucs4_cpystr(N_(" at end of line"));
1061 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1062 tpat
[NPAT
+20-1] = '\0';
1065 fs_give((void **) &b
);
1066 b
= utf8_to_ucs4_cpystr(N_(" exactly for"));
1067 if(b
) ucs4_strncat(tpat
, b
, ucs4_strlen(b
));
1068 tpat
[NPAT
+20-1] = '\0';
1070 if(b
) fs_give((void **) &b
);
1073 tpatp
= &tpat
[ucs4_strlen(tpat
)];
1076 if((tpatp
-tpat
) < NPAT
+20-2){
1082 expandp(pat
, tpatp
, NPAT
+20-(tpatp
-tpat
));
1083 tpat
[NPAT
+20-1] = '\0';
1084 tpatp
+= ucs4_strlen(tpatp
);
1086 if((tpatp
-tpat
) < NPAT
+20-1){
1092 if((tpatp
-tpat
) < NPAT
+20-3){
1099 tpat
[NPAT
+20-1] = '\0';
1101 s
= mlreplyd(tpat
, tpat
, NPAT
, QNORML
, menu_pat
);
1103 if ((s
== TRUE
) && ucs4_strcmp(pat
,tpat
)){ /* Specified */
1104 ucs4_strncpy(pat
, tpat
, NPAT
);
1108 else if (s
== FALSE
&& pat
[0] != '\0') /* CR, but old one */
1114 /* given a line, reverse its content */
1115 void reverse_line(LINE
*l
)
1120 if(l
== NULL
) return;
1122 for(i
= 0; i
< j
; i
++, j
--){
1123 u
= lgetc(l
, j
).c
; /* reverse the text */
1124 lgetc(l
, j
).c
= lgetc(l
, i
).c
;
1126 a
= lgetc(l
, j
).a
; /* and the attribute */
1127 lgetc(l
, j
).a
= lgetc(l
, i
).a
;
1133 reverse_all(UCS
*pat
, int bsearch
)
1142 reverse_buffer(void)
1146 for(l
= lforw(curbp
->b_linep
); l
!= curbp
->b_linep
; l
= lforw(l
))
1149 /* reverse links in buffer */
1152 lforw(l
) = lback(l
);
1154 } while(l
!= curbp
->b_linep
);
1158 lback(lforw(l
)) = l
;
1160 } while (l
!= curbp
->b_linep
);
1162 curwp
->w_doto
= llength(curwp
->w_dotp
) - 1 - curwp
->w_doto
;
1167 /* given a UCS4 string reverse its content */
1168 void reverse_ucs4(UCS
*s
)
1173 j
= ucs4_strlen(s
) - 1;
1174 for(i
= 0; i
< j
; i
++, j
--){
1182 /* search forward for a <patrn>.
1183 * A backward search is a forward search in backward lines with backward
1187 forscan(int *wrapt
, /* boolean indicating search wrapped */
1188 UCS
*patrn
, /* string to scan for */
1189 int flags
, /* direction and position */
1190 LINE
*limitp
, /* stop searching if reached */
1191 int limito
, /* stop searching if reached */
1192 int leavep
) /* place to leave point
1193 PTBEG = begining of match
1194 PTEND = at end of match */
1197 LINE
*curline
; /* current line during scan */
1198 int curoff
; /* position within current line */
1199 LINE
*lastline
; /* last line position during scan */
1200 int lastoff
; /* position within last line */
1201 UCS c
; /* character at current position */
1202 LINE
*matchline
; /* current line during matching */
1203 int matchoff
; /* position in matching line */
1204 UCS
*patptr
; /* pointer into pattern */
1205 int stopoff
; /* offset to stop search */
1206 LINE
*stopline
; /* line to stop search */
1207 int ftest
; /* position of first character of test */
1212 bsearch
= flags
& SR_BACKWRD
;
1213 bol
= flags
& SR_BEGLINE
;
1214 eol
= flags
& SR_ENDLINE
;
1217 /* if bsearch is set we return the match at the beginning of the
1218 * matching string, so we make this algorithm return the end of
1219 * the string, so that when we reverse we be at the beginning.
1222 leavep
= leavep
== PTBEG
? PTEND
: PTBEG
;
1225 * the idea is to set the character to end the search at the
1226 * next character in the buffer. thus, let the search wrap
1227 * completely around the buffer.
1229 * first, test to see if we are at the end of the line,
1230 * otherwise start searching on the next character.
1232 if(curwp
->w_doto
== llength(curwp
->w_dotp
)){
1234 * dot is not on end of a line
1235 * start at 0 offset of the next line
1237 stopoff
= curoff
= 0;
1238 stopline
= curline
= lforw(curwp
->w_dotp
);
1239 if (curwp
->w_dotp
== curbp
->b_linep
)
1243 stopoff
= curoff
= curwp
->w_doto
;
1244 stopline
= curline
= curwp
->w_dotp
;
1247 /* scan each character until we hit the head link record */
1250 * maybe wrapping is a good idea
1254 if(curline
== curbp
->b_linep
)
1257 /* save the current position in case we need to
1258 restore it on a match */
1263 /* get the current character resolving EOLs */
1264 if (curoff
== llength(curline
)) { /* if at EOL */
1265 curline
= lforw(curline
); /* skip to next line */
1267 c
= '\n'; /* and return a <NL> */
1269 else if(curoff
== -1){
1270 stopoff
= curoff
= 0;
1275 c
= lgetc(curline
, curoff
++).c
; /* get the char */
1278 ftest
= bsearch
== 0 ? 1 : llength(curline
) - ucs4_strlen(patrn
) + 1;
1280 ftest
= bsearch
== 0 ? llength(curline
) - ucs4_strlen(patrn
) + 1 : 1;
1281 /* test it against first char in pattern */
1282 if (eq(c
, patrn
[0]) != FALSE
/* if we find it..*/
1283 && ((bol
== 0 && eol
== 0) /* ...and if it is anywhere */
1284 || (bol
!= 0 /* ...or is at the begin or line */
1285 && ((bsearch
== 0 && curoff
== ftest
) /* and search forward and found at beginning of line */
1286 || (bsearch
!= 0 && curoff
== ftest
))) /* or search backward and found at end of line */
1287 || (eol
!= 0 /* ...or is at the end or line */
1288 && ((bsearch
== 0 && curoff
== ftest
) /* and search forward and found at beginning of line */
1289 || (bsearch
!= 0 && curoff
== ftest
))) /* or search backward and found at end of line */
1291 /* setup match pointers */
1292 matchline
= curline
;
1296 /* scan through patrn for a match */
1297 while (*++patptr
!= '\0') {
1298 /* advance all the pointers */
1299 if (matchoff
>= llength(matchline
)) {
1300 /* advance past EOL */
1301 matchline
= lforw(matchline
);
1305 c
= lgetc(matchline
, matchoff
++).c
;
1307 if(matchline
== limitp
&& matchoff
== limito
)
1310 /* and test it against the pattern */
1311 if (eq(*patptr
, c
) == FALSE
)
1315 /* A SUCCESSFULL MATCH!!! */
1316 /* reset the global "." pointers */
1317 if (leavep
== PTEND
) { /* at end of string */
1318 curwp
->w_dotp
= matchline
;
1319 curwp
->w_doto
= matchoff
- 1;
1321 else { /* at begining of string */
1322 curwp
->w_dotp
= lastline
;
1323 curwp
->w_doto
= lastoff
;
1326 curwp
->w_flag
|= WFMOVE
; /* flag that we have moved */
1330 fail
:; /* continue to search */
1331 if(((curline
== stopline
) && (curoff
== stopoff
))
1332 || (curline
== limitp
&& curoff
== limito
))
1333 break; /* searched everywhere... */
1335 /* we could not find a match */
1342 /* expandp: expand control key sequences for output */
1344 expandp(UCS
*srcstr
, /* string to expand */
1345 UCS
*deststr
, /* destination of expanded string */
1346 int maxlength
) /* maximum chars in destination */
1348 UCS c
; /* current char to translate */
1350 /* scan through the string */
1351 while ((c
= *srcstr
++) != 0) {
1352 if (c
== '\n') { /* its an EOL */
1358 } else if (c
< 0x20 || c
== 0x7f) { /* control character */
1360 *deststr
++ = c
^ 0x40;
1362 } else if (c
== '%') {
1366 } else { /* any other character */
1371 /* check for maxlength */
1372 if (maxlength
< 4) {
1385 * chword() - change the given word, wp, pointed to by the curwp->w_dot
1386 * pointers to the word in cb
1387 * if bsearch is set, then cb is supposed to come unreversed, while
1388 * the buffer is supposed to be reversed, so we must reverse cb before
1392 chword(UCS
*wb
, UCS
*cb
, int bsearch
)
1395 ldelete(ucs4_strlen(wb
), NULL
); /* not saved in kill buffer */
1396 if(bsearch
) reverse_ucs4(cb
);
1397 for(u
= cb
; *u
!= '\0'; u
++)
1399 if(bsearch
) reverse_ucs4(cb
);
1401 curwp
->w_flag
|= WFEDIT
;
1405 supdate(UCS
*pat
, int bsearch
)
1407 reverse_all(pat
, bsearch
); /* undo reverse buffer and pattern */
1409 reverse_all(pat
, bsearch
); /* reverse buffer and pattern */
1413 sucs4_to_utf8_cpystr(UCS
*orig
, int bsearch
)
1416 if(bsearch
) reverse_ucs4(orig
);
1417 utf8
= ucs4_to_utf8_cpystr(orig
);
1418 if(bsearch
) reverse_ucs4(orig
);