* clear out some warnings by gcc 9.3.1.
[alpine.git] / pico / search.c
blob282e37e94d62fa7ddebc67a5e8fbe274bbffdd94
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: search.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 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
25 * like that.
29 #include "headers.h"
31 int eq(UCS, UCS);
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) { \
47 thisflag |= CFSRCH; \
48 curwp->w_flag |= WFMODE; \
49 sgarbk = TRUE; \
50 return(RV); \
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')},
74 {NULL, NULL, 0},
75 {NULL, NULL, 0}
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 */
89 {NULL, NULL, 0},
90 {NULL, NULL, 0},
91 {NULL, NULL, 0},
92 {NULL, NULL, 0}
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 beginning 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"),
112 " ",
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."),
118 " ",
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."),
124 " ",
125 N_(" The text search is not case sensitive, and will examine the"),
126 N_(" entire message."),
127 " ",
128 N_(" Should the search fail, a message will be displayed."),
129 " ",
130 N_("End of Search Help."),
131 " ",
132 NULL
137 * Compare two characters. The "bc" comes from the buffer. It has it's case
138 * folded out. The "pc" is from the pattern.
141 eq(UCS bc, UCS pc)
143 if ((curwp->w_bufp->b_mode & MDEXACT) == 0){
144 if (bc>='a' && bc<='z')
145 bc -= 0x20;
147 if (pc>='a' && pc<='z')
148 pc -= 0x20;
151 return(bc == pc);
156 forwsearch(int f, int n)
158 int status, flags;
159 int wrapt = FALSE, wrapt2 = FALSE;
160 int repl_mode = FALSE;
161 UCS defpat[NPAT];
162 int search = FALSE;
163 EML eml;
166 /* resolve the repeat count */
167 if (n == 0)
168 n = 1;
170 if (n < 1) /* search backwards */
171 FWS_RETURN(0);
173 defpat[0] = '\0';
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)
183 flags |= SR_NOEXACT;
184 else
185 flags |= SR_EXACTSR;
187 /* ask the user for the text of a pattern */
188 while(1){
190 if (gmode & MDREPLACE)
191 status = srpat("Search", defpat, NPAT, repl_mode, flags);
192 else
193 status = readpattern("Search", TRUE, flags);
195 switch(status){
196 case TRUE: /* user typed something */
197 search = TRUE;
198 break;
200 case HELPCH: /* help requested */
201 if(Pmaster){
202 VARS_TO_SAVE *saved_state;
204 saved_state = save_pico_state();
205 (*Pmaster->helper)(Pmaster->search_help,
206 _("Help for Searching"), 1);
207 if(saved_state){
208 restore_pico_state(saved_state);
209 free_pico_state(saved_state);
212 else
213 pico_help(SearchHelpText, _("Help for Searching"), 1);
215 case (CTRL|'L'): /* redraw requested */
216 pico_refresh(FALSE, 1);
217 update();
218 break;
221 case (CTRL|'P'):
222 if(flags & SR_ORIGMEN){
223 /* Undefined still */
225 if(flags & SR_OPTNMEN){
226 if(flags & SR_FORWARD){
227 flags &= ~SR_FORWARD;
228 flags |= SR_BACKWRD;
229 } else {
230 flags &= ~SR_BACKWRD;
231 flags |= SR_FORWARD;
234 break;
236 case (CTRL|'V'):
237 if(flags & SR_ORIGMEN){
238 gotoeob(0, 1);
239 mlerase();
240 FWS_RETURN(TRUE);
241 } else if (flags & SR_OPTNMEN){
242 if(flags & SR_ENDLINE)
243 flags &= ~SR_ENDLINE;
244 if(flags & SR_BEGLINE)
245 flags &= ~SR_BEGLINE;
246 else
247 flags |= SR_BEGLINE;
249 break;
251 case (CTRL|'N'):
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;
259 else
260 flags |= SR_ENDLINE;
262 break;
264 case (CTRL|'Y'):
265 if(flags & SR_ORIGMEN){
266 gotobob(0, 1);
267 mlerase();
268 FWS_RETURN(TRUE);
271 case (CTRL|'^'):
272 if (flags & SR_ORIGMEN){
273 flags &= ~SR_ORIGMEN;
274 flags |= SR_OPTNMEN;
275 } else {
276 flags &= ~SR_OPTNMEN;
277 flags |= SR_ORIGMEN;
279 break;
281 case (CTRL|'X'):
282 if(flags & SR_OPTNMEN){
283 if (flags & SR_NOEXACT){
284 flags &= ~SR_NOEXACT;
285 flags |= SR_EXACTSR;
286 } else {
287 flags &= ~SR_EXACTSR;
288 flags |= SR_NOEXACT;
290 if((curwp->w_bufp->b_mode & MDEXACT) == 0)
291 curwp->w_bufp->b_mode |= MDEXACT;
292 else
293 curwp->w_bufp->b_mode &= ~MDEXACT;
295 break;
297 case (CTRL|'T') :
298 if(flags & SR_ORIGMEN){
299 switch(status = readnumpat(_("Search to Line Number : "))){
300 case -1 :
301 emlwrite(_("Search to Line Number Cancelled"), NULL);
302 FWS_RETURN(FALSE);
304 case 0 :
305 emlwrite(_("Line number must be greater than zero"), NULL);
306 FWS_RETURN(FALSE);
308 case -2 :
309 emlwrite(_("Line number must contain only digits"), NULL);
310 FWS_RETURN(FALSE);
312 case -3 :
313 continue;
315 default :
316 gotoline(0, status);
317 mlerase();
318 FWS_RETURN(TRUE);
321 break;
323 case (CTRL|'W'):
324 if(flags & SR_ORIGMEN){
325 LINE *linep = curwp->w_dotp;
326 int offset = curwp->w_doto;
328 gotobop(0, 1);
329 gotobol(0, 1);
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)){
339 backchar(0, 1);
340 gotobop(0, 1);
341 gotobol(0, 1);
343 mlerase();
344 FWS_RETURN(TRUE);
346 break;
348 case (CTRL|'O'):
349 if(flags & SR_ORIGMEN){
350 if(curwp->w_dotp != curbp->b_linep){
351 gotoeop(0, 1);
352 forwchar(0, 1);
354 mlerase();
355 FWS_RETURN(TRUE);
357 break;
359 case (CTRL|'U'):
360 if(flags & SR_ORIGMEN){
361 fillbuf(0, 1);
362 mlerase();
363 FWS_RETURN(TRUE);
365 break;
367 case (CTRL|'R'): /* toggle replacement option */
368 repl_mode = !repl_mode;
369 break;
371 default:
372 if(status == ABORT)
373 emlwrite(_("Search Cancelled"), NULL);
374 else
375 mlerase();
377 FWS_RETURN(FALSE);
380 /* replace option is disabled */
381 if (!(gmode & MDREPLACE)){
382 ucs4_strncpy(defpat, pat, NPAT);
383 defpat[NPAT-1] = '\0';
384 break;
386 else if (search){ /* search now */
387 ucs4_strncpy(pat, defpat, NPAT); /* remember this search for the future */
388 pat[NPAT-1] = '\0';
389 break;
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 occurrences 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
401 * place...
403 status = 0; /* using "status" as int temporarily! */
404 while(1){
405 if(defpat[status] == '\0'){
406 forwchar(0, 1);
407 break;
410 if(status + curwp->w_doto >= llength(curwp->w_dotp) ||
411 !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c))
412 break;
413 status++;
416 /* search for the pattern */
418 while (n-- > 0) {
419 if((status = forscan(&wrapt,defpat, flags, NULL,0,PTBEG)) == FALSE)
420 break;
423 /* and complain if not there */
424 if (status == FALSE){
425 char *utf8;
426 UCS x[1];
428 x[0] = '\0';
430 utf8 = sucs4_to_utf8_cpystr(defpat ? defpat : x, flags & SR_BACKWRD);
431 /* TRANSLATORS: reporting the result of a failed search */
432 eml.s = utf8;
433 emlwrite(_("\"%s\" not found"), &eml);
434 if(utf8)
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){
448 emlwrite("", NULL);
451 reverse_all(defpat, flags & SR_BACKWRD);
452 if(curwp->w_doto == -1){
453 curwp->w_doto = 0;
454 curwp->w_flag |= WFMOVE;
456 FWS_RETURN(status);
460 /* Replace a pattern with the pattern the user types in one or more times. */
462 replace_pat(UCS *defpat, int *wrapt, int bsearch)
464 register int status;
465 UCS lpat[NPAT], origpat[NPAT]; /* case sensitive pattern */
466 EXTRAKEYS menu_pat[12];
467 int repl_all = FALSE;
468 UCS *b;
469 char utf8tmp[NPMT];
470 UCS prompt[NPMT];
471 UCS *promptp;
472 int i, flags;
474 if(bsearch){
475 flags = SR_BACKWRD;
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 */
482 lpat[0] = '\0';
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);
490 while(1) {
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 */
499 update();
500 (*term.t_rev)(1);
501 get_pat_cases(origpat, defpat);
502 pputs(origpat, 1); /* highlight word */
503 (*term.t_rev)(0);
505 snprintf(utf8tmp, NPMT, "Replace%s \"", repl_all ? " every" : "");
506 b = utf8_to_ucs4_cpystr(utf8tmp);
507 if(b){
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");
520 if(b){
521 ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
522 promptp += ucs4_strlen(promptp);
523 prompt[NPMT-1] = '\0';
524 fs_give((void **) &b);
527 if(rpat[0] != '\0'){
528 if((promptp-prompt) < NPMT-2){
529 *promptp++ = ' ';
530 *promptp++ = '[';
531 *promptp = '\0';
534 expandp(rpat, promptp, NPMT-(promptp-prompt));
535 prompt[NPMT-1] = '\0';
536 promptp += ucs4_strlen(promptp);
538 if((promptp-prompt) < NPMT-1){
539 *promptp++ = ']';
540 *promptp = '\0';
544 if((promptp-prompt) < NPMT-3){
545 *promptp++ = ' ';
546 *promptp++ = ':';
547 *promptp++ = ' ';
548 *promptp = '\0';
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 */
559 switch(status){
561 case TRUE :
562 case FALSE :
563 if(lpat[0]){
564 ucs4_strncpy(rpat, lpat, NPAT); /* remember default */
565 rpat[NPAT-1] = '\0';
567 else{
568 ucs4_strncpy(lpat, rpat, NPAT); /* use default */
569 lpat[NPAT-1] = '\0';
572 if (repl_all){
573 status = replace_all(defpat, lpat, bsearch);
575 else{
576 if(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))
590 curwp->w_doto--;
591 supdate(defpat, bsearch);
592 status = TRUE;
595 if(status == TRUE)
596 emlwrite("", NULL);
598 return(status);
600 case HELPCH: /* help requested */
601 if(Pmaster){
602 VARS_TO_SAVE *saved_state;
604 saved_state = save_pico_state();
605 (*Pmaster->helper)(Pmaster->search_help,
606 _("Help for Searching"), 1);
607 if(saved_state){
608 restore_pico_state(saved_state);
609 free_pico_state(saved_state);
612 else
613 pico_help(SearchHelpText, _("Help for Searching"), 1);
615 case (CTRL|'L'): /* redraw requested */
616 pico_refresh(FALSE, 1);
617 update();
618 break;
620 case (CTRL|'X'): /* toggle replace all option */
621 if (repl_all){
622 repl_all = FALSE;
623 /* TRANSLATORS: abbreviation for Replace All occurrences */
624 menu_pat[0].label = N_("Repl All");
626 else{
627 repl_all = TRUE;
628 /* TRANSLATORS: Replace just one occurence */
629 menu_pat[0].label = N_("Repl One");
632 break;
634 default:
635 if(status == ABORT){
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 */
641 else{
642 mlerase();
643 chword(defpat, origpat, bsearch);
646 supdate(defpat, bsearch);
647 return(FALSE);
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 */
656 void
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;
677 UCS *b;
678 UCS realpat[NPAT];
679 char utf8tmp[NPMT];
680 UCS *promptp;
681 UCS prompt[NPMT];
682 int wrapt, n = 0;
683 LINE *stop_line = curwp->w_dotp;
684 int stop_offset = curwp->w_doto;
685 EML eml;
686 int flags;
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
690 * to the beginning.
692 if(bsearch){
693 flags = SR_BACKWRD;
694 curwp->w_doto -= ucs4_strlen(orig) - 1;
695 curwp->w_flag |= WFMOVE;
697 else
698 flags = SR_FORWARD;
700 stop_offset = curwp->w_doto;
701 while (1)
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 */
706 update();
707 (*term.t_rev)(1);
708 get_pat_cases(realpat, orig);
709 pputs(realpat, 1); /* highlight word */
710 (*term.t_rev)(0);
711 fflush(stdout);
713 snprintf(utf8tmp, NPMT, "Replace \"");
714 b = utf8_to_ucs4_cpystr(utf8tmp);
715 if(b){
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 \"");
729 if(b){
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){
741 *promptp++ = '\"';
742 *promptp = '\0';
745 prompt[NPMT-1] = '\0';
747 status = mlyesno(prompt, TRUE); /* ask user */
749 if(bsearch){
750 curwp->w_doto -= ucs4_strlen(realpat) - 1;
751 curwp->w_flag |= WFMOVE;
753 if (status == TRUE){
754 n++;
755 chword(realpat, repl, bsearch); /* replace word */
756 supdate(realpat, bsearch);
757 }else{
758 chword(realpat, realpat, bsearch); /* replace word by itself */
759 supdate(realpat, bsearch);
760 if(status == ABORT){ /* if cancelled return */
761 eml.s = comatose(n);
762 emlwrite("Replace All cancelled after %s changes", &eml);
763 return (ABORT); /* ... else keep looking */
767 else{
768 char *utf8;
770 utf8 = sucs4_to_utf8_cpystr(orig, bsearch);
771 if(utf8){
772 eml.s = utf8;
773 emlwrite(_("No more matches for \"%s\""), &eml);
774 fs_give((void **) &utf8);
776 else
777 emlwrite(_("No more matches"), NULL);
779 return (FALSE);
784 /* Read a replacement pattern. Modeled after readpattern(). */
786 srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode, int flags)
788 register int s;
789 int i = 0;
790 int toggle, bsearch, bol, eol, exact;
791 UCS *b;
792 UCS prompt[NPMT];
793 UCS *promptp;
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;
809 if(toggle == 0){
810 if (i == REPLACE_KEY)
811 menu_pat[i].label = repl_mode ? N_("No Replace")
812 : N_("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;
818 switch(i){
819 case EXACTSR_KEY:
820 menu_pat[i].label = exact ? N_("No Exact")
821 : N_("Exact");
822 break;
823 case REPLACE_KEY:
824 menu_pat[i].label = repl_mode ? N_("No Replace")
825 : N_("Replace");
826 break;
827 case BSEARCH_KEY:
828 menu_pat[i].label = bsearch ? N_("Srch Fwd")
829 : N_("Srch Back");
830 break;
831 case BGNLINE_KEY:
832 menu_pat[i].label = bol ? N_("Anywhere")
833 : N_("Bgn Line");
834 break;
835 case ENDLINE_KEY:
836 menu_pat[i].label = eol ? N_("Anywhere")
837 : N_("End Line");
838 break;
839 default : break;
841 if(menu_pat[i].name)
842 KS_OSDATASET(&menu_pat[i], KS_NONE);
845 b = utf8_to_ucs4_cpystr(utf8prompt);
846 if(b){
847 ucs4_strncpy(prompt, b, NPMT);
848 prompt[NPMT-1] = '\0';
849 if(bsearch){
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';
855 if(bol){
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';
860 } else if(eol){
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';
866 if(exact){
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)];
877 if(repl_mode){
878 b = utf8_to_ucs4_cpystr(" (to replace)");
879 if(b){
880 ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
881 promptp += ucs4_strlen(promptp);
882 prompt[NPMT-1] = '\0';
883 fs_give((void **) &b);
887 if(pat[0] != '\0'){
888 if((promptp-prompt) < NPMT-2){
889 *promptp++ = ' ';
890 *promptp++ = '[';
891 *promptp = '\0';
894 expandp(pat, promptp, NPMT-(promptp-prompt));
895 prompt[NPMT-1] = '\0';
896 promptp += ucs4_strlen(promptp);
898 if((promptp-prompt) < NPMT-1){
899 *promptp++ = ']';
900 *promptp = '\0';
904 if((promptp-prompt) < NPMT-2){
905 *promptp++ = ':';
906 *promptp++ = ' ';
907 *promptp = '\0';
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);
921 pat[NPAT-1] = '\0';
922 rpat[0] = '\0';
925 s = TRUE; /* let caller know to proceed */
928 return(s);
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
938 * the search string.
942 readnumpat(char *utf8prompt)
944 int i, n;
945 char numpat[NPMT];
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;
956 numpat[0] = '\0';
957 while(1)
958 switch(mlreplyd_utf8(utf8prompt, numpat, NPMT, QNORML, menu_pat)){
959 case TRUE :
960 if(*numpat){
961 for(i = n = 0; numpat[i]; i++)
962 if(strchr("0123456789", numpat[i])){
963 n = (n * 10) + (numpat[i] - '0');
965 else
966 return(-2);
968 return(n);
971 case FALSE :
972 default :
973 return(-1);
975 case (CTRL|'T') :
976 return(-3);
978 case (CTRL|'L') :
979 case HELPCH :
980 break;
986 readpattern(char *utf8prompt, int text_mode, int flags)
988 register int s;
989 int i;
990 int toggle, bsearch, bol, eol, exact;
991 UCS *b;
992 UCS tpat[NPAT+20];
993 UCS *tpatp;
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;
1009 if(toggle == 0){
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;
1019 switch(i){
1020 case EXACTSR_KEY:
1021 menu_pat[i].label = exact ? N_("No Exact")
1022 : N_("Exact");
1023 break;
1024 case BSEARCH_KEY:
1025 menu_pat[i].label = bsearch ? N_("Srch Fwd")
1026 : N_("Srch Back");
1027 break;
1028 case BGNLINE_KEY:
1029 menu_pat[i].label = bol ? N_("Anywhere")
1030 : N_("Bgn Line");
1031 break;
1032 case ENDLINE_KEY:
1033 menu_pat[i].label = eol ? N_("Anywhere")
1034 : N_("End Line");
1035 break;
1036 default : break;
1038 if(menu_pat[i].name)
1039 KS_OSDATASET(&menu_pat[i], KS_NONE);
1043 b = utf8_to_ucs4_cpystr(utf8prompt);
1044 if(b){
1045 ucs4_strncpy(tpat, b, NPAT+20);
1046 tpat[NPAT+20-1] = '\0';
1047 if(bsearch){
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';
1053 if(bol){
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';
1058 } else if (eol){
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';
1064 if (exact){
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)];
1075 if(pat[0] != '\0'){
1076 if((tpatp-tpat) < NPAT+20-2){
1077 *tpatp++ = ' ';
1078 *tpatp++ = '[';
1079 *tpatp = '\0';
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){
1087 *tpatp++ = ']';
1088 *tpatp = '\0';
1092 if((tpatp-tpat) < NPAT+20-3){
1093 *tpatp++ = ' ';
1094 *tpatp++ = ':';
1095 *tpatp++ = ' ';
1096 *tpatp = '\0';
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);
1105 pat[NPAT-1] = '\0';
1106 rpat[0] = '\0';
1108 else if (s == FALSE && pat[0] != '\0') /* CR, but old one */
1109 s = TRUE;
1111 return(s);
1114 /* given a line, reverse its content */
1115 void
1116 reverse_line(LINE *l)
1118 int i, j, a;
1119 UCS u;
1121 if(l == NULL) return;
1122 j = llength(l) - 1;
1123 for(i = 0; i < j; i++, j--){
1124 u = lgetc(l, j).c; /* reverse the text */
1125 lgetc(l, j).c = lgetc(l, i).c;
1126 lgetc(l, i).c = u;
1127 a = lgetc(l, j).a; /* and the attribute */
1128 lgetc(l, j).a = lgetc(l, i).a;
1129 lgetc(l, i).a = a;
1133 void
1134 reverse_all(UCS *pat, int bsearch)
1136 if(bsearch != 0){
1137 reverse_buffer();
1138 reverse_ucs4(pat);
1142 void
1143 reverse_buffer(void)
1145 LINE *l;
1147 for(l = lforw(curbp->b_linep); l != curbp->b_linep; l = lforw(l))
1148 reverse_line(l);
1150 /* reverse links in buffer */
1151 l = curbp->b_linep;
1152 do {
1153 lforw(l) = lback(l);
1154 l = lforw(l);
1155 } while(l != curbp->b_linep);
1157 l = curbp->b_linep;
1158 do {
1159 lback(lforw(l)) = l;
1160 l = lforw(l);
1161 } while (l != curbp->b_linep);
1163 curwp->w_doto = llength(curwp->w_dotp) - 1 - curwp->w_doto;
1168 /* given a UCS4 string reverse its content */
1169 void
1170 reverse_ucs4(UCS *s)
1172 int i, j;
1173 UCS u;
1175 j = ucs4_strlen(s) - 1;
1176 for(i = 0; i < j; i++, j--){
1177 u = s[j];
1178 s[j] = s[i];
1179 s[i] = u;
1184 /* search forward for a <patrn>.
1185 * A backward search is a forward search in backward lines with backward
1186 * patterns
1189 forscan(int *wrapt, /* boolean indicating search wrapped */
1190 UCS *patrn, /* string to scan for */
1191 int flags, /* direction and position */
1192 LINE *limitp, /* stop searching if reached */
1193 int limito, /* stop searching if reached */
1194 int leavep) /* place to leave point
1195 PTBEG = beginning of match
1196 PTEND = at end of match */
1199 LINE *curline; /* current line during scan */
1200 int curoff; /* position within current line */
1201 LINE *lastline; /* last line position during scan */
1202 int lastoff; /* position within last line */
1203 UCS c; /* character at current position */
1204 LINE *matchline; /* current line during matching */
1205 int matchoff; /* position in matching line */
1206 UCS *patptr; /* pointer into pattern */
1207 int stopoff; /* offset to stop search */
1208 LINE *stopline; /* line to stop search */
1209 int ftest; /* position of first character of test */
1210 int bsearch;
1211 int bol;
1212 int eol;
1214 bsearch = flags & SR_BACKWRD;
1215 bol = flags & SR_BEGLINE;
1216 eol = flags & SR_ENDLINE;
1217 *wrapt = FALSE;
1219 /* if bsearch is set we return the match at the beginning of the
1220 * matching string, so we make this algorithm return the end of
1221 * the string, so that when we reverse we be at the beginning.
1223 if(bsearch)
1224 leavep = leavep == PTBEG ? PTEND : PTBEG;
1227 * the idea is to set the character to end the search at the
1228 * next character in the buffer. thus, let the search wrap
1229 * completely around the buffer.
1231 * first, test to see if we are at the end of the line,
1232 * otherwise start searching on the next character.
1234 if(curwp->w_doto == llength(curwp->w_dotp)){
1236 * dot is not on end of a line
1237 * start at 0 offset of the next line
1239 stopoff = curoff = 0;
1240 stopline = curline = lforw(curwp->w_dotp);
1241 if (curwp->w_dotp == curbp->b_linep)
1242 *wrapt = TRUE;
1244 else{
1245 stopoff = curoff = curwp->w_doto;
1246 stopline = curline = curwp->w_dotp;
1249 /* scan each character until we hit the head link record */
1252 * maybe wrapping is a good idea
1254 while (curline){
1256 if(curline == curbp->b_linep)
1257 *wrapt = TRUE;
1259 /* save the current position in case we need to
1260 restore it on a match */
1262 lastline = curline;
1263 lastoff = curoff;
1265 /* get the current character resolving EOLs */
1266 if (curoff == llength(curline)) { /* if at EOL */
1267 curline = lforw(curline); /* skip to next line */
1268 curoff = 0;
1269 c = '\n'; /* and return a <NL> */
1271 else if(curoff == -1){
1272 stopoff = curoff = 0;
1273 continue;
1274 c = '\n';
1276 else
1277 c = lgetc(curline, curoff++).c; /* get the char */
1279 if(bol)
1280 ftest = bsearch == 0 ? 1 : llength(curline) - ucs4_strlen(patrn) + 1;
1281 else if (eol)
1282 ftest = bsearch == 0 ? llength(curline) - ucs4_strlen(patrn) + 1 : 1;
1283 /* test it against first char in pattern */
1284 if (eq(c, patrn[0]) != FALSE /* if we find it..*/
1285 && ((bol == 0 && eol == 0) /* ...and if it is anywhere */
1286 || (bol != 0 /* ...or is at the begin or line */
1287 && ((bsearch == 0 && curoff == ftest) /* and search forward and found at beginning of line */
1288 || (bsearch != 0 && curoff == ftest))) /* or search backward and found at end of line */
1289 || (eol != 0 /* ...or is at the end or line */
1290 && ((bsearch == 0 && curoff == ftest) /* and search forward and found at beginning of line */
1291 || (bsearch != 0 && curoff == ftest))) /* or search backward and found at end of line */
1293 /* setup match pointers */
1294 matchline = curline;
1295 matchoff = curoff;
1296 patptr = &patrn[0];
1298 /* scan through patrn for a match */
1299 while (*++patptr != '\0') {
1300 /* advance all the pointers */
1301 if (matchoff >= llength(matchline)) {
1302 /* advance past EOL */
1303 matchline = lforw(matchline);
1304 matchoff = 0;
1305 c = '\n';
1306 } else
1307 c = lgetc(matchline, matchoff++).c;
1309 if(matchline == limitp && matchoff == limito)
1310 return(FALSE);
1312 /* and test it against the pattern */
1313 if (eq(*patptr, c) == FALSE)
1314 goto fail;
1317 /* A SUCCESSFUL MATCH!!! */
1318 /* reset the global "." pointers */
1319 if (leavep == PTEND) { /* at end of string */
1320 curwp->w_dotp = matchline;
1321 curwp->w_doto = matchoff - 1;
1323 else { /* at beginning of string */
1324 curwp->w_dotp = lastline;
1325 curwp->w_doto = lastoff;
1328 curwp->w_flag |= WFMOVE; /* flag that we have moved */
1329 return(TRUE);
1332 fail:; /* continue to search */
1333 if(((curline == stopline) && (curoff == stopoff))
1334 || (curline == limitp && curoff == limito))
1335 break; /* searched everywhere... */
1337 /* we could not find a match */
1339 return(FALSE);
1344 /* expandp: expand control key sequences for output */
1346 expandp(UCS *srcstr, /* string to expand */
1347 UCS *deststr, /* destination of expanded string */
1348 int maxlength) /* maximum chars in destination */
1350 UCS c; /* current char to translate */
1352 /* scan through the string */
1353 while ((c = *srcstr++) != 0) {
1354 if (c == '\n') { /* its an EOL */
1355 *deststr++ = '<';
1356 *deststr++ = 'N';
1357 *deststr++ = 'L';
1358 *deststr++ = '>';
1359 maxlength -= 4;
1360 } else if (c < 0x20 || c == 0x7f) { /* control character */
1361 *deststr++ = '^';
1362 *deststr++ = c ^ 0x40;
1363 maxlength -= 2;
1364 } else if (c == '%') {
1365 *deststr++ = '%';
1366 *deststr++ = '%';
1367 maxlength -= 2;
1368 } else { /* any other character */
1369 *deststr++ = c;
1370 maxlength--;
1373 /* check for maxlength */
1374 if (maxlength < 4) {
1375 *deststr++ = '$';
1376 *deststr = '\0';
1377 return(FALSE);
1381 *deststr = '\0';
1382 return(TRUE);
1387 * chword() - change the given word, wp, pointed to by the curwp->w_dot
1388 * pointers to the word in cb
1389 * if bsearch is set, then cb is supposed to come unreversed, while
1390 * the buffer is supposed to be reversed, so we must reverse cb before
1391 * inserting it.
1393 void
1394 chword(UCS *wb, UCS *cb, int bsearch)
1396 UCS *u;
1397 ldelete(ucs4_strlen(wb), NULL); /* not saved in kill buffer */
1398 if(bsearch) reverse_ucs4(cb);
1399 for(u = cb; *u != '\0'; u++)
1400 linsert(1, *u);
1401 if(bsearch) reverse_ucs4(cb);
1403 curwp->w_flag |= WFEDIT;
1406 void
1407 supdate(UCS *pat, int bsearch)
1409 reverse_all(pat, bsearch); /* undo reverse buffer and pattern */
1410 update();
1411 reverse_all(pat, bsearch); /* reverse buffer and pattern */
1414 char *
1415 sucs4_to_utf8_cpystr(UCS *orig, int bsearch)
1417 char *utf8;
1418 if(bsearch) reverse_ucs4(orig);
1419 utf8 = ucs4_to_utf8_cpystr(orig);
1420 if(bsearch) reverse_ucs4(orig);
1421 return utf8;