brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 02807b9 Raw
313 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "string2.h"3#include "strfilter.h"4 5#include <errno.h>6#include <stdlib.h>7#include <linux/ctype.h>8#include <linux/string.h>9#include <linux/zalloc.h>10 11/* Operators */12static const char *OP_and	= "&";	/* Logical AND */13static const char *OP_or	= "|";	/* Logical OR */14static const char *OP_not	= "!";	/* Logical NOT */15 16#define is_operator(c)	((c) == '|' || (c) == '&' || (c) == '!')17#define is_separator(c)	(is_operator(c) || (c) == '(' || (c) == ')')18 19static void strfilter_node__delete(struct strfilter_node *node)20{21	if (node) {22		if (node->p && !is_operator(*node->p))23			zfree((char **)&node->p);24		strfilter_node__delete(node->l);25		strfilter_node__delete(node->r);26		free(node);27	}28}29 30void strfilter__delete(struct strfilter *filter)31{32	if (filter) {33		strfilter_node__delete(filter->root);34		free(filter);35	}36}37 38static const char *get_token(const char *s, const char **e)39{40	const char *p;41 42	s = skip_spaces(s);43 44	if (*s == '\0') {45		p = s;46		goto end;47	}48 49	p = s + 1;50	if (!is_separator(*s)) {51		/* End search */52retry:53		while (*p && !is_separator(*p) && !isspace(*p))54			p++;55		/* Escape and special case: '!' is also used in glob pattern */56		if (*(p - 1) == '\\' || (*p == '!' && *(p - 1) == '[')) {57			p++;58			goto retry;59		}60	}61end:62	*e = p;63	return s;64}65 66static struct strfilter_node *strfilter_node__alloc(const char *op,67						    struct strfilter_node *l,68						    struct strfilter_node *r)69{70	struct strfilter_node *node = zalloc(sizeof(*node));71 72	if (node) {73		node->p = op;74		node->l = l;75		node->r = r;76	}77 78	return node;79}80 81static struct strfilter_node *strfilter_node__new(const char *s,82						  const char **ep)83{84	struct strfilter_node root, *cur, *last_op;85	const char *e;86 87	if (!s)88		return NULL;89 90	memset(&root, 0, sizeof(root));91	last_op = cur = &root;92 93	s = get_token(s, &e);94	while (*s != '\0' && *s != ')') {95		switch (*s) {96		case '&':	/* Exchg last OP->r with AND */97			if (!cur->r || !last_op->r)98				goto error;99			cur = strfilter_node__alloc(OP_and, last_op->r, NULL);100			if (!cur)101				goto nomem;102			last_op->r = cur;103			last_op = cur;104			break;105		case '|':	/* Exchg the root with OR */106			if (!cur->r || !root.r)107				goto error;108			cur = strfilter_node__alloc(OP_or, root.r, NULL);109			if (!cur)110				goto nomem;111			root.r = cur;112			last_op = cur;113			break;114		case '!':	/* Add NOT as a leaf node */115			if (cur->r)116				goto error;117			cur->r = strfilter_node__alloc(OP_not, NULL, NULL);118			if (!cur->r)119				goto nomem;120			cur = cur->r;121			break;122		case '(':	/* Recursively parses inside the parenthesis */123			if (cur->r)124				goto error;125			cur->r = strfilter_node__new(s + 1, &s);126			if (!s)127				goto nomem;128			if (!cur->r || *s != ')')129				goto error;130			e = s + 1;131			break;132		default:133			if (cur->r)134				goto error;135			cur->r = strfilter_node__alloc(NULL, NULL, NULL);136			if (!cur->r)137				goto nomem;138			cur->r->p = strndup(s, e - s);139			if (!cur->r->p)140				goto nomem;141		}142		s = get_token(e, &e);143	}144	if (!cur->r)145		goto error;146	*ep = s;147	return root.r;148nomem:149	s = NULL;150error:151	*ep = s;152	strfilter_node__delete(root.r);153	return NULL;154}155 156/*157 * Parse filter rule and return new strfilter.158 * Return NULL if fail, and *ep == NULL if memory allocation failed.159 */160struct strfilter *strfilter__new(const char *rules, const char **err)161{162	struct strfilter *filter = zalloc(sizeof(*filter));163	const char *ep = NULL;164 165	if (filter)166		filter->root = strfilter_node__new(rules, &ep);167 168	if (!filter || !filter->root || *ep != '\0') {169		if (err)170			*err = ep;171		strfilter__delete(filter);172		filter = NULL;173	}174 175	return filter;176}177 178static int strfilter__append(struct strfilter *filter, bool _or,179			     const char *rules, const char **err)180{181	struct strfilter_node *right, *root;182	const char *ep = NULL;183 184	if (!filter || !rules)185		return -EINVAL;186 187	right = strfilter_node__new(rules, &ep);188	if (!right || *ep != '\0') {189		if (err)190			*err = ep;191		goto error;192	}193	root = strfilter_node__alloc(_or ? OP_or : OP_and, filter->root, right);194	if (!root) {195		ep = NULL;196		goto error;197	}198 199	filter->root = root;200	return 0;201 202error:203	strfilter_node__delete(right);204	return ep ? -EINVAL : -ENOMEM;205}206 207int strfilter__or(struct strfilter *filter, const char *rules, const char **err)208{209	return strfilter__append(filter, true, rules, err);210}211 212int strfilter__and(struct strfilter *filter, const char *rules,213		   const char **err)214{215	return strfilter__append(filter, false, rules, err);216}217 218static bool strfilter_node__compare(struct strfilter_node *node,219				    const char *str)220{221	if (!node || !node->p)222		return false;223 224	switch (*node->p) {225	case '|':	/* OR */226		return strfilter_node__compare(node->l, str) ||227			strfilter_node__compare(node->r, str);228	case '&':	/* AND */229		return strfilter_node__compare(node->l, str) &&230			strfilter_node__compare(node->r, str);231	case '!':	/* NOT */232		return !strfilter_node__compare(node->r, str);233	default:234		return strglobmatch(str, node->p);235	}236}237 238/* Return true if STR matches the filter rules */239bool strfilter__compare(struct strfilter *filter, const char *str)240{241	if (!filter)242		return false;243	return strfilter_node__compare(filter->root, str);244}245 246static int strfilter_node__sprint(struct strfilter_node *node, char *buf);247 248/* sprint node in parenthesis if needed */249static int strfilter_node__sprint_pt(struct strfilter_node *node, char *buf)250{251	int len;252	int pt = node->r ? 2 : 0;	/* don't need to check node->l */253 254	if (buf && pt)255		*buf++ = '(';256	len = strfilter_node__sprint(node, buf);257	if (len < 0)258		return len;259	if (buf && pt)260		*(buf + len) = ')';261	return len + pt;262}263 264static int strfilter_node__sprint(struct strfilter_node *node, char *buf)265{266	int len = 0, rlen;267 268	if (!node || !node->p)269		return -EINVAL;270 271	switch (*node->p) {272	case '|':273	case '&':274		len = strfilter_node__sprint_pt(node->l, buf);275		if (len < 0)276			return len;277		fallthrough;278	case '!':279		if (buf) {280			*(buf + len++) = *node->p;281			buf += len;282		} else283			len++;284		rlen = strfilter_node__sprint_pt(node->r, buf);285		if (rlen < 0)286			return rlen;287		len += rlen;288		break;289	default:290		len = strlen(node->p);291		if (buf)292			strcpy(buf, node->p);293	}294 295	return len;296}297 298char *strfilter__string(struct strfilter *filter)299{300	int len;301	char *ret = NULL;302 303	len = strfilter_node__sprint(filter->root, NULL);304	if (len < 0)305		return NULL;306 307	ret = malloc(len + 1);308	if (ret)309		strfilter_node__sprint(filter->root, ret);310 311	return ret;312}313