summaryrefslogtreecommitdiff
path: root/pico/search.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pico/search.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pico/search.c')
-rw-r--r--pico/search.c1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/pico/search.c b/pico/search.c
new file mode 100644
index 00000000..0886b24c
--- /dev/null
+++ b/pico/search.c
@@ -0,0 +1,1037 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: search.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Searching routines
+ *
+ * The functions in this file implement commands that search in the forward
+ * and backward directions. There are no special characters in the search
+ * strings. Probably should have a regular expression search, or something
+ * like that.
+ *
+ */
+
+#include "headers.h"
+
+int eq(UCS, UCS);
+int expandp(UCS *, UCS *, int);
+int readnumpat(char *);
+void get_pat_cases(UCS *, UCS *);
+int srpat(char *, UCS *, size_t, int);
+int readpattern(char *, int);
+int replace_pat(UCS *, int *);
+int replace_all(UCS *, UCS *);
+
+
+#define FWS_RETURN(RV) { \
+ thisflag |= CFSRCH; \
+ curwp->w_flag |= WFMODE; \
+ sgarbk = TRUE; \
+ return(RV); \
+ }
+
+
+/*
+ * Search forward. Get a search string from the user, and search, beginning at
+ * ".", for the string. If found, reset the "." to be just after the match
+ * string, and [perhaps] repaint the display. Bound to "C-S".
+ */
+
+/* string search input parameters */
+
+#define PTBEG 1 /* leave the point at the begining on search */
+#define PTEND 2 /* leave the point at the end on search */
+
+#define NPMT (2*NLINE+32)
+
+
+static char *SearchHelpText[] = {
+/* TRANSLATORS: Some help text that goes together in a group. */
+N_("Help for Search Command"),
+" ",
+N_(" Enter the words or characters you would like to search"),
+N_("~ for, then press ~R~e~t~u~r~n. The search then takes place."),
+N_(" When the characters or words that you entered "),
+N_(" are found, the buffer will be redisplayed with the cursor "),
+N_(" at the beginning of the selected text."),
+" ",
+N_(" The most recent string for which a search was made is"),
+N_(" displayed in the \"Search\" prompt between the square"),
+N_(" brackets. This string is the default search prompt."),
+N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"),
+N_(" search to be made with the default value."),
+" ",
+N_(" The text search is not case sensitive, and will examine the"),
+N_(" entire message."),
+" ",
+N_(" Should the search fail, a message will be displayed."),
+" ",
+N_("End of Search Help."),
+" ",
+NULL
+};
+
+
+/*
+ * Compare two characters. The "bc" comes from the buffer. It has it's case
+ * folded out. The "pc" is from the pattern.
+ */
+int
+eq(UCS bc, UCS pc)
+{
+ if ((curwp->w_bufp->b_mode & MDEXACT) == 0){
+ if (bc>='a' && bc<='z')
+ bc -= 0x20;
+
+ if (pc>='a' && pc<='z')
+ pc -= 0x20;
+ }
+
+ return(bc == pc);
+}
+
+
+int
+forwsearch(int f, int n)
+{
+ int status;
+ int wrapt = FALSE, wrapt2 = FALSE;
+ int repl_mode = FALSE;
+ UCS defpat[NPAT];
+ int search = FALSE;
+ EML eml;
+
+ /* resolve the repeat count */
+ if (n == 0)
+ n = 1;
+
+ if (n < 1) /* search backwards */
+ FWS_RETURN(0);
+
+ defpat[0] = '\0';
+
+ /* ask the user for the text of a pattern */
+ while(1){
+
+ if (gmode & MDREPLACE)
+ status = srpat("Search", defpat, NPAT, repl_mode);
+ else
+ status = readpattern("Search", TRUE);
+
+ switch(status){
+ case TRUE: /* user typed something */
+ search = TRUE;
+ break;
+
+ case HELPCH: /* help requested */
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->search_help,
+ _("Help for Searching"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(SearchHelpText, _("Help for Searching"), 1);
+
+ case (CTRL|'L'): /* redraw requested */
+ pico_refresh(FALSE, 1);
+ update();
+ break;
+
+ case (CTRL|'V'):
+ gotoeob(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'Y'):
+ gotobob(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'T') :
+ switch(status = readnumpat(_("Search to Line Number : "))){
+ case -1 :
+ emlwrite(_("Search to Line Number Cancelled"), NULL);
+ FWS_RETURN(FALSE);
+
+ case 0 :
+ emlwrite(_("Line number must be greater than zero"), NULL);
+ FWS_RETURN(FALSE);
+
+ case -2 :
+ emlwrite(_("Line number must contain only digits"), NULL);
+ FWS_RETURN(FALSE);
+
+ case -3 :
+ continue;
+
+ default :
+ gotoline(0, status);
+ mlerase();
+ FWS_RETURN(TRUE);
+ }
+
+ break;
+
+ case (CTRL|'W'):
+ {
+ LINE *linep = curwp->w_dotp;
+ int offset = curwp->w_doto;
+
+ gotobop(0, 1);
+ gotobol(0, 1);
+
+ /*
+ * if we're asked to backup and we're already
+ *
+ */
+ if((lastflag & CFSRCH)
+ && linep == curwp->w_dotp
+ && offset == curwp->w_doto
+ && !(offset == 0 && lback(linep) == curbp->b_linep)){
+ backchar(0, 1);
+ gotobop(0, 1);
+ gotobol(0, 1);
+ }
+ }
+
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'O'):
+ if(curwp->w_dotp != curbp->b_linep){
+ gotoeop(0, 1);
+ forwchar(0, 1);
+ }
+
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'U'):
+ fillbuf(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'R'): /* toggle replacement option */
+ repl_mode = !repl_mode;
+ break;
+
+ default:
+ if(status == ABORT)
+ emlwrite(_("Search Cancelled"), NULL);
+ else
+ mlerase();
+
+ FWS_RETURN(FALSE);
+ }
+
+ /* replace option is disabled */
+ if (!(gmode & MDREPLACE)){
+ ucs4_strncpy(defpat, pat, NPAT);
+ defpat[NPAT-1] = '\0';
+ break;
+ }
+ else if (search){ /* search now */
+ ucs4_strncpy(pat, defpat, NPAT); /* remember this search for the future */
+ pat[NPAT-1] = '\0';
+ break;
+ }
+ }
+
+ /*
+ * This code is kind of dumb. What I want is successive C-W 's to
+ * move dot to successive occurences of the pattern. So, if dot is
+ * already sitting at the beginning of the pattern, then we'll move
+ * forward a char before beginning the search. We'll let the
+ * automatic wrapping handle putting the dot back in the right
+ * place...
+ */
+ status = 0; /* using "status" as int temporarily! */
+ while(1){
+ if(defpat[status] == '\0'){
+ forwchar(0, 1);
+ break; /* find next occurence! */
+ }
+
+ if(status + curwp->w_doto >= llength(curwp->w_dotp) ||
+ !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c))
+ break; /* do nothing! */
+ status++;
+ }
+
+ /* search for the pattern */
+
+ while (n-- > 0) {
+ if((status = forscan(&wrapt,defpat,NULL,0,PTBEG)) == FALSE)
+ break;
+ }
+
+ /* and complain if not there */
+ if (status == FALSE){
+ char *utf8;
+ UCS x[1];
+
+ x[0] = '\0';
+
+ utf8 = ucs4_to_utf8_cpystr(defpat ? defpat : x);
+ /* TRANSLATORS: reporting the result of a failed search */
+ eml.s = utf8;
+ emlwrite(_("\"%s\" not found"), &eml);
+ if(utf8)
+ fs_give((void **) &utf8);
+ }
+ else if((gmode & MDREPLACE) && repl_mode == TRUE){
+ status = replace_pat(defpat, &wrapt2); /* replace pattern */
+ if (wrapt == TRUE || wrapt2 == TRUE){
+ eml.s = (status == ABORT) ? "cancelled but wrapped" : "Wrapped";
+ emlwrite("Replacement %s", &eml);
+ }
+ }
+ else if(wrapt == TRUE){
+ emlwrite("Search Wrapped", NULL);
+ }
+ else if(status == TRUE){
+ emlwrite("", NULL);
+ }
+
+ FWS_RETURN(status);
+}
+
+
+/* Replace a pattern with the pattern the user types in one or more times. */
+int
+replace_pat(UCS *defpat, int *wrapt)
+{
+ register int status;
+ UCS lpat[NPAT], origpat[NPAT]; /* case sensitive pattern */
+ EXTRAKEYS menu_pat[2];
+ int repl_all = FALSE;
+ UCS *b;
+ char utf8tmp[NPMT];
+ UCS prompt[NPMT];
+ UCS *promptp;
+
+ forscan(wrapt, defpat, NULL, 0, PTBEG); /* go to word to be replaced */
+
+ lpat[0] = '\0';
+
+ /* additional 'replace all' menu option */
+ menu_pat[0].name = "^X";
+ menu_pat[0].key = (CTRL|'X');
+ menu_pat[0].label = N_("Repl All");
+ KS_OSDATASET(&menu_pat[0], KS_NONE);
+ menu_pat[1].name = NULL;
+
+ while(1) {
+
+ update();
+ (*term.t_rev)(1);
+ get_pat_cases(origpat, defpat);
+ pputs(origpat, 1); /* highlight word */
+ (*term.t_rev)(0);
+
+ snprintf(utf8tmp, NPMT, "Replace%s \"", repl_all ? " every" : "");
+ b = utf8_to_ucs4_cpystr(utf8tmp);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ expandp(defpat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ b = utf8_to_ucs4_cpystr("\" with");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ if(rpat[0] != '\0'){
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ' ';
+ *promptp++ = '[';
+ *promptp = '\0';
+ }
+
+ expandp(rpat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = ']';
+ *promptp = '\0';
+ }
+ }
+
+ if((promptp-prompt) < NPMT-3){
+ *promptp++ = ' ';
+ *promptp++ = ':';
+ *promptp++ = ' ';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ status = mlreplyd(prompt, lpat, NPAT, QDEFLT, menu_pat);
+
+ curwp->w_flag |= WFMOVE;
+
+ switch(status){
+
+ case TRUE :
+ case FALSE :
+ if(lpat[0]){
+ ucs4_strncpy(rpat, lpat, NPAT); /* remember default */
+ rpat[NPAT-1] = '\0';
+ }
+ else{
+ ucs4_strncpy(lpat, rpat, NPAT); /* use default */
+ lpat[NPAT-1] = '\0';
+ }
+
+ if (repl_all){
+ status = replace_all(defpat, lpat);
+ }
+ else{
+ chword(defpat, lpat); /* replace word */
+ update();
+ status = TRUE;
+ }
+
+ if(status == TRUE)
+ emlwrite("", NULL);
+
+ return(status);
+
+ case HELPCH: /* help requested */
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->search_help,
+ _("Help for Searching"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(SearchHelpText, _("Help for Searching"), 1);
+
+ case (CTRL|'L'): /* redraw requested */
+ pico_refresh(FALSE, 1);
+ update();
+ break;
+
+ case (CTRL|'X'): /* toggle replace all option */
+ if (repl_all){
+ repl_all = FALSE;
+ /* TRANSLATORS: abbreviation for Replace All occurences */
+ menu_pat[0].label = N_("Repl All");
+ }
+ else{
+ repl_all = TRUE;
+ /* TRANSLATORS: Replace just one occurence */
+ menu_pat[0].label = N_("Repl One");
+ }
+
+ break;
+
+ default:
+ if(status == ABORT){
+ emlwrite(_("Replacement Cancelled"), NULL);
+ pico_refresh(FALSE, 1);
+ }
+ else{
+ mlerase();
+ chword(defpat, origpat);
+ }
+
+ update();
+ return(FALSE);
+ }
+ }
+}
+
+
+/* Since the search is not case sensitive, we must obtain the actual pattern
+ that appears in the text, so that we can highlight (and unhighlight) it
+ without using the wrong cases */
+void
+get_pat_cases(UCS *realpat, UCS *searchpat)
+{
+ int i, searchpatlen, curoff;
+
+ curoff = curwp->w_doto;
+ searchpatlen = ucs4_strlen(searchpat);
+
+ for (i = 0; i < searchpatlen; i++)
+ realpat[i] = lgetc(curwp->w_dotp, curoff++).c;
+
+ realpat[searchpatlen] = '\0';
+}
+
+
+/* Ask the user about every occurence of orig pattern and replace it with a
+ repl pattern if the response is affirmative. */
+int
+replace_all(UCS *orig, UCS *repl)
+{
+ register int status = 0;
+ UCS *b;
+ UCS realpat[NPAT];
+ char utf8tmp[NPMT];
+ UCS *promptp;
+ UCS prompt[NPMT];
+ int wrapt, n = 0;
+ LINE *stop_line = curwp->w_dotp;
+ int stop_offset = curwp->w_doto;
+ EML eml;
+
+ while (1)
+ if (forscan(&wrapt, orig, stop_line, stop_offset, PTBEG)){
+ curwp->w_flag |= WFMOVE; /* put cursor back */
+
+ update();
+ (*term.t_rev)(1);
+ get_pat_cases(realpat, orig);
+ pputs(realpat, 1); /* highlight word */
+ (*term.t_rev)(0);
+ fflush(stdout);
+
+ snprintf(utf8tmp, NPMT, "Replace \"");
+ b = utf8_to_ucs4_cpystr(utf8tmp);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ expandp(orig, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ b = utf8_to_ucs4_cpystr("\" with \"");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ expandp(repl, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = '\"';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ status = mlyesno(prompt, TRUE); /* ask user */
+
+ if (status == TRUE){
+ n++;
+ chword(realpat, repl); /* replace word */
+ update();
+ }else{
+ chword(realpat, realpat); /* replace word by itself */
+ update();
+ if(status == ABORT){ /* if cancelled return */
+ eml.s = comatose(n);
+ emlwrite("Replace All cancelled after %s changes", &eml);
+ return (ABORT); /* ... else keep looking */
+ }
+ }
+ }
+ else{
+ char *utf8;
+
+ utf8 = ucs4_to_utf8_cpystr(orig);
+ if(utf8){
+ eml.s = utf8;
+ emlwrite(_("No more matches for \"%s\""), &eml);
+ fs_give((void **) &utf8);
+ }
+ else
+ emlwrite(_("No more matches"), NULL);
+
+ return (FALSE);
+ }
+}
+
+
+/* Read a replacement pattern. Modeled after readpattern(). */
+int
+srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode)
+{
+ register int s;
+ int i = 0;
+ UCS *b;
+ UCS prompt[NPMT];
+ UCS *promptp;
+ EXTRAKEYS menu_pat[8];
+
+ menu_pat[i = 0].name = "^Y";
+ menu_pat[i].label = N_("FirstLine");
+ menu_pat[i].key = (CTRL|'Y');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^V";
+ menu_pat[i].label = N_("LastLine");
+ menu_pat[i].key = (CTRL|'V');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^R";
+ menu_pat[i].label = repl_mode ? N_("No Replace") : N_("Replace");
+ menu_pat[i].key = (CTRL|'R');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ if(!repl_mode){
+ menu_pat[++i].name = "^T";
+ menu_pat[i].label = N_("LineNumber");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^W";
+ /* TRANSLATORS: Start of paragraph */
+ menu_pat[i].label = N_("Start of Para");
+ menu_pat[i].key = (CTRL|'W');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^O";
+ menu_pat[i].label = N_("End of Para");
+ menu_pat[i].key = (CTRL|'O');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^U";
+ /* TRANSLATORS: Instead of justifying (formatting) just a
+ single paragraph, Full Justify justifies the entire
+ message. */
+ menu_pat[i].label = N_("FullJustify");
+ menu_pat[i].key = (CTRL|'U');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+ }
+
+ menu_pat[++i].name = NULL;
+
+ b = utf8_to_ucs4_cpystr(utf8prompt);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ if(repl_mode){
+ b = utf8_to_ucs4_cpystr(" (to replace)");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+ }
+
+ if(pat[0] != '\0'){
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ' ';
+ *promptp++ = '[';
+ *promptp = '\0';
+ }
+
+ expandp(pat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = ']';
+ *promptp = '\0';
+ }
+ }
+
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ':';
+ *promptp++ = ' ';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ s = mlreplyd(prompt, defpat, defpatlen, QDEFLT, menu_pat);
+
+ if (s == TRUE || s == FALSE){ /* changed or not, they're done */
+ if(!defpat[0]){ /* use default */
+ ucs4_strncpy(defpat, pat, defpatlen);
+ defpat[defpatlen-1] = '\0';
+ }
+ else if(ucs4_strcmp(pat, defpat)){ /* Specified */
+ ucs4_strncpy(pat, defpat, NPAT);
+ pat[NPAT-1] = '\0';
+ rpat[0] = '\0';
+ }
+
+ s = TRUE; /* let caller know to proceed */
+ }
+
+ return(s);
+}
+
+
+/*
+ * Read a pattern. Stash it in the external variable "pat". The "pat" is not
+ * updated if the user types in an empty line. If the user typed an empty line,
+ * and there is no old pattern, it is an error. Display the old pattern, in the
+ * style of Jeff Lomicka. There is some do-it-yourself control expansion.
+ * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
+ * the search string.
+ */
+
+int
+readnumpat(char *utf8prompt)
+{
+ int i, n;
+ char numpat[NPMT];
+ EXTRAKEYS menu_pat[2];
+
+ menu_pat[i = 0].name = "^T";
+ menu_pat[i].label = N_("No Line Number");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i++], KS_NONE);
+
+ menu_pat[i].name = NULL;
+
+ numpat[0] = '\0';
+ while(1)
+ switch(mlreplyd_utf8(utf8prompt, numpat, NPMT, QNORML, menu_pat)){
+ case TRUE :
+ if(*numpat){
+ for(i = n = 0; numpat[i]; i++)
+ if(strchr("0123456789", numpat[i])){
+ n = (n * 10) + (numpat[i] - '0');
+ }
+ else
+ return(-2);
+
+ return(n);
+ }
+
+ case FALSE :
+ default :
+ return(-1);
+
+ case (CTRL|'T') :
+ return(-3);
+
+ case (CTRL|'L') :
+ case HELPCH :
+ break;
+ }
+}
+
+
+int
+readpattern(char *utf8prompt, int text_mode)
+{
+ register int s;
+ int i;
+ UCS *b;
+ UCS tpat[NPAT+20];
+ UCS *tpatp;
+ EXTRAKEYS menu_pat[7];
+
+ menu_pat[i = 0].name = "^Y";
+ menu_pat[i].label = N_("FirstLine");
+ menu_pat[i].key = (CTRL|'Y');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^V";
+ menu_pat[i].label = N_("LastLine");
+ menu_pat[i].key = (CTRL|'V');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ if(text_mode){
+ menu_pat[++i].name = "^T";
+ menu_pat[i].label = N_("LineNumber");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^W";
+ menu_pat[i].label = N_("Start of Para");
+ menu_pat[i].key = (CTRL|'W');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^O";
+ menu_pat[i].label = N_("End of Para");
+ menu_pat[i].key = (CTRL|'O');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^U";
+ menu_pat[i].label = N_("FullJustify");
+ menu_pat[i].key = (CTRL|'U');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+ }
+
+ menu_pat[++i].name = NULL;
+
+ b = utf8_to_ucs4_cpystr(utf8prompt);
+ if(b){
+ ucs4_strncpy(tpat, b, NPAT+20);
+ tpat[NPAT+20-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ tpatp = &tpat[ucs4_strlen(tpat)];
+
+ if(pat[0] != '\0'){
+ if((tpatp-tpat) < NPAT+20-2){
+ *tpatp++ = ' ';
+ *tpatp++ = '[';
+ *tpatp = '\0';
+ }
+
+ expandp(pat, tpatp, NPAT+20-(tpatp-tpat));
+ tpat[NPAT+20-1] = '\0';
+ tpatp += ucs4_strlen(tpatp);
+
+ if((tpatp-tpat) < NPAT+20-1){
+ *tpatp++ = ']';
+ *tpatp = '\0';
+ }
+ }
+
+ if((tpatp-tpat) < NPAT+20-3){
+ *tpatp++ = ' ';
+ *tpatp++ = ':';
+ *tpatp++ = ' ';
+ *tpatp = '\0';
+ }
+
+ tpat[NPAT+20-1] = '\0';
+
+ s = mlreplyd(tpat, tpat, NPAT, QNORML, menu_pat);
+
+ if ((s == TRUE) && ucs4_strcmp(pat,tpat)){ /* Specified */
+ ucs4_strncpy(pat, tpat, NPAT);
+ pat[NPAT-1] = '\0';
+ rpat[0] = '\0';
+ }
+ else if (s == FALSE && pat[0] != '\0') /* CR, but old one */
+ s = TRUE;
+
+ return(s);
+}
+
+
+/* search forward for a <patrn> */
+int
+forscan(int *wrapt, /* boolean indicating search wrapped */
+ UCS *patrn, /* string to scan for */
+ LINE *limitp, /* stop searching if reached */
+ int limito, /* stop searching if reached */
+ int leavep) /* place to leave point
+ PTBEG = begining of match
+ PTEND = at end of match */
+
+{
+ LINE *curline; /* current line during scan */
+ int curoff; /* position within current line */
+ LINE *lastline; /* last line position during scan */
+ int lastoff; /* position within last line */
+ UCS c; /* character at current position */
+ LINE *matchline; /* current line during matching */
+ int matchoff; /* position in matching line */
+ UCS *patptr; /* pointer into pattern */
+ int stopoff; /* offset to stop search */
+ LINE *stopline; /* line to stop search */
+
+ *wrapt = FALSE;
+
+ /*
+ * the idea is to set the character to end the search at the
+ * next character in the buffer. thus, let the search wrap
+ * completely around the buffer.
+ *
+ * first, test to see if we are at the end of the line,
+ * otherwise start searching on the next character.
+ */
+ if(curwp->w_doto == llength(curwp->w_dotp)){
+ /*
+ * dot is not on end of a line
+ * start at 0 offset of the next line
+ */
+ stopoff = curoff = 0;
+ stopline = curline = lforw(curwp->w_dotp);
+ if (curwp->w_dotp == curbp->b_linep)
+ *wrapt = TRUE;
+ }
+ else{
+ stopoff = curoff = curwp->w_doto;
+ stopline = curline = curwp->w_dotp;
+ }
+
+ /* scan each character until we hit the head link record */
+
+ /*
+ * maybe wrapping is a good idea
+ */
+ while (curline){
+
+ if (curline == curbp->b_linep)
+ *wrapt = TRUE;
+
+ /* save the current position in case we need to
+ restore it on a match */
+
+ lastline = curline;
+ lastoff = curoff;
+
+ /* get the current character resolving EOLs */
+ if (curoff == llength(curline)) { /* if at EOL */
+ curline = lforw(curline); /* skip to next line */
+ curoff = 0;
+ c = '\n'; /* and return a <NL> */
+ }
+ else
+ c = lgetc(curline, curoff++).c; /* get the char */
+
+ /* test it against first char in pattern */
+ if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/
+ /* setup match pointers */
+ matchline = curline;
+ matchoff = curoff;
+ patptr = &patrn[0];
+
+ /* scan through patrn for a match */
+ while (*++patptr != '\0') {
+ /* advance all the pointers */
+ if (matchoff == llength(matchline)) {
+ /* advance past EOL */
+ matchline = lforw(matchline);
+ matchoff = 0;
+ c = '\n';
+ } else
+ c = lgetc(matchline, matchoff++).c;
+
+ if(matchline == limitp && matchoff == limito)
+ return(FALSE);
+
+ /* and test it against the pattern */
+ if (eq(*patptr, c) == FALSE)
+ goto fail;
+ }
+
+ /* A SUCCESSFULL MATCH!!! */
+ /* reset the global "." pointers */
+ if (leavep == PTEND) { /* at end of string */
+ curwp->w_dotp = matchline;
+ curwp->w_doto = matchoff;
+ }
+ else { /* at begining of string */
+ curwp->w_dotp = lastline;
+ curwp->w_doto = lastoff;
+ }
+
+ curwp->w_flag |= WFMOVE; /* flag that we have moved */
+ return(TRUE);
+
+ }
+
+fail:; /* continue to search */
+ if(((curline == stopline) && (curoff == stopoff))
+ || (curline == limitp && curoff == limito))
+ break; /* searched everywhere... */
+ }
+ /* we could not find a match */
+
+ return(FALSE);
+}
+
+
+
+/* expandp: expand control key sequences for output */
+int
+expandp(UCS *srcstr, /* string to expand */
+ UCS *deststr, /* destination of expanded string */
+ int maxlength) /* maximum chars in destination */
+{
+ UCS c; /* current char to translate */
+
+ /* scan through the string */
+ while ((c = *srcstr++) != 0) {
+ if (c == '\n') { /* its an EOL */
+ *deststr++ = '<';
+ *deststr++ = 'N';
+ *deststr++ = 'L';
+ *deststr++ = '>';
+ maxlength -= 4;
+ } else if (c < 0x20 || c == 0x7f) { /* control character */
+ *deststr++ = '^';
+ *deststr++ = c ^ 0x40;
+ maxlength -= 2;
+ } else if (c == '%') {
+ *deststr++ = '%';
+ *deststr++ = '%';
+ maxlength -= 2;
+ } else { /* any other character */
+ *deststr++ = c;
+ maxlength--;
+ }
+
+ /* check for maxlength */
+ if (maxlength < 4) {
+ *deststr++ = '$';
+ *deststr = '\0';
+ return(FALSE);
+ }
+ }
+
+ *deststr = '\0';
+ return(TRUE);
+}
+
+
+/*
+ * chword() - change the given word, wp, pointed to by the curwp->w_dot
+ * pointers to the word in cb
+ */
+void
+chword(UCS *wb, UCS *cb)
+{
+ ldelete((long) ucs4_strlen(wb), NULL); /* not saved in kill buffer */
+ while(*cb != '\0')
+ linsert(1, *cb++);
+
+ curwp->w_flag |= WFEDIT;
+}