summaryrefslogtreecommitdiffstats
path: root/usr.bin/less/pattern.c
blob: 6e98ff28b0e90eac85e74107c68037a4db0a94a7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (C) 1984-2012  Mark Nudelman
 * Modified for use with illumos by Garrett D'Amore.
 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Less License, as specified in the README file.
 *
 * For more information, see the README file.
 */

/*
 * Routines to do pattern matching.
 */

#include "less.h"
#include "pattern.h"

extern int caseless;
extern int less_is_more;

/*
 * Compile a search pattern, for future use by match_pattern.
 */
static int
compile_pattern2(char *pattern, int search_type, regex_t **comp_pattern)
{
	regex_t *comp;

	if (search_type & SRCH_NO_REGEX)
		return (0);
	comp = ecalloc(1, sizeof (regex_t));
	if (regcomp(comp, pattern, less_is_more ? 0 : REGCOMP_FLAG)) {
		free(comp);
		error("Invalid pattern", NULL);
		return (-1);
	}
	if (*comp_pattern != NULL)
		regfree(*comp_pattern);
	*comp_pattern = comp;
	return (0);
}

/*
 * Like compile_pattern2, but convert the pattern to lowercase if necessary.
 */
int
compile_pattern(char *pattern, int search_type, regex_t **comp_pattern)
{
	char *cvt_pattern;
	int result;

	if (caseless != OPT_ONPLUS) {
		cvt_pattern = pattern;
	} else {
		cvt_pattern = ecalloc(1, cvt_length(strlen(pattern)));
		cvt_text(cvt_pattern, pattern, NULL, NULL, CVT_TO_LC);
	}
	result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
	if (cvt_pattern != pattern)
		free(cvt_pattern);
	return (result);
}

/*
 * Forget that we have a compiled pattern.
 */
void
uncompile_pattern(regex_t **pattern)
{
	if (*pattern != NULL)
		regfree(*pattern);
	*pattern = NULL;
}

/*
 * Simple pattern matching function.
 * It supports no metacharacters like *, etc.
 */
static int
match(char *pattern, int pattern_len, char *buf, int buf_len,
    char **pfound, char **pend)
{
	char *pp, *lp;
	char *pattern_end = pattern + pattern_len;
	char *buf_end = buf + buf_len;

	for (; buf < buf_end; buf++) {
		for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
			if (pp == pattern_end || lp == buf_end)
				break;
		if (pp == pattern_end) {
			if (pfound != NULL)
				*pfound = buf;
			if (pend != NULL)
				*pend = lp;
			return (1);
		}
	}
	return (0);
}

/*
 * Perform a pattern match with the previously compiled pattern.
 * Set sp and ep to the start and end of the matched string.
 */
int
match_pattern(void *pattern, char *tpattern, char *line, int line_len,
    char **sp, char **ep, int notbol, int search_type)
{
	int matched;
	regex_t *spattern = (regex_t *)pattern;

	if (search_type & SRCH_NO_REGEX) {
		matched = match(tpattern, strlen(tpattern), line, line_len,
		    sp, ep);
	} else {
		regmatch_t rm;
		int flags = (notbol) ? REG_NOTBOL : 0;
#ifdef	REG_STARTEND
		flags |= REG_STARTEND;
		rm.rm_so = 0;
		rm.rm_eo = line_len;
#endif
		*sp = NULL;
		*ep = NULL;
		matched = !regexec(spattern, line, 1, &rm, flags);
		if (matched) {
			*sp = line + rm.rm_so;
			*ep = line + rm.rm_eo;
		}
	}
	matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
	    ((search_type & SRCH_NO_MATCH) && !matched);
	return (matched);
}