1 /* $OpenBSD: re_search.c,v 1.26 2011/01/21 19:10:13 kjell Exp $ */
3 /* This file is in the public domain. */
6 * regular expression search commands for Mg
8 * This file contains functions to implement several of gnuemacs's regular
9 * expression functions for Mg. Several of the routines below are just minor
10 * re-arrangements of Mg's non-regular expression search functions. Some of
11 * them are similar in structure to the original MicroEMACS, others are
12 * modifications of Rich Ellison's code. Peter Newton re-wrote about half of
19 #include <sys/types.h>
24 #define SRCH_BEGIN (0) /* search sub-codes */
25 #define SRCH_FORW (-1)
26 #define SRCH_BACK (-2)
27 #define SRCH_NOPR (-3)
28 #define SRCH_ACCM (-4)
29 #define SRCH_MARK (-5)
31 #define RE_NMATCH 10 /* max number of matches */
32 #define REPLEN 256 /* max length of replacement string */
34 char re_pat[NPAT]; /* regex pattern */
35 int re_srch_lastdir = SRCH_NOPR; /* last search flags */
36 int casefoldsearch = TRUE; /* does search ignore case? */
38 static int re_doreplace(RSIZE, char *);
39 static int re_forwsrch(void);
40 static int re_backsrch(void);
41 static int re_readpattern(char *);
42 static int killmatches(int);
43 static int countmatches(int);
47 * Get a search string from the user and search for it starting at ".". If
48 * found, move "." to just after the matched characters. display does all
49 * the hard stuff. If not found, it just prints a message.
53 re_forwsearch(int f, int n)
57 if ((s = re_readpattern("RE Search")) != TRUE)
59 if (re_forwsrch() == FALSE) {
60 ewprintf("Search failed: \"%s\"", re_pat);
63 re_srch_lastdir = SRCH_FORW;
69 * Get a search string from the user, and search, starting at "."
70 * and proceeding toward the front of the buffer. If found "." is left
71 * pointing at the first character of the pattern [the last character that
76 re_backsearch(int f, int n)
80 if ((s = re_readpattern("RE Search backward")) != TRUE)
82 if (re_backsrch() == FALSE) {
83 ewprintf("Search failed: \"%s\"", re_pat);
86 re_srch_lastdir = SRCH_BACK;
91 * Search again, using the same search string and direction as the last search
92 * command. The direction has been saved in "srch_lastdir", so you know which
95 * XXX: This code has problems -- some incompatibility(?) with extend.c causes
96 * match to fail when it should not.
100 re_searchagain(int f, int n)
102 if (re_srch_lastdir == SRCH_NOPR) {
103 ewprintf("No last search");
106 if (re_srch_lastdir == SRCH_FORW) {
107 if (re_forwsrch() == FALSE) {
108 ewprintf("Search failed: \"%s\"", re_pat);
113 if (re_srch_lastdir == SRCH_BACK)
114 if (re_backsrch() == FALSE) {
115 ewprintf("Search failed: \"%s\"", re_pat);
122 /* Compiled regex goes here-- changed only when new pattern read */
123 static regex_t re_buff;
124 static regmatch_t re_match[RE_NMATCH];
128 * Replace strings selectively. Does a search and replace operation.
132 re_queryrepl(int f, int n)
134 int rcnt = 0; /* replacements made so far */
135 int plen, s; /* length of found string */
136 char news[NPAT]; /* replacement string */
138 if ((s = re_readpattern("RE Query replace")) != TRUE)
140 if (eread("Query replace %s with: ", news, NPAT,
141 EFNUL | EFNEW | EFCR, re_pat) == NULL)
143 ewprintf("Query replacing %s with %s:", re_pat, news);
146 * Search forward repeatedly, checking each time whether to insert
147 * or not. The "!" case makes the check always true, so it gets put
148 * into a tighter loop for efficiency.
150 while (re_forwsrch() == TRUE) {
153 switch (getkey(FALSE)) {
155 plen = re_match[0].rm_eo - re_match[0].rm_so;
156 if (re_doreplace((RSIZE)plen, news) == FALSE)
162 plen = re_match[0].rm_eo - re_match[0].rm_so;
163 if (re_doreplace((RSIZE)plen, news) == FALSE)
168 case CCHR('G'): /* ^G */
169 (void)ctrlg(FFRAND, 0);
171 case CCHR('['): /* ESC */
176 plen = re_match[0].rm_eo - re_match[0].rm_so;
177 if (re_doreplace((RSIZE)plen, news) == FALSE)
180 } while (re_forwsrch() == TRUE);
183 case CCHR('?'): /* To not replace */
187 ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
193 curwp->w_rflag |= WFFULL;
197 ewprintf("(No replacements done)");
199 ewprintf("(1 replacement done)");
201 ewprintf("(%d replacements done)", rcnt);
207 * Routine re_doreplace calls lreplace to make replacements needed by
208 * re_query replace. Its reason for existence is to deal with \1, \2. etc.
209 * plen: length to remove
210 * st: replacement string
213 re_doreplace(RSIZE plen, char *st)
215 int j, k, s, more, num, state;
225 /* The following FSA parses the replacement string */
232 } else if (*st == '\0')
243 if (*st >= '0' && *st <= '9') {
247 } else if (*st == '\0')
259 if (*st >= '0' && *st <= '9') {
260 num = 10 * num + *st - '0';
263 if (num >= RE_NMATCH)
265 k = re_match[num].rm_eo - re_match[num].rm_so;
268 bcopy(&(clp->l_text[re_match[num].rm_so]),
286 } /* switch (state) */
290 s = lreplace(plen, repstr);
295 * This routine does the real work of a forward search. The pattern is
296 * sitting in the external variable "pat". If found, dot is updated, the
297 * window system is notified of the change, and TRUE is returned. If the
298 * string isn't found, FALSE is returned.
309 if (tbo == clp->l_used)
311 * Don't start matching past end of line -- must move to
312 * beginning of next line, unless at end of file.
314 if (clp != curbp->b_headp) {
319 * Note this loop does not process the last line, but this editor
320 * always makes the last line empty so this is good.
322 while (clp != (curbp->b_headp)) {
323 re_match[0].rm_so = tbo;
324 re_match[0].rm_eo = llength(clp);
325 error = regexec(&re_buff, ltext(clp), RE_NMATCH, re_match,
331 curwp->w_doto = re_match[0].rm_eo;
333 curwp->w_rflag |= WFMOVE;
341 * This routine does the real work of a backward search. The pattern is sitting
342 * in the external variable "re_pat". If found, dot is updated, the window
343 * system is notified of the change, and TRUE is returned. If the string isn't
344 * found, FALSE is returned.
351 regmatch_t lastmatch;
356 /* Start search one position to the left of dot */
359 /* must move up one line */
365 * Note this loop does not process the last line, but this editor
366 * always makes the last line empty so this is good.
368 while (clp != (curbp->b_headp)) {
369 re_match[0].rm_so = 0;
370 re_match[0].rm_eo = llength(clp);
371 lastmatch.rm_so = -1;
373 * Keep searching until we don't match any longer. Assumes a
374 * non-match does not modify the re_match array. We have to
375 * do this character-by-character after the first match since
376 * POSIX regexps don't give you a way to do reverse matches.
378 while (!regexec(&re_buff, ltext(clp), RE_NMATCH, re_match,
379 REG_STARTEND) && re_match[0].rm_so < tbo) {
380 memcpy(&lastmatch, &re_match[0], sizeof(regmatch_t));
382 re_match[0].rm_eo = llength(clp);
384 if (lastmatch.rm_so == -1) {
388 memcpy(&re_match[0], &lastmatch, sizeof(regmatch_t));
389 curwp->w_doto = re_match[0].rm_so;
391 curwp->w_rflag |= WFMOVE;
400 * Stash it in the external variable "re_pat". The "pat" is
401 * not updated if the user types in an empty line. If the user typed
402 * an empty line, and there is no old pattern, it is an error.
403 * Display the old pattern, in the style of Jeff Lomicka. There is
404 * some do-it-yourself control expansion.
407 re_readpattern(char *prompt)
409 static int dofree = 0;
411 char tpat[NPAT], *rep;
413 if (re_pat[0] == '\0')
414 rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, prompt);
416 rep = eread("%s: (default %s) ", tpat, NPAT,
417 EFNUL | EFNEW | EFCR, prompt, re_pat);
420 if (rep[0] != '\0') {
421 /* New pattern given */
422 (void)strlcpy(re_pat, tpat, sizeof(re_pat));
424 flags = REG_EXTENDED | REG_ICASE;
426 flags = REG_EXTENDED;
429 error = regcomp(&re_buff, re_pat, flags);
432 regerror(error, &re_buff, message, sizeof(message));
433 ewprintf("Regex Error: %s", message);
439 } else if (rep[0] == '\0' && re_pat[0] != '\0')
440 /* Just using old pattern */
448 * Cause case to not matter in searches. This is the default. If called
449 * with argument cause case to matter.
453 setcasefold(int f, int n)
456 casefoldsearch = FALSE;
457 ewprintf("Case-fold-search unset");
459 casefoldsearch = TRUE;
460 ewprintf("Case-fold-search set");
464 * Invalidate the regular expression pattern since I'm too lazy to
472 * Delete all lines after dot that contain a string matching regex.
476 delmatchlines(int f, int n)
480 if ((s = re_readpattern("Flush lines (containing match for regexp)"))
484 s = killmatches(TRUE);
489 * Delete all lines after dot that don't contain a string matching regex.
493 delnonmatchlines(int f, int n)
497 if ((s = re_readpattern("Keep lines (containing match for regexp)"))
501 s = killmatches(FALSE);
506 * This function does the work of deleting matching lines.
509 killmatches(int cond)
516 if (curwp->w_doto == llength(clp))
517 /* Consider dot on next line */
520 while (clp != (curbp->b_headp)) {
521 /* see if line matches */
522 re_match[0].rm_so = 0;
523 re_match[0].rm_eo = llength(clp);
524 error = regexec(&re_buff, ltext(clp), RE_NMATCH, re_match,
527 /* Delete line when appropriate */
528 if ((cond == FALSE && error) || (cond == TRUE && !error)) {
532 s = ldelete(llength(clp) + 1, KNONE);
534 curwp->w_rflag |= WFMOVE;
541 ewprintf("%d line(s) deleted", count);
543 curwp->w_rflag |= WFMOVE;
549 * Count lines matching regex.
553 cntmatchlines(int f, int n)
557 if ((s = re_readpattern("Count lines (matching regexp)")) != TRUE)
559 s = countmatches(TRUE);
565 * Count lines that fail to match regex.
569 cntnonmatchlines(int f, int n)
573 if ((s = re_readpattern("Count lines (not matching regexp)")) != TRUE)
575 s = countmatches(FALSE);
581 * This function does the work of counting matching lines.
584 countmatches(int cond)
591 if (curwp->w_doto == llength(clp))
592 /* Consider dot on next line */
595 while (clp != (curbp->b_headp)) {
596 /* see if line matches */
597 re_match[0].rm_so = 0;
598 re_match[0].rm_eo = llength(clp);
599 error = regexec(&re_buff, ltext(clp), RE_NMATCH, re_match,
602 /* Count line when appropriate */
603 if ((cond == FALSE && error) || (cond == TRUE && !error))
609 ewprintf("Number of lines matching: %d", count);
611 ewprintf("Number of lines not matching: %d", count);