Annotation of src/usr.bin/mg/re_search.c, Revision 1.35
1.35 ! tb 1: /* $OpenBSD: re_search.c,v 1.34 2020/07/09 10:42:24 tb Exp $ */
1.17 kjell 2:
3: /* This file is in the public domain. */
1.7 niklas 4:
1.1 deraadt 5: /*
1.6 millert 6: * regular expression search commands for Mg
1.1 deraadt 7: *
1.6 millert 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
1.9 mickey 11: * them are similar in structure to the original MicroEMACS, others are
1.6 millert 12: * modifications of Rich Ellison's code. Peter Newton re-wrote about half of
13: * them from scratch.
1.1 deraadt 14: */
15:
1.6 millert 16: #ifdef REGEX
1.31 bcallah 17: #include <sys/queue.h>
1.2 millert 18: #include <sys/types.h>
19: #include <regex.h>
1.31 bcallah 20: #include <signal.h>
21: #include <stdio.h>
22: #include <string.h>
1.2 millert 23:
1.31 bcallah 24: #include "def.h"
1.6 millert 25: #include "macro.h"
1.1 deraadt 26:
1.6 millert 27: #define SRCH_BEGIN (0) /* search sub-codes */
1.1 deraadt 28: #define SRCH_FORW (-1)
29: #define SRCH_BACK (-2)
30: #define SRCH_NOPR (-3)
31: #define SRCH_ACCM (-4)
32: #define SRCH_MARK (-5)
33:
1.6 millert 34: #define RE_NMATCH 10 /* max number of matches */
35: #define REPLEN 256 /* max length of replacement string */
1.2 millert 36:
1.6 millert 37: char re_pat[NPAT]; /* regex pattern */
38: int re_srch_lastdir = SRCH_NOPR; /* last search flags */
39: int casefoldsearch = TRUE; /* does search ignore case? */
40:
1.20 kjell 41: static int re_doreplace(RSIZE, char *);
1.11 millert 42: static int re_forwsrch(void);
43: static int re_backsrch(void);
44: static int re_readpattern(char *);
45: static int killmatches(int);
46: static int countmatches(int);
1.1 deraadt 47:
48: /*
49: * Search forward.
1.9 mickey 50: * Get a search string from the user and search for it starting at ".". If
51: * found, move "." to just after the matched characters. display does all
1.6 millert 52: * the hard stuff. If not found, it just prints a message.
1.1 deraadt 53: */
1.5 millert 54: /* ARGSUSED */
1.6 millert 55: int
1.12 cloder 56: re_forwsearch(int f, int n)
1.5 millert 57: {
1.6 millert 58: int s;
1.1 deraadt 59:
1.5 millert 60: if ((s = re_readpattern("RE Search")) != TRUE)
1.1 deraadt 61: return (s);
62: if (re_forwsrch() == FALSE) {
1.30 lum 63: dobeep();
1.1 deraadt 64: ewprintf("Search failed: \"%s\"", re_pat);
65: return (FALSE);
66: }
67: re_srch_lastdir = SRCH_FORW;
68: return (TRUE);
69: }
70:
71: /*
72: * Reverse search.
1.14 db 73: * Get a search string from the user, and search, starting at "."
1.1 deraadt 74: * and proceeding toward the front of the buffer. If found "." is left
75: * pointing at the first character of the pattern [the last character that
76: * was matched].
77: */
1.5 millert 78: /* ARGSUSED */
1.6 millert 79: int
1.12 cloder 80: re_backsearch(int f, int n)
1.5 millert 81: {
1.6 millert 82: int s;
1.1 deraadt 83:
1.5 millert 84: if ((s = re_readpattern("RE Search backward")) != TRUE)
1.1 deraadt 85: return (s);
86: if (re_backsrch() == FALSE) {
1.30 lum 87: dobeep();
1.1 deraadt 88: ewprintf("Search failed: \"%s\"", re_pat);
89: return (FALSE);
90: }
91: re_srch_lastdir = SRCH_BACK;
92: return (TRUE);
93: }
94:
95: /*
1.9 mickey 96: * Search again, using the same search string and direction as the last search
97: * command. The direction has been saved in "srch_lastdir", so you know which
1.6 millert 98: * way to go.
99: *
100: * XXX: This code has problems -- some incompatibility(?) with extend.c causes
101: * match to fail when it should not.
1.1 deraadt 102: */
1.5 millert 103: /* ARGSUSED */
1.6 millert 104: int
1.12 cloder 105: re_searchagain(int f, int n)
1.5 millert 106: {
107: if (re_srch_lastdir == SRCH_NOPR) {
1.30 lum 108: dobeep();
1.5 millert 109: ewprintf("No last search");
110: return (FALSE);
111: }
112: if (re_srch_lastdir == SRCH_FORW) {
113: if (re_forwsrch() == FALSE) {
1.30 lum 114: dobeep();
1.5 millert 115: ewprintf("Search failed: \"%s\"", re_pat);
116: return (FALSE);
117: }
118: return (TRUE);
119: }
1.9 mickey 120: if (re_srch_lastdir == SRCH_BACK)
1.5 millert 121: if (re_backsrch() == FALSE) {
1.30 lum 122: dobeep();
1.5 millert 123: ewprintf("Search failed: \"%s\"", re_pat);
124: return (FALSE);
125: }
1.6 millert 126:
127: return (TRUE);
1.1 deraadt 128: }
129:
130: /* Compiled regex goes here-- changed only when new pattern read */
1.28 jasper 131: static regex_t regex_buff;
132: static regmatch_t regex_match[RE_NMATCH];
1.1 deraadt 133:
134: /*
135: * Re-Query Replace.
136: * Replace strings selectively. Does a search and replace operation.
137: */
1.5 millert 138: /* ARGSUSED */
1.6 millert 139: int
1.12 cloder 140: re_queryrepl(int f, int n)
1.5 millert 141: {
1.14 db 142: int rcnt = 0; /* replacements made so far */
1.13 vincent 143: int plen, s; /* length of found string */
1.19 deraadt 144: char news[NPAT]; /* replacement string */
1.1 deraadt 145:
1.5 millert 146: if ((s = re_readpattern("RE Query replace")) != TRUE)
1.1 deraadt 147: return (s);
1.19 deraadt 148: if (eread("Query replace %s with: ", news, NPAT,
149: EFNUL | EFNEW | EFCR, re_pat) == NULL)
1.13 vincent 150: return (ABORT);
1.1 deraadt 151: ewprintf("Query replacing %s with %s:", re_pat, news);
152:
153: /*
154: * Search forward repeatedly, checking each time whether to insert
155: * or not. The "!" case makes the check always true, so it gets put
156: * into a tighter loop for efficiency.
157: */
158: while (re_forwsrch() == TRUE) {
1.5 millert 159: retry:
1.27 lum 160: update(CMODE);
1.1 deraadt 161: switch (getkey(FALSE)) {
162: case ' ':
1.28 jasper 163: plen = regex_match[0].rm_eo - regex_match[0].rm_so;
1.20 kjell 164: if (re_doreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 165: return (FALSE);
166: rcnt++;
167: break;
168:
169: case '.':
1.28 jasper 170: plen = regex_match[0].rm_eo - regex_match[0].rm_so;
1.20 kjell 171: if (re_doreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 172: return (FALSE);
173: rcnt++;
174: goto stopsearch;
175:
1.6 millert 176: case CCHR('G'): /* ^G */
1.8 art 177: (void)ctrlg(FFRAND, 0);
1.22 kjell 178: goto stopsearch;
1.6 millert 179: case CCHR('['): /* ESC */
1.1 deraadt 180: case '`':
181: goto stopsearch;
182: case '!':
183: do {
1.28 jasper 184: plen = regex_match[0].rm_eo - regex_match[0].rm_so;
1.20 kjell 185: if (re_doreplace((RSIZE)plen, news) == FALSE)
1.1 deraadt 186: return (FALSE);
187: rcnt++;
188: } while (re_forwsrch() == TRUE);
189: goto stopsearch;
190:
1.6 millert 191: case CCHR('?'): /* To not replace */
1.1 deraadt 192: break;
193:
194: default:
1.5 millert 195: ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
1.1 deraadt 196: goto retry;
197: }
198: }
1.6 millert 199:
1.1 deraadt 200: stopsearch:
1.25 kjell 201: curwp->w_rflag |= WFFULL;
1.27 lum 202: update(CMODE);
1.1 deraadt 203: if (!inmacro) {
204: if (rcnt == 0)
205: ewprintf("(No replacements done)");
206: else if (rcnt == 1)
207: ewprintf("(1 replacement done)");
208: else
209: ewprintf("(%d replacements done)", rcnt);
210: }
1.14 db 211: return (TRUE);
1.1 deraadt 212: }
213:
1.5 millert 214: /*
215: * Routine re_doreplace calls lreplace to make replacements needed by
216: * re_query replace. Its reason for existence is to deal with \1, \2. etc.
1.12 cloder 217: * plen: length to remove
218: * st: replacement string
1.1 deraadt 219: */
1.6 millert 220: static int
1.20 kjell 221: re_doreplace(RSIZE plen, char *st)
1.6 millert 222: {
223: int j, k, s, more, num, state;
1.21 deraadt 224: struct line *clp;
1.6 millert 225: char repstr[REPLEN];
1.5 millert 226:
227: clp = curwp->w_dotp;
228: more = TRUE;
229: j = 0;
230: state = 0;
1.6 millert 231: num = 0;
1.5 millert 232:
233: /* The following FSA parses the replacement string */
234: while (more) {
235: switch (state) {
236: case 0:
237: if (*st == '\\') {
238: st++;
239: state = 1;
240: } else if (*st == '\0')
241: more = FALSE;
242: else {
243: repstr[j] = *st;
244: j++;
245: if (j >= REPLEN)
246: return (FALSE);
247: st++;
248: }
249: break;
250: case 1:
251: if (*st >= '0' && *st <= '9') {
252: num = *st - '0';
253: st++;
254: state = 2;
255: } else if (*st == '\0')
256: more = FALSE;
257: else {
258: repstr[j] = *st;
259: j++;
260: if (j >= REPLEN)
261: return (FALSE);
262: st++;
263: state = 0;
264: }
265: break;
266: case 2:
267: if (*st >= '0' && *st <= '9') {
268: num = 10 * num + *st - '0';
269: st++;
270: } else {
271: if (num >= RE_NMATCH)
272: return (FALSE);
1.28 jasper 273: k = regex_match[num].rm_eo - regex_match[num].rm_so;
1.5 millert 274: if (j + k >= REPLEN)
275: return (FALSE);
1.28 jasper 276: bcopy(&(clp->l_text[regex_match[num].rm_so]),
1.6 millert 277: &repstr[j], k);
1.5 millert 278: j += k;
279: if (*st == '\0')
280: more = FALSE;
281: if (*st == '\\') {
282: st++;
283: state = 1;
284: } else {
285: repstr[j] = *st;
286: j++;
287: if (j >= REPLEN)
288: return (FALSE);
289: st++;
290: state = 0;
291: }
292: }
293: break;
1.6 millert 294: } /* switch (state) */
295: } /* while (more) */
1.1 deraadt 296:
1.5 millert 297: repstr[j] = '\0';
1.20 kjell 298: s = lreplace(plen, repstr);
1.5 millert 299: return (s);
1.1 deraadt 300: }
301:
302: /*
1.9 mickey 303: * This routine does the real work of a forward search. The pattern is
304: * sitting in the external variable "pat". If found, dot is updated, the
1.6 millert 305: * window system is notified of the change, and TRUE is returned. If the
1.1 deraadt 306: * string isn't found, FALSE is returned.
307: */
1.6 millert 308: static int
1.12 cloder 309: re_forwsrch(void)
1.5 millert 310: {
1.35 ! tb 311: int re_flags, tbo, tdotline, error;
1.21 deraadt 312: struct line *clp;
1.5 millert 313:
314: clp = curwp->w_dotp;
315: tbo = curwp->w_doto;
1.29 florian 316: tdotline = curwp->w_dotline;
1.5 millert 317:
318: if (tbo == clp->l_used)
319: /*
1.6 millert 320: * Don't start matching past end of line -- must move to
1.35 ! tb 321: * beginning of next line, unless line is empty or at
! 322: * end of file.
1.5 millert 323: */
1.35 ! tb 324: if (clp != curbp->b_headp && llength(clp) != 0) {
1.5 millert 325: clp = lforw(clp);
1.29 florian 326: tdotline++;
1.5 millert 327: tbo = 0;
328: }
329: /*
330: * Note this loop does not process the last line, but this editor
331: * always makes the last line empty so this is good.
332: */
1.24 kjell 333: while (clp != (curbp->b_headp)) {
1.35 ! tb 334: re_flags = REG_STARTEND;
! 335: if (tbo != 0)
! 336: re_flags |= REG_NOTBOL;
1.28 jasper 337: regex_match[0].rm_so = tbo;
338: regex_match[0].rm_eo = llength(clp);
1.34 tb 339: error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "",
1.35 ! tb 340: RE_NMATCH, regex_match, re_flags);
1.6 millert 341: if (error != 0) {
1.5 millert 342: clp = lforw(clp);
1.29 florian 343: tdotline++;
1.5 millert 344: tbo = 0;
345: } else {
1.28 jasper 346: curwp->w_doto = regex_match[0].rm_eo;
1.5 millert 347: curwp->w_dotp = clp;
1.29 florian 348: curwp->w_dotline = tdotline;
1.25 kjell 349: curwp->w_rflag |= WFMOVE;
1.5 millert 350: return (TRUE);
351: }
352: }
353: return (FALSE);
1.1 deraadt 354: }
355:
356: /*
1.6 millert 357: * This routine does the real work of a backward search. The pattern is sitting
1.9 mickey 358: * in the external variable "re_pat". If found, dot is updated, the window
359: * system is notified of the change, and TRUE is returned. If the string isn't
1.6 millert 360: * found, FALSE is returned.
1.1 deraadt 361: */
1.6 millert 362: static int
1.12 cloder 363: re_backsrch(void)
1.5 millert 364: {
1.21 deraadt 365: struct line *clp;
1.29 florian 366: int tbo, tdotline;
1.6 millert 367: regmatch_t lastmatch;
1.5 millert 368:
369: clp = curwp->w_dotp;
370: tbo = curwp->w_doto;
1.29 florian 371: tdotline = curwp->w_dotline;
1.5 millert 372:
373: /* Start search one position to the left of dot */
374: tbo = tbo - 1;
375: if (tbo < 0) {
376: /* must move up one line */
377: clp = lback(clp);
1.29 florian 378: tdotline--;
1.5 millert 379: tbo = llength(clp);
380: }
1.6 millert 381:
1.5 millert 382: /*
383: * Note this loop does not process the last line, but this editor
384: * always makes the last line empty so this is good.
385: */
1.24 kjell 386: while (clp != (curbp->b_headp)) {
1.28 jasper 387: regex_match[0].rm_so = 0;
388: regex_match[0].rm_eo = llength(clp);
1.5 millert 389: lastmatch.rm_so = -1;
390: /*
391: * Keep searching until we don't match any longer. Assumes a
1.28 jasper 392: * non-match does not modify the regex_match array. We have to
1.5 millert 393: * do this character-by-character after the first match since
394: * POSIX regexps don't give you a way to do reverse matches.
395: */
1.34 tb 396: while (!regexec(®ex_buff, ltext(clp) ? ltext(clp) : "",
397: RE_NMATCH, regex_match, REG_STARTEND) &&
398: regex_match[0].rm_so <= tbo) {
1.28 jasper 399: memcpy(&lastmatch, ®ex_match[0], sizeof(regmatch_t));
400: regex_match[0].rm_so++;
401: regex_match[0].rm_eo = llength(clp);
1.5 millert 402: }
403: if (lastmatch.rm_so == -1) {
404: clp = lback(clp);
1.29 florian 405: tdotline--;
1.5 millert 406: tbo = llength(clp);
407: } else {
1.28 jasper 408: memcpy(®ex_match[0], &lastmatch, sizeof(regmatch_t));
409: curwp->w_doto = regex_match[0].rm_so;
1.5 millert 410: curwp->w_dotp = clp;
1.29 florian 411: curwp->w_dotline = tdotline;
1.25 kjell 412: curwp->w_rflag |= WFMOVE;
1.5 millert 413: return (TRUE);
414: }
415: }
416: return (FALSE);
1.1 deraadt 417: }
418:
419: /*
420: * Read a pattern.
421: * Stash it in the external variable "re_pat". The "pat" is
422: * not updated if the user types in an empty line. If the user typed
423: * an empty line, and there is no old pattern, it is an error.
424: * Display the old pattern, in the style of Jeff Lomicka. There is
425: * some do-it-yourself control expansion.
426: */
1.6 millert 427: static int
1.32 florian 428: re_readpattern(char *re_prompt)
1.5 millert 429: {
1.6 millert 430: static int dofree = 0;
1.13 vincent 431: int flags, error, s;
432: char tpat[NPAT], *rep;
1.5 millert 433:
434: if (re_pat[0] == '\0')
1.32 florian 435: rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, re_prompt);
1.5 millert 436: else
1.33 bcallah 437: rep = eread("%s (default %s): ", tpat, NPAT,
1.32 florian 438: EFNUL | EFNEW | EFCR, re_prompt, re_pat);
1.18 kjell 439: if (rep == NULL)
440: return (ABORT);
441: if (rep[0] != '\0') {
1.5 millert 442: /* New pattern given */
1.14 db 443: (void)strlcpy(re_pat, tpat, sizeof(re_pat));
1.5 millert 444: if (casefoldsearch)
445: flags = REG_EXTENDED | REG_ICASE;
446: else
447: flags = REG_EXTENDED;
448: if (dofree)
1.28 jasper 449: regfree(®ex_buff);
450: error = regcomp(®ex_buff, re_pat, flags);
1.6 millert 451: if (error != 0) {
452: char message[256];
1.28 jasper 453: regerror(error, ®ex_buff, message, sizeof(message));
1.30 lum 454: dobeep();
1.5 millert 455: ewprintf("Regex Error: %s", message);
456: re_pat[0] = '\0';
457: return (FALSE);
458: }
459: dofree = 1;
1.13 vincent 460: s = TRUE;
461: } else if (rep[0] == '\0' && re_pat[0] != '\0')
1.5 millert 462: /* Just using old pattern */
463: s = TRUE;
1.13 vincent 464: else
465: s = FALSE;
1.1 deraadt 466: return (s);
467: }
468:
1.5 millert 469: /*
470: * Cause case to not matter in searches. This is the default. If called
471: * with argument cause case to matter.
1.1 deraadt 472: */
1.22 kjell 473: /* ARGSUSED*/
1.6 millert 474: int
1.12 cloder 475: setcasefold(int f, int n)
1.5 millert 476: {
477: if (f & FFARG) {
478: casefoldsearch = FALSE;
479: ewprintf("Case-fold-search unset");
480: } else {
481: casefoldsearch = TRUE;
482: ewprintf("Case-fold-search set");
483: }
1.1 deraadt 484:
1.5 millert 485: /*
486: * Invalidate the regular expression pattern since I'm too lazy to
487: * recompile it.
488: */
489: re_pat[0] = '\0';
490: return (TRUE);
1.6 millert 491: }
1.1 deraadt 492:
1.5 millert 493: /*
1.14 db 494: * Delete all lines after dot that contain a string matching regex.
1.1 deraadt 495: */
1.22 kjell 496: /* ARGSUSED */
1.6 millert 497: int
1.12 cloder 498: delmatchlines(int f, int n)
1.5 millert 499: {
1.6 millert 500: int s;
1.1 deraadt 501:
1.9 mickey 502: if ((s = re_readpattern("Flush lines (containing match for regexp)"))
1.6 millert 503: != TRUE)
1.5 millert 504: return (s);
1.1 deraadt 505:
1.5 millert 506: s = killmatches(TRUE);
507: return (s);
1.1 deraadt 508: }
509:
1.5 millert 510: /*
1.14 db 511: * Delete all lines after dot that don't contain a string matching regex.
1.1 deraadt 512: */
1.22 kjell 513: /* ARGSUSED */
1.6 millert 514: int
1.12 cloder 515: delnonmatchlines(int f, int n)
1.5 millert 516: {
1.6 millert 517: int s;
1.1 deraadt 518:
1.9 mickey 519: if ((s = re_readpattern("Keep lines (containing match for regexp)"))
1.6 millert 520: != TRUE)
1.5 millert 521: return (s);
1.1 deraadt 522:
1.5 millert 523: s = killmatches(FALSE);
524: return (s);
1.1 deraadt 525: }
526:
1.9 mickey 527: /*
1.14 db 528: * This function does the work of deleting matching lines.
1.6 millert 529: */
530: static int
1.12 cloder 531: killmatches(int cond)
1.1 deraadt 532: {
1.6 millert 533: int s, error;
534: int count = 0;
1.21 deraadt 535: struct line *clp;
1.5 millert 536:
537: clp = curwp->w_dotp;
538: if (curwp->w_doto == llength(clp))
539: /* Consider dot on next line */
540: clp = lforw(clp);
541:
1.24 kjell 542: while (clp != (curbp->b_headp)) {
1.5 millert 543: /* see if line matches */
1.28 jasper 544: regex_match[0].rm_so = 0;
545: regex_match[0].rm_eo = llength(clp);
1.34 tb 546: error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "",
547: RE_NMATCH, regex_match, REG_STARTEND);
1.5 millert 548:
549: /* Delete line when appropriate */
550: if ((cond == FALSE && error) || (cond == TRUE && !error)) {
551: curwp->w_doto = 0;
552: curwp->w_dotp = clp;
553: count++;
554: s = ldelete(llength(clp) + 1, KNONE);
555: clp = curwp->w_dotp;
1.25 kjell 556: curwp->w_rflag |= WFMOVE;
1.5 millert 557: if (s == FALSE)
558: return (FALSE);
559: } else
560: clp = lforw(clp);
561: }
1.1 deraadt 562:
1.5 millert 563: ewprintf("%d line(s) deleted", count);
564: if (count > 0)
1.25 kjell 565: curwp->w_rflag |= WFMOVE;
1.1 deraadt 566:
1.5 millert 567: return (TRUE);
1.1 deraadt 568: }
569:
1.5 millert 570: /*
1.14 db 571: * Count lines matching regex.
1.1 deraadt 572: */
1.22 kjell 573: /* ARGSUSED */
1.6 millert 574: int
1.12 cloder 575: cntmatchlines(int f, int n)
1.5 millert 576: {
1.6 millert 577: int s;
1.1 deraadt 578:
1.5 millert 579: if ((s = re_readpattern("Count lines (matching regexp)")) != TRUE)
580: return (s);
581: s = countmatches(TRUE);
1.14 db 582:
1.5 millert 583: return (s);
1.1 deraadt 584: }
585:
1.5 millert 586: /*
1.14 db 587: * Count lines that fail to match regex.
1.1 deraadt 588: */
1.22 kjell 589: /* ARGSUSED */
1.6 millert 590: int
1.12 cloder 591: cntnonmatchlines(int f, int n)
1.5 millert 592: {
1.6 millert 593: int s;
1.1 deraadt 594:
1.5 millert 595: if ((s = re_readpattern("Count lines (not matching regexp)")) != TRUE)
596: return (s);
597: s = countmatches(FALSE);
1.1 deraadt 598:
1.5 millert 599: return (s);
1.1 deraadt 600: }
601:
1.6 millert 602: /*
603: * This function does the work of counting matching lines.
604: */
605: int
1.12 cloder 606: countmatches(int cond)
1.1 deraadt 607: {
1.6 millert 608: int error;
609: int count = 0;
1.21 deraadt 610: struct line *clp;
1.5 millert 611:
612: clp = curwp->w_dotp;
613: if (curwp->w_doto == llength(clp))
614: /* Consider dot on next line */
615: clp = lforw(clp);
616:
1.24 kjell 617: while (clp != (curbp->b_headp)) {
1.5 millert 618: /* see if line matches */
1.28 jasper 619: regex_match[0].rm_so = 0;
620: regex_match[0].rm_eo = llength(clp);
1.34 tb 621: error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "",
622: RE_NMATCH, regex_match, REG_STARTEND);
1.5 millert 623:
624: /* Count line when appropriate */
625: if ((cond == FALSE && error) || (cond == TRUE && !error))
626: count++;
627: clp = lforw(clp);
628: }
1.1 deraadt 629:
1.5 millert 630: if (cond)
631: ewprintf("Number of lines matching: %d", count);
632: else
633: ewprintf("Number of lines not matching: %d", count);
634:
635: return (TRUE);
1.1 deraadt 636: }
1.6 millert 637: #endif /* REGEX */