/* radare - LGPL - Copyright 2009-2026 - pancake */

#include <r_util.h>

// lookup table for r_name_validate_char: true = valid (alnum or . : _)
static const bool valid_name_char[256] = {
	['.'] = true,
	['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
	['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true,
	[':'] = true,
	['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true, ['F'] = true, ['G'] = true,
	['H'] = true, ['I'] = true, ['J'] = true, ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true,
	['O'] = true, ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true, ['U'] = true,
	['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true, ['Z'] = true,
	['_'] = true,
	['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true, ['f'] = true, ['g'] = true,
	['h'] = true, ['i'] = true, ['j'] = true, ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true,
	['o'] = true, ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true, ['u'] = true,
	['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true, ['z'] = true,
};

// lookup table for r_name_validate_dash: true = dash-like (replace with _)
static const bool dash_char[256] = {
	['!'] = true, ['"'] = true, ['$'] = true, ['%'] = true, ['('] = true, [')'] = true,
	[','] = true, ['-'] = true, ['/'] = true, [';'] = true, ['<'] = true, ['>'] = true,
	['?'] = true, ['@'] = true, ['['] = true, ['\\'] = true, [']'] = true, ['_'] = true,
	['`'] = true, ['~'] = true, [' '] = true,
};

// lookup table for shell filter: true = skip char
static const bool shell_skip_char[256] = {
	['\n'] = true, ['\r'] = true, [' '] = true, ['"'] = true, ['='] = true, ['\\'] = true,
};

// used to determine if we want to replace those chars with '_' in r_name_filter()
R_API bool r_name_validate_dash(const char ch) {
	return dash_char[(ut8)ch];
}

R_API bool r_name_validate_char(const char ch) {
	return valid_name_char[(ut8)ch];
}

R_API bool r_name_validate_first(const char ch) {
	if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
		return true;
	}
	switch (ch) {
	case '_':
	case ':':
		return true;
	}
	return false;
}

static int name_utf8_size(const char *s) {
	if ((ut8)*s < 0x80) {
		return 0;
	}
	RRune ch;
	int size = r_utf8_decode ((const ut8 *)s, -1, &ch);
	return size > 1 && ch >= 0xa0 && ch != 0x2028 && ch != 0x2029? size: 0;
}

R_API bool r_name_check_utf8(const char *s, bool utf8) {
	R_RETURN_VAL_IF_FAIL (s, false);
	int size = utf8? name_utf8_size (s): 0;
	if (!size && !r_name_validate_first (*s)) {
		return false;
	}
	for (s += size? size: 1; *s; s++) {
		if (utf8 && (ut8)*s >= 0x80 && (size = name_utf8_size (s))) {
			s += size - 1;
			continue;
		}
		if (!valid_name_char[(ut8)*s]) {
			return false;
		}
	}
	return true;
}

R_API bool r_name_check(const char *s) {
	return r_name_check_utf8 (s, false);
}

static inline bool is_special_char(char n) {
	const char chars[] = "sbfnrtva";
	return strchr (chars, n);
}

R_API const char *r_name_filter_ro(const char *a) {
	R_RETURN_VAL_IF_FAIL (a, NULL);
	while (*a++ == '_');
	return a - 1;
}

R_API bool r_name_filter_utf8(char *s, int maxlen, bool utf8) {
	R_RETURN_VAL_IF_FAIL (s, false);
	if (R_STR_ISEMPTY (s)) {
		return false;
	}
	size_t count = 0;
	r_str_trim_head (s);
	if (!*s) {
		return false;
	}
	int size = utf8? name_utf8_size (s): 0;
	bool valid = size || r_name_validate_first (*s);
	if (!valid) {
		*s = '_';
	}
	s += size? size: 1;
	char *out = s;
	for (; *s; s++, count++) {
		if (maxlen > 0 && count > maxlen) {
			valid = false;
			break;
		}
		if (utf8 && (ut8)*s >= 0x80 && (size = name_utf8_size (s))) {
			memmove (out, s, size);
			out += size;
			s += size - 1;
		} else if (r_name_validate_char (*s)) {
			*out++ = *s;
		} else {
			valid = false;
			if (r_name_validate_dash (*s) && (*s != '\\' || is_special_char (s[1]))) {
				*out++ = '_';
			}
		}
	}
	*out = 0;
	return valid;
}

R_API bool r_name_filter(char *s, int maxlen) {
	return r_name_filter_utf8 (s, maxlen, false);
}

R_API char *r_name_filter_dup(const char *name) {
	R_RETURN_VAL_IF_FAIL (name, NULL);
	char *s = strdup (name);
	r_name_filter (s, -1);
	if (*s) {
		return s;
	}
	free (s);
	return NULL;
}

// filter out shell special chars
R_API char *r_name_filter_shell(const char *s) {
	R_RETURN_VAL_IF_FAIL (s, NULL);
	char *a = malloc (strlen (s) + 1);
	if (!a) {
		return NULL;
	}
	char *b = a;
	while (*s) {
		switch (*s) {
		case '@':
		case '`':
		case '|':
		case ';':
		case '=':
		case '\n':
			break;
		default:
			*b++ = *s;
			break;
		}
		s++;
	}
	*b = 0;
	return a;
}

R_API char *r_name_filter_quoted_shell(const char *s) {
	R_RETURN_VAL_IF_FAIL (s, NULL);
	char *a = malloc (strlen (s) + 1);
	if (!a) {
		return NULL;
	}
	char *b = a;
	while (*s) {
		if (!shell_skip_char[(ut8)*s]) {
			*b++ = *s;
		}
		s++;
	}
	*b = 0;
	return a;
}
