xref: /openbmc/linux/fs/unicode/utf8-core.c (revision b593bce5)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4 #include <linux/string.h>
5 #include <linux/slab.h>
6 #include <linux/parser.h>
7 #include <linux/errno.h>
8 #include <linux/unicode.h>
9 
10 #include "utf8n.h"
11 
12 int utf8_validate(const struct unicode_map *um, const struct qstr *str)
13 {
14 	const struct utf8data *data = utf8nfdi(um->version);
15 
16 	if (utf8nlen(data, str->name, str->len) < 0)
17 		return -1;
18 	return 0;
19 }
20 EXPORT_SYMBOL(utf8_validate);
21 
22 int utf8_strncmp(const struct unicode_map *um,
23 		 const struct qstr *s1, const struct qstr *s2)
24 {
25 	const struct utf8data *data = utf8nfdi(um->version);
26 	struct utf8cursor cur1, cur2;
27 	int c1, c2;
28 
29 	if (utf8ncursor(&cur1, data, s1->name, s1->len) < 0)
30 		return -EINVAL;
31 
32 	if (utf8ncursor(&cur2, data, s2->name, s2->len) < 0)
33 		return -EINVAL;
34 
35 	do {
36 		c1 = utf8byte(&cur1);
37 		c2 = utf8byte(&cur2);
38 
39 		if (c1 < 0 || c2 < 0)
40 			return -EINVAL;
41 		if (c1 != c2)
42 			return 1;
43 	} while (c1);
44 
45 	return 0;
46 }
47 EXPORT_SYMBOL(utf8_strncmp);
48 
49 int utf8_strncasecmp(const struct unicode_map *um,
50 		     const struct qstr *s1, const struct qstr *s2)
51 {
52 	const struct utf8data *data = utf8nfdicf(um->version);
53 	struct utf8cursor cur1, cur2;
54 	int c1, c2;
55 
56 	if (utf8ncursor(&cur1, data, s1->name, s1->len) < 0)
57 		return -EINVAL;
58 
59 	if (utf8ncursor(&cur2, data, s2->name, s2->len) < 0)
60 		return -EINVAL;
61 
62 	do {
63 		c1 = utf8byte(&cur1);
64 		c2 = utf8byte(&cur2);
65 
66 		if (c1 < 0 || c2 < 0)
67 			return -EINVAL;
68 		if (c1 != c2)
69 			return 1;
70 	} while (c1);
71 
72 	return 0;
73 }
74 EXPORT_SYMBOL(utf8_strncasecmp);
75 
76 /* String cf is expected to be a valid UTF-8 casefolded
77  * string.
78  */
79 int utf8_strncasecmp_folded(const struct unicode_map *um,
80 			    const struct qstr *cf,
81 			    const struct qstr *s1)
82 {
83 	const struct utf8data *data = utf8nfdicf(um->version);
84 	struct utf8cursor cur1;
85 	int c1, c2;
86 	int i = 0;
87 
88 	if (utf8ncursor(&cur1, data, s1->name, s1->len) < 0)
89 		return -EINVAL;
90 
91 	do {
92 		c1 = utf8byte(&cur1);
93 		c2 = cf->name[i++];
94 		if (c1 < 0)
95 			return -EINVAL;
96 		if (c1 != c2)
97 			return 1;
98 	} while (c1);
99 
100 	return 0;
101 }
102 EXPORT_SYMBOL(utf8_strncasecmp_folded);
103 
104 int utf8_casefold(const struct unicode_map *um, const struct qstr *str,
105 		  unsigned char *dest, size_t dlen)
106 {
107 	const struct utf8data *data = utf8nfdicf(um->version);
108 	struct utf8cursor cur;
109 	size_t nlen = 0;
110 
111 	if (utf8ncursor(&cur, data, str->name, str->len) < 0)
112 		return -EINVAL;
113 
114 	for (nlen = 0; nlen < dlen; nlen++) {
115 		int c = utf8byte(&cur);
116 
117 		dest[nlen] = c;
118 		if (!c)
119 			return nlen;
120 		if (c == -1)
121 			break;
122 	}
123 	return -EINVAL;
124 }
125 
126 EXPORT_SYMBOL(utf8_casefold);
127 
128 int utf8_normalize(const struct unicode_map *um, const struct qstr *str,
129 		   unsigned char *dest, size_t dlen)
130 {
131 	const struct utf8data *data = utf8nfdi(um->version);
132 	struct utf8cursor cur;
133 	ssize_t nlen = 0;
134 
135 	if (utf8ncursor(&cur, data, str->name, str->len) < 0)
136 		return -EINVAL;
137 
138 	for (nlen = 0; nlen < dlen; nlen++) {
139 		int c = utf8byte(&cur);
140 
141 		dest[nlen] = c;
142 		if (!c)
143 			return nlen;
144 		if (c == -1)
145 			break;
146 	}
147 	return -EINVAL;
148 }
149 
150 EXPORT_SYMBOL(utf8_normalize);
151 
152 static int utf8_parse_version(const char *version, unsigned int *maj,
153 			      unsigned int *min, unsigned int *rev)
154 {
155 	substring_t args[3];
156 	char version_string[12];
157 	const struct match_token token[] = {
158 		{1, "%d.%d.%d"},
159 		{0, NULL}
160 	};
161 
162 	strncpy(version_string, version, sizeof(version_string));
163 
164 	if (match_token(version_string, token, args) != 1)
165 		return -EINVAL;
166 
167 	if (match_int(&args[0], maj) || match_int(&args[1], min) ||
168 	    match_int(&args[2], rev))
169 		return -EINVAL;
170 
171 	return 0;
172 }
173 
174 struct unicode_map *utf8_load(const char *version)
175 {
176 	struct unicode_map *um = NULL;
177 	int unicode_version;
178 
179 	if (version) {
180 		unsigned int maj, min, rev;
181 
182 		if (utf8_parse_version(version, &maj, &min, &rev) < 0)
183 			return ERR_PTR(-EINVAL);
184 
185 		if (!utf8version_is_supported(maj, min, rev))
186 			return ERR_PTR(-EINVAL);
187 
188 		unicode_version = UNICODE_AGE(maj, min, rev);
189 	} else {
190 		unicode_version = utf8version_latest();
191 		printk(KERN_WARNING"UTF-8 version not specified. "
192 		       "Assuming latest supported version (%d.%d.%d).",
193 		       (unicode_version >> 16) & 0xff,
194 		       (unicode_version >> 8) & 0xff,
195 		       (unicode_version & 0xff));
196 	}
197 
198 	um = kzalloc(sizeof(struct unicode_map), GFP_KERNEL);
199 	if (!um)
200 		return ERR_PTR(-ENOMEM);
201 
202 	um->charset = "UTF-8";
203 	um->version = unicode_version;
204 
205 	return um;
206 }
207 EXPORT_SYMBOL(utf8_load);
208 
209 void utf8_unload(struct unicode_map *um)
210 {
211 	kfree(um);
212 }
213 EXPORT_SYMBOL(utf8_unload);
214 
215 MODULE_LICENSE("GPL v2");
216