chiark / gitweb /
libudev: udev_device - add attribute cache
[elogind.git] / udev / lib / libudev-util.c
index ae0adf4b88cbb8b26df2dbbbcce9ed6a57826876..50eacdd37d760c402b56a1462a8f4708f9b45095 100644 (file)
@@ -26,6 +26,8 @@
 #include <errno.h>
 #include <string.h>
 #include <dirent.h>
+#include <ctype.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 
 #include "libudev.h"
@@ -96,7 +98,7 @@ int util_resolve_sys_link(struct udev *udev, char *devpath, size_t size)
 }
 
 struct util_name_entry *util_name_list_add(struct udev *udev, struct list_head *name_list,
-                                          const char *name, int sort)
+                                          const char *name, const char *value, int sort)
 {
        struct util_name_entry *name_loop;
        struct util_name_entry *name_new;
@@ -125,7 +127,14 @@ struct util_name_entry *util_name_list_add(struct udev *udev, struct list_head *
                free(name_new);
                return NULL;
        }
-       dbg(udev, "adding '%s'\n", name_new->name);
+       if (value != NULL) {
+               name_new->value = strdup(value);
+               if (name_new->value == NULL) {
+                       free(name_new);
+                       return NULL;
+               }
+       }
+       dbg(udev, "adding '%s=%s'\n", name_new->name, name_new->value);
        list_add_tail(&name_new->node, &name_loop->node);
        return name_new;
 }
@@ -138,6 +147,7 @@ void util_name_list_cleanup(struct udev *udev, struct list_head *name_list)
        list_for_each_entry_safe(name_loop, name_tmp, name_list, node) {
                list_del(&name_loop->node);
                free(name_loop->name);
+               free(name_loop->value);
                free(name_loop);
        }
 }
@@ -255,3 +265,179 @@ size_t util_strlcat(char *dst, const char *src, size_t size)
        *q = '\0';
        return bytes;
 }
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str)
+{
+       unsigned char c = (unsigned char)str[0];
+
+       if (c < 0x80)
+               return 1;
+       if ((c & 0xe0) == 0xc0)
+               return 2;
+       if ((c & 0xf0) == 0xe0)
+               return 3;
+       if ((c & 0xf8) == 0xf0)
+               return 4;
+       if ((c & 0xfc) == 0xf8)
+               return 5;
+       if ((c & 0xfe) == 0xfc)
+               return 6;
+       return 0;
+}
+
+/* decode one unicode char */
+static int utf8_encoded_to_unichar(const char *str)
+{
+       int unichar;
+       int len;
+       int i;
+
+       len = utf8_encoded_expected_len(str);
+       switch (len) {
+       case 1:
+               return (int)str[0];
+       case 2:
+               unichar = str[0] & 0x1f;
+               break;
+       case 3:
+               unichar = (int)str[0] & 0x0f;
+               break;
+       case 4:
+               unichar = (int)str[0] & 0x07;
+               break;
+       case 5:
+               unichar = (int)str[0] & 0x03;
+               break;
+       case 6:
+               unichar = (int)str[0] & 0x01;
+               break;
+       default:
+               return -1;
+       }
+
+       for (i = 1; i < len; i++) {
+               if (((int)str[i] & 0xc0) != 0x80)
+                       return -1;
+               unichar <<= 6;
+               unichar |= (int)str[i] & 0x3f;
+       }
+
+       return unichar;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar)
+{
+       if (unichar < 0x80)
+               return 1;
+       if (unichar < 0x800)
+               return 2;
+       if (unichar < 0x10000)
+               return 3;
+       if (unichar < 0x200000)
+               return 4;
+       if (unichar < 0x4000000)
+               return 5;
+       return 6;
+}
+
+/* check if unicode char has a valid numeric range */
+static int utf8_unichar_valid_range(int unichar)
+{
+       if (unichar > 0x10ffff)
+               return 0;
+       if ((unichar & 0xfffff800) == 0xd800)
+               return 0;
+       if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
+               return 0;
+       if ((unichar & 0xffff) == 0xffff)
+               return 0;
+       return 1;
+}
+
+/* validate one encoded unicode char and return its length */
+static int utf8_encoded_valid_unichar(const char *str)
+{
+       int len;
+       int unichar;
+       int i;
+
+       len = utf8_encoded_expected_len(str);
+       if (len == 0)
+               return -1;
+
+       /* ascii is valid */
+       if (len == 1)
+               return 1;
+
+       /* check if expected encoded chars are available */
+       for (i = 0; i < len; i++)
+               if ((str[i] & 0x80) != 0x80)
+                       return -1;
+
+       unichar = utf8_encoded_to_unichar(str);
+
+       /* check if encoded length matches encoded value */
+       if (utf8_unichar_to_encoded_len(unichar) != len)
+               return -1;
+
+       /* check if value has valid range */
+       if (!utf8_unichar_valid_range(unichar))
+               return -1;
+
+       return len;
+}
+
+/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
+int util_replace_chars(char *str, const char *white)
+{
+       size_t i = 0;
+       int replaced = 0;
+
+       while (str[i] != '\0') {
+               int len;
+
+               /* accept whitelist */
+               if (white != NULL && strchr(white, str[i]) != NULL) {
+                       i++;
+                       continue;
+               }
+
+               /* accept plain ascii char */
+               if ((str[i] >= '0' && str[i] <= '9') ||
+                   (str[i] >= 'A' && str[i] <= 'Z') ||
+                   (str[i] >= 'a' && str[i] <= 'z')) {
+                       i++;
+                       continue;
+               }
+
+               /* accept hex encoding */
+               if (str[i] == '\\' && str[i+1] == 'x') {
+                       i += 2;
+                       continue;
+               }
+
+               /* accept valid utf8 */
+               len = utf8_encoded_valid_unichar(&str[i]);
+               if (len > 1) {
+                       i += len;
+                       continue;
+               }
+
+               /* if space is allowed, replace whitespace with ordinary space */
+               if (isspace(str[i]) && strchr(white, ' ') != NULL) {
+                       str[i] = ' ';
+                       i++;
+                       replaced++;
+                       continue;
+               }
+
+               /* everything else is replaced with '_' */
+               str[i] = '_';
+               i++;
+               replaced++;
+       }
+
+       return replaced;
+}