chiark / gitweb /
unify string replacement
[elogind.git] / udev / lib / libudev-util.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30
31 #include "libudev.h"
32 #include "libudev-private.h"
33
34 static ssize_t get_sys_link(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
35 {
36         char path[UTIL_PATH_SIZE];
37         ssize_t len;
38         const char *pos;
39
40         util_strlcpy(path, syspath, sizeof(path));
41         util_strlcat(path, "/", sizeof(path));
42         util_strlcat(path, slink, sizeof(path));
43         len = readlink(path, path, sizeof(path));
44         if (len < 0 || len >= (ssize_t) sizeof(path))
45                 return -1;
46         path[len] = '\0';
47         pos = strrchr(path, '/');
48         if (pos == NULL)
49                 return -1;
50         pos = &pos[1];
51         dbg(udev, "resolved link to: '%s'\n", pos);
52         return util_strlcpy(value, pos, size);
53 }
54
55 ssize_t util_get_sys_subsystem(struct udev *udev, const char *syspath, char *subsystem, size_t size)
56 {
57         return get_sys_link(udev, "subsystem", syspath, subsystem, size);
58 }
59
60 ssize_t util_get_sys_driver(struct udev *udev, const char *syspath, char *driver, size_t size)
61 {
62         return get_sys_link(udev, "driver", syspath, driver, size);
63 }
64
65 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
66 {
67         char link_target[UTIL_PATH_SIZE];
68
69         int len;
70         int i;
71         int back;
72
73         len = readlink(syspath, link_target, sizeof(link_target));
74         if (len <= 0)
75                 return -1;
76         link_target[len] = '\0';
77         dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
78
79         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
80                 ;
81         dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
82         for (i = 0; i <= back; i++) {
83                 char *pos = strrchr(syspath, '/');
84
85                 if (pos == NULL)
86                         return -1;
87                 pos[0] = '\0';
88         }
89         dbg(udev, "after moving back '%s'\n", syspath);
90         util_strlcat(syspath, "/", size);
91         util_strlcat(syspath, &link_target[back * 3], size);
92         return 0;
93 }
94
95 int util_log_priority(const char *priority)
96 {
97         char *endptr;
98         int prio;
99
100         prio = strtol(priority, &endptr, 10);
101         if (endptr[0] == '\0')
102                 return prio;
103         if (strncasecmp(priority, "err", 3) == 0)
104                 return LOG_ERR;
105         if (strcasecmp(priority, "info") == 0)
106                 return LOG_INFO;
107         if (strcasecmp(priority, "debug") == 0)
108                 return LOG_DEBUG;
109         return 0;
110 }
111
112 size_t util_path_encode(char *s, size_t len)
113 {
114         char t[(len * 3)+1];
115         size_t i, j;
116
117         for (i = 0, j = 0; s[i] != '\0'; i++) {
118                 if (s[i] == '/') {
119                         memcpy(&t[j], "\\x2f", 4);
120                         j += 4;
121                 } else if (s[i] == '\\') {
122                         memcpy(&t[j], "\\x5c", 4);
123                         j += 4;
124                 } else {
125                         t[j] = s[i];
126                         j++;
127                 }
128         }
129         if (len == 0)
130                 return j;
131         i = (j < len - 1) ? j : len - 1;
132         memcpy(s, t, i);
133         s[i] = '\0';
134         return j;
135 }
136
137 size_t util_path_decode(char *s)
138 {
139         size_t i, j;
140
141         for (i = 0, j = 0; s[i] != '\0'; j++) {
142                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
143                         s[j] = '/';
144                         i += 4;
145                 }else if (memcmp(&s[i], "\\x5c", 4) == 0) {
146                         s[j] = '\\';
147                         i += 4;
148                 } else {
149                         s[j] = s[i];
150                         i++;
151                 }
152         }
153         s[j] = '\0';
154         return j;
155 }
156
157 void util_remove_trailing_chars(char *path, char c)
158 {
159         size_t len;
160
161         if (path == NULL)
162                 return;
163         len = strlen(path);
164         while (len > 0 && path[len-1] == c)
165                 path[--len] = '\0';
166 }
167
168 size_t util_strlcpy(char *dst, const char *src, size_t size)
169 {
170         size_t bytes = 0;
171         char *q = dst;
172         const char *p = src;
173         char ch;
174
175         while ((ch = *p++)) {
176                 if (bytes+1 < size)
177                         *q++ = ch;
178                 bytes++;
179         }
180
181         /* If size == 0 there is no space for a final null... */
182         if (size)
183                 *q = '\0';
184         return bytes;
185 }
186
187 size_t util_strlcat(char *dst, const char *src, size_t size)
188 {
189         size_t bytes = 0;
190         char *q = dst;
191         const char *p = src;
192         char ch;
193
194         while (bytes < size && *q) {
195                 q++;
196                 bytes++;
197         }
198         if (bytes == size)
199                 return (bytes + strlen(src));
200
201         while ((ch = *p++)) {
202                 if (bytes+1 < size)
203                 *q++ = ch;
204                 bytes++;
205         }
206
207         *q = '\0';
208         return bytes;
209 }
210
211 /* count of characters used to encode one unicode char */
212 static int utf8_encoded_expected_len(const char *str)
213 {
214         unsigned char c = (unsigned char)str[0];
215
216         if (c < 0x80)
217                 return 1;
218         if ((c & 0xe0) == 0xc0)
219                 return 2;
220         if ((c & 0xf0) == 0xe0)
221                 return 3;
222         if ((c & 0xf8) == 0xf0)
223                 return 4;
224         if ((c & 0xfc) == 0xf8)
225                 return 5;
226         if ((c & 0xfe) == 0xfc)
227                 return 6;
228         return 0;
229 }
230
231 /* decode one unicode char */
232 static int utf8_encoded_to_unichar(const char *str)
233 {
234         int unichar;
235         int len;
236         int i;
237
238         len = utf8_encoded_expected_len(str);
239         switch (len) {
240         case 1:
241                 return (int)str[0];
242         case 2:
243                 unichar = str[0] & 0x1f;
244                 break;
245         case 3:
246                 unichar = (int)str[0] & 0x0f;
247                 break;
248         case 4:
249                 unichar = (int)str[0] & 0x07;
250                 break;
251         case 5:
252                 unichar = (int)str[0] & 0x03;
253                 break;
254         case 6:
255                 unichar = (int)str[0] & 0x01;
256                 break;
257         default:
258                 return -1;
259         }
260
261         for (i = 1; i < len; i++) {
262                 if (((int)str[i] & 0xc0) != 0x80)
263                         return -1;
264                 unichar <<= 6;
265                 unichar |= (int)str[i] & 0x3f;
266         }
267
268         return unichar;
269 }
270
271 /* expected size used to encode one unicode char */
272 static int utf8_unichar_to_encoded_len(int unichar)
273 {
274         if (unichar < 0x80)
275                 return 1;
276         if (unichar < 0x800)
277                 return 2;
278         if (unichar < 0x10000)
279                 return 3;
280         if (unichar < 0x200000)
281                 return 4;
282         if (unichar < 0x4000000)
283                 return 5;
284         return 6;
285 }
286
287 /* check if unicode char has a valid numeric range */
288 static int utf8_unichar_valid_range(int unichar)
289 {
290         if (unichar > 0x10ffff)
291                 return 0;
292         if ((unichar & 0xfffff800) == 0xd800)
293                 return 0;
294         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
295                 return 0;
296         if ((unichar & 0xffff) == 0xffff)
297                 return 0;
298         return 1;
299 }
300
301 /* validate one encoded unicode char and return its length */
302 static int utf8_encoded_valid_unichar(const char *str)
303 {
304         int len;
305         int unichar;
306         int i;
307
308         len = utf8_encoded_expected_len(str);
309         if (len == 0)
310                 return -1;
311
312         /* ascii is valid */
313         if (len == 1)
314                 return 1;
315
316         /* check if expected encoded chars are available */
317         for (i = 0; i < len; i++)
318                 if ((str[i] & 0x80) != 0x80)
319                         return -1;
320
321         unichar = utf8_encoded_to_unichar(str);
322
323         /* check if encoded length matches encoded value */
324         if (utf8_unichar_to_encoded_len(unichar) != len)
325                 return -1;
326
327         /* check if value has valid range */
328         if (!utf8_unichar_valid_range(unichar))
329                 return -1;
330
331         return len;
332 }
333
334 int udev_util_replace_whitespace(const char *str, char *to, size_t len)
335 {
336         size_t i, j;
337
338         /* strip trailing whitespace */
339         len = strnlen(str, len);
340         while (len && isspace(str[len-1]))
341                 len--;
342
343         /* strip leading whitespace */
344         i = 0;
345         while (isspace(str[i]) && (i < len))
346                 i++;
347
348         j = 0;
349         while (i < len) {
350                 /* substitute multiple whitespace with a single '_' */
351                 if (isspace(str[i])) {
352                         while (isspace(str[i]))
353                                 i++;
354                         to[j++] = '_';
355                 }
356                 to[j++] = str[i++];
357         }
358         to[j] = '\0';
359         return 0;
360 }
361
362 static int is_whitelisted(char c, const char *white)
363 {
364         if ((c >= '0' && c <= '9') ||
365             (c >= 'A' && c <= 'Z') ||
366             (c >= 'a' && c <= 'z') ||
367             strchr("#+-.:=@_", c) != NULL ||
368             (white != NULL && strchr(white, c) != NULL))
369                 return 1;
370         return 0;
371 }
372
373 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
374 int udev_util_replace_chars(char *str, const char *white)
375 {
376         size_t i = 0;
377         int replaced = 0;
378
379         while (str[i] != '\0') {
380                 int len;
381
382                 if (is_whitelisted(str[i], white)) {
383                         i++;
384                         continue;
385                 }
386
387                 /* accept hex encoding */
388                 if (str[i] == '\\' && str[i+1] == 'x') {
389                         i += 2;
390                         continue;
391                 }
392
393                 /* accept valid utf8 */
394                 len = utf8_encoded_valid_unichar(&str[i]);
395                 if (len > 1) {
396                         i += len;
397                         continue;
398                 }
399
400                 /* if space is allowed, replace whitespace with ordinary space */
401                 if (isspace(str[i]) && strchr(white, ' ') != NULL) {
402                         str[i] = ' ';
403                         i++;
404                         replaced++;
405                         continue;
406                 }
407
408                 /* everything else is replaced with '_' */
409                 str[i] = '_';
410                 i++;
411                 replaced++;
412         }
413         return replaced;
414 }
415
416 /**
417  * util_encode_string:
418  * @str: input string to be encoded
419  * @str_enc: output string to store the encoded input string
420  * @len: maximum size of the output string, which may be
421  *       four times as long as the input string
422  *
423  * Encode all potentially unsafe characters of a string to the
424  * corresponding hex value prefixed by '\x'.
425  *
426  * Returns: 0 if the entire string was copied, non-zero otherwise.
427  **/
428 int udev_util_encode_string(const char *str, char *str_enc, size_t len)
429 {
430         size_t i, j;
431
432         if (str == NULL || str_enc == NULL || len == 0)
433                 return -1;
434
435         str_enc[0] = '\0';
436         for (i = 0, j = 0; str[i] != '\0'; i++) {
437                 int seqlen;
438
439                 seqlen = utf8_encoded_valid_unichar(&str[i]);
440                 if (seqlen > 1) {
441                         memcpy(&str_enc[j], &str[i], seqlen);
442                         j += seqlen;
443                         i += (seqlen-1);
444                 } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
445                         sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
446                         j += 4;
447                 } else {
448                         str_enc[j] = str[i];
449                         j++;
450                 }
451                 if (j+3 >= len)
452                         goto err;
453         }
454         str_enc[j] = '\0';
455         return 0;
456 err:
457         return -1;
458 }