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