1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2012 Petr Machata, Red Hat Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 */
20
21#include <stdlib.h>
22#include <assert.h>
23#include <stdio.h>
24#include <string.h>
25
26#include "filter.h"
27#include "library.h"
28#include "callback.h"
29
30void
31filter_init(struct filter *filt)
32{
33	filt->rules = NULL;
34	filt->next = NULL;
35}
36
37void
38filter_destroy(struct filter *filt)
39{
40	struct filter_rule *it;
41	for (it = filt->rules; it != NULL; ) {
42		struct filter_rule *next = it->next;
43		filter_rule_destroy(it);
44		it = next;
45	}
46}
47
48void
49filter_rule_init(struct filter_rule *rule, enum filter_rule_type type,
50		 struct filter_lib_matcher *matcher,
51		 regex_t symbol_re)
52{
53	rule->type = type;
54	rule->lib_matcher = matcher;
55	rule->symbol_re = symbol_re;
56	rule->next = NULL;
57}
58
59void
60filter_rule_destroy(struct filter_rule *rule)
61{
62	filter_lib_matcher_destroy(rule->lib_matcher);
63	regfree(&rule->symbol_re);
64}
65
66void
67filter_add_rule(struct filter *filt, struct filter_rule *rule)
68{
69	struct filter_rule **rulep;
70	for (rulep = &filt->rules; *rulep != NULL; rulep = &(*rulep)->next)
71		;
72	*rulep = rule;
73}
74
75void
76filter_lib_matcher_name_init(struct filter_lib_matcher *matcher,
77			     enum filter_lib_matcher_type type,
78			     regex_t libname_re)
79{
80	switch (type) {
81	case FLM_MAIN:
82		assert(type != type);
83		abort();
84
85	case FLM_SONAME:
86	case FLM_PATHNAME:
87		matcher->type = type;
88		matcher->libname_re = libname_re;
89	}
90}
91
92void
93filter_lib_matcher_main_init(struct filter_lib_matcher *matcher)
94{
95	matcher->type = FLM_MAIN;
96}
97
98void
99filter_lib_matcher_destroy(struct filter_lib_matcher *matcher)
100{
101	switch (matcher->type) {
102	case FLM_SONAME:
103	case FLM_PATHNAME:
104		regfree(&matcher->libname_re);
105		break;
106	case FLM_MAIN:
107		break;
108	}
109}
110
111static int
112re_match_or_error(regex_t *re, const char *name, const char *what)
113{
114	int status = regexec(re, name, 0, NULL, 0);
115	if (status == 0)
116		return 1;
117	if (status == REG_NOMATCH)
118		return 0;
119
120	char buf[200];
121	regerror(status, re, buf, sizeof buf);
122	fprintf(stderr, "Error when matching %s: %s\n", name, buf);
123
124	return 0;
125}
126
127static int
128matcher_matches_library(struct filter_lib_matcher *matcher, struct library *lib)
129{
130	switch (matcher->type) {
131	case FLM_SONAME:
132		return re_match_or_error(&matcher->libname_re, lib->soname,
133					 "library soname");
134	case FLM_PATHNAME:
135		return re_match_or_error(&matcher->libname_re, lib->pathname,
136					 "library pathname");
137	case FLM_MAIN:
138		return lib->type == LT_LIBTYPE_MAIN;
139	}
140	assert(matcher->type != matcher->type);
141	abort();
142}
143
144int
145filter_matches_library(struct filter *filt, struct library *lib)
146{
147	if (filt == NULL)
148		return 0;
149
150	for (; filt != NULL; filt = filt->next) {
151		struct filter_rule *it;
152		for (it = filt->rules; it != NULL; it = it->next)
153			switch (it->type) {
154			case FR_ADD:
155				if (matcher_matches_library(it->lib_matcher, lib))
156					return 1;
157			case FR_SUBTRACT:
158				continue;
159			};
160	}
161	return 0;
162}
163
164int
165filter_matches_symbol(struct filter *filt,
166		      const char *sym_name, struct library *lib)
167{
168	for (; filt != NULL; filt = filt->next) {
169		int matches = 0;
170		struct filter_rule *it;
171		for (it = filt->rules; it != NULL; it = it->next) {
172			switch (it->type) {
173			case FR_ADD:
174				if (matches)
175					continue;
176				break;
177			case FR_SUBTRACT:
178				if (!matches)
179					continue;
180			}
181
182			if (matcher_matches_library(it->lib_matcher, lib)
183			    && re_match_or_error(&it->symbol_re, sym_name,
184						 "symbol name"))
185				matches = !matches;
186		}
187		if (matches)
188			return 1;
189	}
190	return 0;
191}
192