162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/ctype.h>
362306a36Sopenharmony_ci#include "spk_types.h"
462306a36Sopenharmony_ci#include "spk_priv.h"
562306a36Sopenharmony_ci#include "speakup.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistatic struct st_var_header var_headers[] = {
862306a36Sopenharmony_ci	{ "version", VERSION, VAR_PROC, NULL, NULL },
962306a36Sopenharmony_ci	{ "synth_name", SYNTH, VAR_PROC, NULL, NULL },
1062306a36Sopenharmony_ci	{ "keymap", KEYMAP, VAR_PROC, NULL, NULL },
1162306a36Sopenharmony_ci	{ "silent", SILENT, VAR_PROC, NULL, NULL },
1262306a36Sopenharmony_ci	{ "punc_some", PUNC_SOME, VAR_PROC, NULL, NULL },
1362306a36Sopenharmony_ci	{ "punc_most", PUNC_MOST, VAR_PROC, NULL, NULL },
1462306a36Sopenharmony_ci	{ "punc_all", PUNC_ALL, VAR_PROC, NULL, NULL },
1562306a36Sopenharmony_ci	{ "delimiters", DELIM, VAR_PROC, NULL, NULL },
1662306a36Sopenharmony_ci	{ "repeats", REPEATS, VAR_PROC, NULL, NULL },
1762306a36Sopenharmony_ci	{ "ex_num", EXNUMBER, VAR_PROC, NULL, NULL },
1862306a36Sopenharmony_ci	{ "characters", CHARS, VAR_PROC, NULL, NULL },
1962306a36Sopenharmony_ci	{ "synth_direct", SYNTH_DIRECT, VAR_PROC, NULL, NULL },
2062306a36Sopenharmony_ci	{ "caps_start", CAPS_START, VAR_STRING, spk_str_caps_start, NULL },
2162306a36Sopenharmony_ci	{ "caps_stop", CAPS_STOP, VAR_STRING, spk_str_caps_stop, NULL },
2262306a36Sopenharmony_ci	{ "delay_time", DELAY, VAR_TIME, NULL, NULL },
2362306a36Sopenharmony_ci	{ "trigger_time", TRIGGER, VAR_TIME, NULL, NULL },
2462306a36Sopenharmony_ci	{ "jiffy_delta", JIFFY, VAR_TIME, NULL, NULL },
2562306a36Sopenharmony_ci	{ "full_time", FULL, VAR_TIME, NULL, NULL },
2662306a36Sopenharmony_ci	{ "flush_time", FLUSH, VAR_TIME, NULL, NULL },
2762306a36Sopenharmony_ci	{ "spell_delay", SPELL_DELAY, VAR_NUM, &spk_spell_delay, NULL },
2862306a36Sopenharmony_ci	{ "bleeps", BLEEPS, VAR_NUM, &spk_bleeps, NULL },
2962306a36Sopenharmony_ci	{ "attrib_bleep", ATTRIB_BLEEP, VAR_NUM, &spk_attrib_bleep, NULL },
3062306a36Sopenharmony_ci	{ "bleep_time", BLEEP_TIME, VAR_TIME, &spk_bleep_time, NULL },
3162306a36Sopenharmony_ci	{ "cursor_time", CURSOR_TIME, VAR_TIME, NULL, NULL },
3262306a36Sopenharmony_ci	{ "punc_level", PUNC_LEVEL, VAR_NUM, &spk_punc_level, NULL },
3362306a36Sopenharmony_ci	{ "reading_punc", READING_PUNC, VAR_NUM, &spk_reading_punc, NULL },
3462306a36Sopenharmony_ci	{ "say_control", SAY_CONTROL, VAR_NUM, &spk_say_ctrl, NULL },
3562306a36Sopenharmony_ci	{ "say_word_ctl", SAY_WORD_CTL, VAR_NUM, &spk_say_word_ctl, NULL },
3662306a36Sopenharmony_ci	{ "no_interrupt", NO_INTERRUPT, VAR_NUM, &spk_no_intr, NULL },
3762306a36Sopenharmony_ci	{ "key_echo", KEY_ECHO, VAR_NUM, &spk_key_echo, NULL },
3862306a36Sopenharmony_ci	{ "bell_pos", BELL_POS, VAR_NUM, &spk_bell_pos, NULL },
3962306a36Sopenharmony_ci	{ "rate", RATE, VAR_NUM, NULL, NULL },
4062306a36Sopenharmony_ci	{ "pitch", PITCH, VAR_NUM, NULL, NULL },
4162306a36Sopenharmony_ci	{ "inflection", INFLECTION, VAR_NUM, NULL, NULL },
4262306a36Sopenharmony_ci	{ "vol", VOL, VAR_NUM, NULL, NULL },
4362306a36Sopenharmony_ci	{ "tone", TONE, VAR_NUM, NULL, NULL },
4462306a36Sopenharmony_ci	{ "punct", PUNCT, VAR_NUM, NULL, NULL   },
4562306a36Sopenharmony_ci	{ "voice", VOICE, VAR_NUM, NULL, NULL },
4662306a36Sopenharmony_ci	{ "freq", FREQUENCY, VAR_NUM, NULL, NULL },
4762306a36Sopenharmony_ci	{ "lang", LANG, VAR_NUM, NULL, NULL },
4862306a36Sopenharmony_ci	{ "chartab", CHARTAB, VAR_PROC, NULL, NULL },
4962306a36Sopenharmony_ci	{ "direct", DIRECT, VAR_NUM, NULL, NULL },
5062306a36Sopenharmony_ci	{ "pause", PAUSE, VAR_STRING, spk_str_pause, NULL },
5162306a36Sopenharmony_ci	{ "cur_phonetic", CUR_PHONETIC, VAR_NUM, &spk_cur_phonetic, NULL },
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic struct st_var_header *var_ptrs[MAXVARS] = { NULL, NULL, NULL };
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct punc_var_t punc_vars[] = {
5762306a36Sopenharmony_ci	{ PUNC_SOME, 1 },
5862306a36Sopenharmony_ci	{ PUNC_MOST, 2 },
5962306a36Sopenharmony_ci	{ PUNC_ALL, 3 },
6062306a36Sopenharmony_ci	{ DELIM, 4 },
6162306a36Sopenharmony_ci	{ REPEATS, 5 },
6262306a36Sopenharmony_ci	{ EXNUMBER, 6 },
6362306a36Sopenharmony_ci	{ -1, -1 },
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint spk_chartab_get_value(char *keyword)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	int value = 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (!strcmp(keyword, "ALPHA"))
7162306a36Sopenharmony_ci		value = ALPHA;
7262306a36Sopenharmony_ci	else if (!strcmp(keyword, "B_CTL"))
7362306a36Sopenharmony_ci		value = B_CTL;
7462306a36Sopenharmony_ci	else if (!strcmp(keyword, "WDLM"))
7562306a36Sopenharmony_ci		value = WDLM;
7662306a36Sopenharmony_ci	else if (!strcmp(keyword, "A_PUNC"))
7762306a36Sopenharmony_ci		value = A_PUNC;
7862306a36Sopenharmony_ci	else if (!strcmp(keyword, "PUNC"))
7962306a36Sopenharmony_ci		value = PUNC;
8062306a36Sopenharmony_ci	else if (!strcmp(keyword, "NUM"))
8162306a36Sopenharmony_ci		value = NUM;
8262306a36Sopenharmony_ci	else if (!strcmp(keyword, "A_CAP"))
8362306a36Sopenharmony_ci		value = A_CAP;
8462306a36Sopenharmony_ci	else if (!strcmp(keyword, "B_CAPSYM"))
8562306a36Sopenharmony_ci		value = B_CAPSYM;
8662306a36Sopenharmony_ci	else if (!strcmp(keyword, "B_SYM"))
8762306a36Sopenharmony_ci		value = B_SYM;
8862306a36Sopenharmony_ci	return value;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_civoid speakup_register_var(struct var_t *var)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	static char nothing[2] = "\0";
9462306a36Sopenharmony_ci	int i;
9562306a36Sopenharmony_ci	struct st_var_header *p_header;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	BUG_ON(!var || var->var_id < 0 || var->var_id >= MAXVARS);
9862306a36Sopenharmony_ci	if (!var_ptrs[0]) {
9962306a36Sopenharmony_ci		for (i = 0; i < MAXVARS; i++) {
10062306a36Sopenharmony_ci			p_header = &var_headers[i];
10162306a36Sopenharmony_ci			var_ptrs[p_header->var_id] = p_header;
10262306a36Sopenharmony_ci			p_header->data = NULL;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	p_header = var_ptrs[var->var_id];
10662306a36Sopenharmony_ci	if (p_header->data)
10762306a36Sopenharmony_ci		return;
10862306a36Sopenharmony_ci	p_header->data = var;
10962306a36Sopenharmony_ci	switch (p_header->var_type) {
11062306a36Sopenharmony_ci	case VAR_STRING:
11162306a36Sopenharmony_ci		spk_set_string_var(nothing, p_header, 0);
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	case VAR_NUM:
11462306a36Sopenharmony_ci	case VAR_TIME:
11562306a36Sopenharmony_ci		spk_set_num_var(0, p_header, E_DEFAULT);
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid speakup_unregister_var(enum var_id_t var_id)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct st_var_header *p_header;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	BUG_ON(var_id < 0 || var_id >= MAXVARS);
12762306a36Sopenharmony_ci	p_header = var_ptrs[var_id];
12862306a36Sopenharmony_ci	p_header->data = NULL;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct st_var_header *spk_get_var_header(enum var_id_t var_id)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct st_var_header *p_header;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (var_id < 0 || var_id >= MAXVARS)
13662306a36Sopenharmony_ci		return NULL;
13762306a36Sopenharmony_ci	p_header = var_ptrs[var_id];
13862306a36Sopenharmony_ci	if (!p_header->data)
13962306a36Sopenharmony_ci		return NULL;
14062306a36Sopenharmony_ci	return p_header;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_get_var_header);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistruct st_var_header *spk_var_header_by_name(const char *name)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int i;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!name)
14962306a36Sopenharmony_ci		return NULL;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	for (i = 0; i < MAXVARS; i++) {
15262306a36Sopenharmony_ci		if (strcmp(name, var_ptrs[i]->name) == 0)
15362306a36Sopenharmony_ci			return var_ptrs[i];
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	return NULL;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistruct var_t *spk_get_var(enum var_id_t var_id)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	BUG_ON(var_id < 0 || var_id >= MAXVARS);
16162306a36Sopenharmony_ci	BUG_ON(!var_ptrs[var_id]);
16262306a36Sopenharmony_ci	return var_ptrs[var_id]->data;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_get_var);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistruct punc_var_t *spk_get_punc_var(enum var_id_t var_id)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct punc_var_t *rv = NULL;
16962306a36Sopenharmony_ci	struct punc_var_t *where;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	where = punc_vars;
17262306a36Sopenharmony_ci	while ((where->var_id != -1) && (!rv)) {
17362306a36Sopenharmony_ci		if (where->var_id == var_id)
17462306a36Sopenharmony_ci			rv = where;
17562306a36Sopenharmony_ci		else
17662306a36Sopenharmony_ci			where++;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	return rv;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* handlers for setting vars */
18262306a36Sopenharmony_ciint spk_set_num_var(int input, struct st_var_header *var, int how)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	int val;
18562306a36Sopenharmony_ci	int *p_val = var->p_val;
18662306a36Sopenharmony_ci	char buf[32];
18762306a36Sopenharmony_ci	char *cp;
18862306a36Sopenharmony_ci	struct var_t *var_data = var->data;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!var_data)
19162306a36Sopenharmony_ci		return -ENODATA;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	val = var_data->u.n.value;
19462306a36Sopenharmony_ci	switch (how) {
19562306a36Sopenharmony_ci	case E_NEW_DEFAULT:
19662306a36Sopenharmony_ci		if (input < var_data->u.n.low || input > var_data->u.n.high)
19762306a36Sopenharmony_ci			return -ERANGE;
19862306a36Sopenharmony_ci		var_data->u.n.default_val = input;
19962306a36Sopenharmony_ci		return 0;
20062306a36Sopenharmony_ci	case E_DEFAULT:
20162306a36Sopenharmony_ci		val = var_data->u.n.default_val;
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case E_SET:
20462306a36Sopenharmony_ci		val = input;
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case E_INC:
20762306a36Sopenharmony_ci		val += input;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case E_DEC:
21062306a36Sopenharmony_ci		val -= input;
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (val < var_data->u.n.low || val > var_data->u.n.high)
21562306a36Sopenharmony_ci		return -ERANGE;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	var_data->u.n.value = val;
21862306a36Sopenharmony_ci	if (var->var_type == VAR_TIME && p_val) {
21962306a36Sopenharmony_ci		*p_val = msecs_to_jiffies(val);
22062306a36Sopenharmony_ci		return 0;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	if (p_val)
22362306a36Sopenharmony_ci		*p_val = val;
22462306a36Sopenharmony_ci	if (var->var_id == PUNC_LEVEL) {
22562306a36Sopenharmony_ci		spk_punc_mask = spk_punc_masks[val];
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci	if (var_data->u.n.multiplier != 0)
22862306a36Sopenharmony_ci		val *= var_data->u.n.multiplier;
22962306a36Sopenharmony_ci	val += var_data->u.n.offset;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (!synth)
23262306a36Sopenharmony_ci		return 0;
23362306a36Sopenharmony_ci	if (synth->synth_adjust && synth->synth_adjust(synth, var))
23462306a36Sopenharmony_ci		return 0;
23562306a36Sopenharmony_ci	if (var->var_id < FIRST_SYNTH_VAR)
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (!var_data->u.n.synth_fmt)
23962306a36Sopenharmony_ci		return 0;
24062306a36Sopenharmony_ci	if (var->var_id == PITCH)
24162306a36Sopenharmony_ci		cp = spk_pitch_buff;
24262306a36Sopenharmony_ci	else
24362306a36Sopenharmony_ci		cp = buf;
24462306a36Sopenharmony_ci	if (!var_data->u.n.out_str)
24562306a36Sopenharmony_ci		sprintf(cp, var_data->u.n.synth_fmt, (int)val);
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		sprintf(cp, var_data->u.n.synth_fmt,
24862306a36Sopenharmony_ci			var_data->u.n.out_str[val]);
24962306a36Sopenharmony_ci	synth_printf("%s", cp);
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_set_num_var);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciint spk_set_string_var(const char *page, struct st_var_header *var, int len)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct var_t *var_data = var->data;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!var_data)
25962306a36Sopenharmony_ci		return -ENODATA;
26062306a36Sopenharmony_ci	if (len > MAXVARLEN)
26162306a36Sopenharmony_ci		return -E2BIG;
26262306a36Sopenharmony_ci	if (!len) {
26362306a36Sopenharmony_ci		if (!var_data->u.s.default_val)
26462306a36Sopenharmony_ci			return 0;
26562306a36Sopenharmony_ci		if (!var->p_val)
26662306a36Sopenharmony_ci			var->p_val = var_data->u.s.default_val;
26762306a36Sopenharmony_ci		if (var->p_val != var_data->u.s.default_val)
26862306a36Sopenharmony_ci			strcpy((char *)var->p_val, var_data->u.s.default_val);
26962306a36Sopenharmony_ci		return -ERESTART;
27062306a36Sopenharmony_ci	} else if (var->p_val) {
27162306a36Sopenharmony_ci		strcpy((char *)var->p_val, page);
27262306a36Sopenharmony_ci	} else {
27362306a36Sopenharmony_ci		return -E2BIG;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/*
27962306a36Sopenharmony_ci * spk_set_mask_bits sets or clears the punc/delim/repeat bits,
28062306a36Sopenharmony_ci * if input is null uses the defaults.
28162306a36Sopenharmony_ci * values for how: 0 clears bits of chars supplied,
28262306a36Sopenharmony_ci * 1 clears allk, 2 sets bits for chars
28362306a36Sopenharmony_ci */
28462306a36Sopenharmony_ciint spk_set_mask_bits(const char *input, const int which, const int how)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	u_char *cp;
28762306a36Sopenharmony_ci	short mask = spk_punc_info[which].mask;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (how & 1) {
29062306a36Sopenharmony_ci		for (cp = (u_char *)spk_punc_info[3].value; *cp; cp++)
29162306a36Sopenharmony_ci			spk_chartab[*cp] &= ~mask;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	cp = (u_char *)input;
29462306a36Sopenharmony_ci	if (!cp) {
29562306a36Sopenharmony_ci		cp = spk_punc_info[which].value;
29662306a36Sopenharmony_ci	} else {
29762306a36Sopenharmony_ci		for (; *cp; cp++) {
29862306a36Sopenharmony_ci			if (*cp < SPACE)
29962306a36Sopenharmony_ci				break;
30062306a36Sopenharmony_ci			if (mask < PUNC) {
30162306a36Sopenharmony_ci				if (!(spk_chartab[*cp] & PUNC))
30262306a36Sopenharmony_ci					break;
30362306a36Sopenharmony_ci			} else if (spk_chartab[*cp] & B_NUM) {
30462306a36Sopenharmony_ci				break;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci		if (*cp)
30862306a36Sopenharmony_ci			return -EINVAL;
30962306a36Sopenharmony_ci		cp = (u_char *)input;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci	if (how & 2) {
31262306a36Sopenharmony_ci		for (; *cp; cp++)
31362306a36Sopenharmony_ci			if (*cp > SPACE)
31462306a36Sopenharmony_ci				spk_chartab[*cp] |= mask;
31562306a36Sopenharmony_ci	} else {
31662306a36Sopenharmony_ci		for (; *cp; cp++)
31762306a36Sopenharmony_ci			if (*cp > SPACE)
31862306a36Sopenharmony_ci				spk_chartab[*cp] &= ~mask;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cichar *spk_strlwr(char *s)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	char *p;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!s)
32862306a36Sopenharmony_ci		return NULL;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	for (p = s; *p; p++)
33162306a36Sopenharmony_ci		*p = tolower(*p);
33262306a36Sopenharmony_ci	return s;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cichar *spk_s2uchar(char *start, char *dest)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int val;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* Do not replace with kstrtoul: here we need start to be updated */
34062306a36Sopenharmony_ci	val = simple_strtoul(skip_spaces(start), &start, 10);
34162306a36Sopenharmony_ci	if (*start == ',')
34262306a36Sopenharmony_ci		start++;
34362306a36Sopenharmony_ci	*dest = (u_char)val;
34462306a36Sopenharmony_ci	return start;
34562306a36Sopenharmony_ci}
346