18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * lib/ts_fsm.c	   A naive finite state machine text search approach
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Thomas Graf <tgraf@suug.ch>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * ==========================================================================
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *   A finite state machine consists of n states (struct ts_fsm_token)
108c2ecf20Sopenharmony_ci *   representing the pattern as a finite automaton. The data is read
118c2ecf20Sopenharmony_ci *   sequentially on an octet basis. Every state token specifies the number
128c2ecf20Sopenharmony_ci *   of recurrences and the type of value accepted which can be either a
138c2ecf20Sopenharmony_ci *   specific character or ctype based set of characters. The available
148c2ecf20Sopenharmony_ci *   type of recurrences include 1, (0|1), [0 n], and [1 n].
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *   The algorithm differs between strict/non-strict mode specifying
178c2ecf20Sopenharmony_ci *   whether the pattern has to start at the first octet. Strict mode
188c2ecf20Sopenharmony_ci *   is enabled by default and can be disabled by inserting
198c2ecf20Sopenharmony_ci *   TS_FSM_HEAD_IGNORE as the first token in the chain.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *   The runtime performance of the algorithm should be around O(n),
228c2ecf20Sopenharmony_ci *   however while in strict mode the average runtime can be better.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/types.h>
278c2ecf20Sopenharmony_ci#include <linux/string.h>
288c2ecf20Sopenharmony_ci#include <linux/ctype.h>
298c2ecf20Sopenharmony_ci#include <linux/textsearch.h>
308c2ecf20Sopenharmony_ci#include <linux/textsearch_fsm.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct ts_fsm
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	unsigned int		ntokens;
358c2ecf20Sopenharmony_ci	struct ts_fsm_token	tokens[];
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* other values derived from ctype.h */
398c2ecf20Sopenharmony_ci#define _A		0x100 /* ascii */
408c2ecf20Sopenharmony_ci#define _W		0x200 /* wildcard */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Map to _ctype flags and some magic numbers */
438c2ecf20Sopenharmony_cistatic const u16 token_map[TS_FSM_TYPE_MAX+1] = {
448c2ecf20Sopenharmony_ci	[TS_FSM_SPECIFIC] = 0,
458c2ecf20Sopenharmony_ci	[TS_FSM_WILDCARD] = _W,
468c2ecf20Sopenharmony_ci	[TS_FSM_CNTRL]	  = _C,
478c2ecf20Sopenharmony_ci	[TS_FSM_LOWER]	  = _L,
488c2ecf20Sopenharmony_ci	[TS_FSM_UPPER]	  = _U,
498c2ecf20Sopenharmony_ci	[TS_FSM_PUNCT]	  = _P,
508c2ecf20Sopenharmony_ci	[TS_FSM_SPACE]	  = _S,
518c2ecf20Sopenharmony_ci	[TS_FSM_DIGIT]	  = _D,
528c2ecf20Sopenharmony_ci	[TS_FSM_XDIGIT]	  = _D | _X,
538c2ecf20Sopenharmony_ci	[TS_FSM_ALPHA]	  = _U | _L,
548c2ecf20Sopenharmony_ci	[TS_FSM_ALNUM]	  = _U | _L | _D,
558c2ecf20Sopenharmony_ci	[TS_FSM_PRINT]	  = _P | _U | _L | _D | _SP,
568c2ecf20Sopenharmony_ci	[TS_FSM_GRAPH]	  = _P | _U | _L | _D,
578c2ecf20Sopenharmony_ci	[TS_FSM_ASCII]	  = _A,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const u16 token_lookup_tbl[256] = {
618c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*   0-  3 */
628c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*   4-  7 */
638c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C|_S,  _W|_A|_C|_S,  _W|_A|_C|_S,		/*   8- 11 */
648c2ecf20Sopenharmony_ci_W|_A|_C|_S,   _W|_A|_C|_S,  _W|_A|_C,     _W|_A|_C,		/*  12- 15 */
658c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  16- 19 */
668c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  20- 23 */
678c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  24- 27 */
688c2ecf20Sopenharmony_ci_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  28- 31 */
698c2ecf20Sopenharmony_ci_W|_A|_S|_SP,  _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  32- 35 */
708c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  36- 39 */
718c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  40- 43 */
728c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  44- 47 */
738c2ecf20Sopenharmony_ci_W|_A|_D,      _W|_A|_D,     _W|_A|_D,     _W|_A|_D,		/*  48- 51 */
748c2ecf20Sopenharmony_ci_W|_A|_D,      _W|_A|_D,     _W|_A|_D,     _W|_A|_D,		/*  52- 55 */
758c2ecf20Sopenharmony_ci_W|_A|_D,      _W|_A|_D,     _W|_A|_P,     _W|_A|_P,		/*  56- 59 */
768c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  60- 63 */
778c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_U|_X,  _W|_A|_U|_X,  _W|_A|_U|_X,		/*  64- 67 */
788c2ecf20Sopenharmony_ci_W|_A|_U|_X,   _W|_A|_U|_X,  _W|_A|_U|_X,  _W|_A|_U,		/*  68- 71 */
798c2ecf20Sopenharmony_ci_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  72- 75 */
808c2ecf20Sopenharmony_ci_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  76- 79 */
818c2ecf20Sopenharmony_ci_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  80- 83 */
828c2ecf20Sopenharmony_ci_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  84- 87 */
838c2ecf20Sopenharmony_ci_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_P,		/*  88- 91 */
848c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  92- 95 */
858c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_L|_X,  _W|_A|_L|_X,  _W|_A|_L|_X,		/*  96- 99 */
868c2ecf20Sopenharmony_ci_W|_A|_L|_X,   _W|_A|_L|_X,  _W|_A|_L|_X,  _W|_A|_L,		/* 100-103 */
878c2ecf20Sopenharmony_ci_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 104-107 */
888c2ecf20Sopenharmony_ci_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 108-111 */
898c2ecf20Sopenharmony_ci_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 112-115 */
908c2ecf20Sopenharmony_ci_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 116-119 */
918c2ecf20Sopenharmony_ci_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_P,		/* 120-123 */
928c2ecf20Sopenharmony_ci_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_C,		/* 124-127 */
938c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 128-131 */
948c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 132-135 */
958c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 136-139 */
968c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 140-143 */
978c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 144-147 */
988c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 148-151 */
998c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 152-155 */
1008c2ecf20Sopenharmony_ci_W,            _W,           _W,           _W,			/* 156-159 */
1018c2ecf20Sopenharmony_ci_W|_S|_SP,     _W|_P,        _W|_P,        _W|_P,		/* 160-163 */
1028c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 164-167 */
1038c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 168-171 */
1048c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 172-175 */
1058c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 176-179 */
1068c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 180-183 */
1078c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 184-187 */
1088c2ecf20Sopenharmony_ci_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 188-191 */
1098c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 192-195 */
1108c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 196-199 */
1118c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 200-203 */
1128c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 204-207 */
1138c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 208-211 */
1148c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_P,		/* 212-215 */
1158c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 216-219 */
1168c2ecf20Sopenharmony_ci_W|_U,         _W|_U,        _W|_U,        _W|_L,		/* 220-223 */
1178c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 224-227 */
1188c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 228-231 */
1198c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 232-235 */
1208c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 236-239 */
1218c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 240-243 */
1228c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_P,		/* 244-247 */
1238c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 248-251 */
1248c2ecf20Sopenharmony_ci_W|_L,         _W|_L,        _W|_L,        _W|_L};		/* 252-255 */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic inline int match_token(struct ts_fsm_token *t, u8 d)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	if (t->type)
1298c2ecf20Sopenharmony_ci		return (token_lookup_tbl[d] & t->type) != 0;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		return t->value == d;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic unsigned int fsm_find(struct ts_config *conf, struct ts_state *state)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct ts_fsm *fsm = ts_config_priv(conf);
1378c2ecf20Sopenharmony_ci	struct ts_fsm_token *cur = NULL, *next;
1388c2ecf20Sopenharmony_ci	unsigned int match_start, block_idx = 0, tok_idx;
1398c2ecf20Sopenharmony_ci	unsigned block_len = 0, strict, consumed = state->offset;
1408c2ecf20Sopenharmony_ci	const u8 *data;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#define GET_NEXT_BLOCK()		\
1438c2ecf20Sopenharmony_ci({	consumed += block_idx;		\
1448c2ecf20Sopenharmony_ci	block_idx = 0;			\
1458c2ecf20Sopenharmony_ci	block_len = conf->get_next_block(consumed, &data, conf, state); })
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#define TOKEN_MISMATCH()		\
1488c2ecf20Sopenharmony_ci	do {				\
1498c2ecf20Sopenharmony_ci		if (strict)		\
1508c2ecf20Sopenharmony_ci			goto no_match;	\
1518c2ecf20Sopenharmony_ci		block_idx++;		\
1528c2ecf20Sopenharmony_ci		goto startover;		\
1538c2ecf20Sopenharmony_ci	} while(0)
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#define end_of_data() unlikely(block_idx >= block_len && !GET_NEXT_BLOCK())
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (end_of_data())
1588c2ecf20Sopenharmony_ci		goto no_match;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	strict = fsm->tokens[0].recur != TS_FSM_HEAD_IGNORE;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistartover:
1638c2ecf20Sopenharmony_ci	match_start = consumed + block_idx;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (tok_idx = 0; tok_idx < fsm->ntokens; tok_idx++) {
1668c2ecf20Sopenharmony_ci		cur = &fsm->tokens[tok_idx];
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		if (likely(tok_idx < (fsm->ntokens - 1)))
1698c2ecf20Sopenharmony_ci			next = &fsm->tokens[tok_idx + 1];
1708c2ecf20Sopenharmony_ci		else
1718c2ecf20Sopenharmony_ci			next = NULL;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		switch (cur->recur) {
1748c2ecf20Sopenharmony_ci		case TS_FSM_SINGLE:
1758c2ecf20Sopenharmony_ci			if (end_of_data())
1768c2ecf20Sopenharmony_ci				goto no_match;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			if (!match_token(cur, data[block_idx]))
1798c2ecf20Sopenharmony_ci				TOKEN_MISMATCH();
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		case TS_FSM_PERHAPS:
1838c2ecf20Sopenharmony_ci			if (end_of_data() ||
1848c2ecf20Sopenharmony_ci			    !match_token(cur, data[block_idx]))
1858c2ecf20Sopenharmony_ci				continue;
1868c2ecf20Sopenharmony_ci			break;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		case TS_FSM_MULTI:
1898c2ecf20Sopenharmony_ci			if (end_of_data())
1908c2ecf20Sopenharmony_ci				goto no_match;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci			if (!match_token(cur, data[block_idx]))
1938c2ecf20Sopenharmony_ci				TOKEN_MISMATCH();
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			block_idx++;
1968c2ecf20Sopenharmony_ci			/* fall through */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		case TS_FSM_ANY:
1998c2ecf20Sopenharmony_ci			if (next == NULL)
2008c2ecf20Sopenharmony_ci				goto found_match;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci			if (end_of_data())
2038c2ecf20Sopenharmony_ci				continue;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			while (!match_token(next, data[block_idx])) {
2068c2ecf20Sopenharmony_ci				if (!match_token(cur, data[block_idx]))
2078c2ecf20Sopenharmony_ci					TOKEN_MISMATCH();
2088c2ecf20Sopenharmony_ci				block_idx++;
2098c2ecf20Sopenharmony_ci				if (end_of_data())
2108c2ecf20Sopenharmony_ci					goto no_match;
2118c2ecf20Sopenharmony_ci			}
2128c2ecf20Sopenharmony_ci			continue;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/*
2158c2ecf20Sopenharmony_ci		 * Optimization: Prefer small local loop over jumping
2168c2ecf20Sopenharmony_ci		 * back and forth until garbage at head is munched.
2178c2ecf20Sopenharmony_ci		 */
2188c2ecf20Sopenharmony_ci		case TS_FSM_HEAD_IGNORE:
2198c2ecf20Sopenharmony_ci			if (end_of_data())
2208c2ecf20Sopenharmony_ci				continue;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci			while (!match_token(next, data[block_idx])) {
2238c2ecf20Sopenharmony_ci				/*
2248c2ecf20Sopenharmony_ci				 * Special case, don't start over upon
2258c2ecf20Sopenharmony_ci				 * a mismatch, give the user the
2268c2ecf20Sopenharmony_ci				 * chance to specify the type of data
2278c2ecf20Sopenharmony_ci				 * allowed to be ignored.
2288c2ecf20Sopenharmony_ci				 */
2298c2ecf20Sopenharmony_ci				if (!match_token(cur, data[block_idx]))
2308c2ecf20Sopenharmony_ci					goto no_match;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci				block_idx++;
2338c2ecf20Sopenharmony_ci				if (end_of_data())
2348c2ecf20Sopenharmony_ci					goto no_match;
2358c2ecf20Sopenharmony_ci			}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci			match_start = consumed + block_idx;
2388c2ecf20Sopenharmony_ci			continue;
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		block_idx++;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (end_of_data())
2458c2ecf20Sopenharmony_ci		goto found_match;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cino_match:
2488c2ecf20Sopenharmony_ci	return UINT_MAX;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cifound_match:
2518c2ecf20Sopenharmony_ci	state->offset = consumed + block_idx;
2528c2ecf20Sopenharmony_ci	return match_start;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic struct ts_config *fsm_init(const void *pattern, unsigned int len,
2568c2ecf20Sopenharmony_ci				    gfp_t gfp_mask, int flags)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	int i, err = -EINVAL;
2598c2ecf20Sopenharmony_ci	struct ts_config *conf;
2608c2ecf20Sopenharmony_ci	struct ts_fsm *fsm;
2618c2ecf20Sopenharmony_ci	struct ts_fsm_token *tokens = (struct ts_fsm_token *) pattern;
2628c2ecf20Sopenharmony_ci	unsigned int ntokens = len / sizeof(*tokens);
2638c2ecf20Sopenharmony_ci	size_t priv_size = sizeof(*fsm) + len;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (len  % sizeof(struct ts_fsm_token) || ntokens < 1)
2668c2ecf20Sopenharmony_ci		goto errout;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (flags & TS_IGNORECASE)
2698c2ecf20Sopenharmony_ci		goto errout;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	for (i = 0; i < ntokens; i++) {
2728c2ecf20Sopenharmony_ci		struct ts_fsm_token *t = &tokens[i];
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		if (t->type > TS_FSM_TYPE_MAX || t->recur > TS_FSM_RECUR_MAX)
2758c2ecf20Sopenharmony_ci			goto errout;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		if (t->recur == TS_FSM_HEAD_IGNORE &&
2788c2ecf20Sopenharmony_ci		    (i != 0 || i == (ntokens - 1)))
2798c2ecf20Sopenharmony_ci			goto errout;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	conf = alloc_ts_config(priv_size, gfp_mask);
2838c2ecf20Sopenharmony_ci	if (IS_ERR(conf))
2848c2ecf20Sopenharmony_ci		return conf;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	conf->flags = flags;
2878c2ecf20Sopenharmony_ci	fsm = ts_config_priv(conf);
2888c2ecf20Sopenharmony_ci	fsm->ntokens = ntokens;
2898c2ecf20Sopenharmony_ci	memcpy(fsm->tokens, pattern, len);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	for (i = 0; i < fsm->ntokens; i++) {
2928c2ecf20Sopenharmony_ci		struct ts_fsm_token *t = &fsm->tokens[i];
2938c2ecf20Sopenharmony_ci		t->type = token_map[t->type];
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return conf;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cierrout:
2998c2ecf20Sopenharmony_ci	return ERR_PTR(err);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void *fsm_get_pattern(struct ts_config *conf)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct ts_fsm *fsm = ts_config_priv(conf);
3058c2ecf20Sopenharmony_ci	return fsm->tokens;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic unsigned int fsm_get_pattern_len(struct ts_config *conf)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct ts_fsm *fsm = ts_config_priv(conf);
3118c2ecf20Sopenharmony_ci	return fsm->ntokens * sizeof(struct ts_fsm_token);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic struct ts_ops fsm_ops = {
3158c2ecf20Sopenharmony_ci	.name		  = "fsm",
3168c2ecf20Sopenharmony_ci	.find		  = fsm_find,
3178c2ecf20Sopenharmony_ci	.init		  = fsm_init,
3188c2ecf20Sopenharmony_ci	.get_pattern	  = fsm_get_pattern,
3198c2ecf20Sopenharmony_ci	.get_pattern_len  = fsm_get_pattern_len,
3208c2ecf20Sopenharmony_ci	.owner		  = THIS_MODULE,
3218c2ecf20Sopenharmony_ci	.list		  = LIST_HEAD_INIT(fsm_ops.list)
3228c2ecf20Sopenharmony_ci};
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int __init init_fsm(void)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	return textsearch_register(&fsm_ops);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void __exit exit_fsm(void)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	textsearch_unregister(&fsm_ops);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cimodule_init(init_fsm);
3378c2ecf20Sopenharmony_cimodule_exit(exit_fsm);
338