chiark / gitweb /
driverd: implement AddMatch/RemoveMatch logic
[elogind.git] / src / udev / udevadm-hwdb.c
index b18b28aca72ce393cdef2c5f7583a1fb97d68475..d0cce84859aa4a38c28d2a89a03230a26d4fa6f2 100644 (file)
@@ -21,6 +21,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "util.h"
 #include "strbuf.h"
@@ -181,9 +182,9 @@ static int trie_insert(struct trie *trie, struct trie_node *node, const char *se
                 struct trie_node *child;
 
                 for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
-                        char _cleanup_free_ *s = NULL;
+                        _cleanup_free_ char *s = NULL;
                         ssize_t off;
-                        struct trie_node _cleanup_free_ *new_child = NULL;
+                        _cleanup_free_ struct trie_node *new_child = NULL;
 
                         if (c == search[i + p])
                                 continue;
@@ -302,8 +303,10 @@ static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) {
                 int64_t child_off;
 
                 child_off = trie_store_nodes(trie, node->children[i].child);
-                if (child_off < 0)
+                if (child_off < 0) {
+                        free(children);
                         return child_off;
+                }
                 children[i].c = node->children[i].c;
                 children[i].child_off = htole64(child_off);
         }
@@ -402,66 +405,122 @@ out:
         return err;
 }
 
-static int import_file(struct trie *trie, const char *filename) {
+static int insert_data(struct trie *trie, struct udev_list *match_list,
+                       char *line, const char *filename) {
+        char *value;
+        struct udev_list_entry *entry;
+
+        value = strchr(line, '=');
+        if (!value) {
+                log_error("Error, key/value pair expected but got '%s' in '%s':\n", line, filename);
+                return -EINVAL;
+        }
+
+        value[0] = '\0';
+        value++;
+
+        if (line[0] == '\0' || value[0] == '\0') {
+                log_error("Error, empty key or value '%s' in '%s':\n", line, filename);
+                return -EINVAL;
+        }
+
+        udev_list_entry_foreach(entry, udev_list_get_entry(match_list))
+                trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value);
+
+        return 0;
+}
+
+static int import_file(struct udev *udev, struct trie *trie, const char *filename) {
+        enum {
+                HW_MATCH,
+                HW_DATA,
+                HW_NONE,
+        } state = HW_NONE;
         FILE *f;
         char line[LINE_MAX];
-        char match[LINE_MAX];
-        char cond[LINE_MAX];
+        struct udev_list match_list;
+
+        udev_list_init(udev, &match_list, false);
 
         f = fopen(filename, "re");
         if (f == NULL)
                 return -errno;
 
-        match[0] = '\0';
-        cond[0] = '\0';
         while (fgets(line, sizeof(line), f)) {
                 size_t len;
+                char *pos;
 
+                /* comment line */
                 if (line[0] == '#')
                         continue;
 
-                /* new line, new record */
-                if (line[0] == '\n') {
-                        match[0] = '\0';
-                        cond[0] = '\0';
-                        continue;
-                }
+                /* strip trailing comment */
+                pos = strchr(line, '#');
+                if (pos)
+                        pos[0] = '\0';
 
-                /* remove newline */
+                /* strip trailing whitespace */
                 len = strlen(line);
-                if (len < 2)
-                        continue;
-                line[len-1] = '\0';
+                while (len > 0 && isspace(line[len-1]))
+                        len--;
+                line[len] = '\0';
+
+                switch (state) {
+                case HW_NONE:
+                        if (len == 0)
+                                break;
+
+                        if (line[0] == ' ') {
+                                log_error("Error, MATCH expected but got '%s' in '%s':\n", line, filename);
+                                break;
+                        }
 
-                /* start of new record */
-                if (match[0] == '\0') {
-                        strcpy(match, line);
-                        cond[0] = '\0';
-                        continue;
-                }
+                        /* start of record, first match */
+                        state = HW_MATCH;
+                        udev_list_entry_add(&match_list, line, NULL);
+                        break;
 
-                if (line[0] == '+') {
-                        strcpy(cond, line);
-                        continue;
-                }
+                case HW_MATCH:
+                        if (len == 0) {
+                                log_error("Error, DATA expected but got empty line in '%s':\n", filename);
+                                state = HW_NONE;
+                                udev_list_cleanup(&match_list);
+                                break;
+                        }
 
-                /* TODO: support +; skip the entire record until we support it */
-                if (cond[0] != '\0')
-                        continue;
+                        /* another match */
+                        if (line[0] != ' ') {
+                                udev_list_entry_add(&match_list, line, NULL);
+                                break;
+                        }
 
-                /* value lines */
-                if (line[0] == ' ') {
-                        char *value;
+                        /* first data */
+                        state = HW_DATA;
+                        insert_data(trie, &match_list, line, filename);
+                        break;
 
-                        value = strchr(line, '=');
-                        if (!value)
-                                continue;
-                        value[0] = '\0';
-                        value++;
-                        trie_insert(trie, trie->root, match, line, value);
-                }
+                case HW_DATA:
+                        /* end of record */
+                        if (len == 0) {
+                                state = HW_NONE;
+                                udev_list_cleanup(&match_list);
+                                break;
+                        }
+
+                        if (line[0] != ' ') {
+                                log_error("Error, DATA expected but got '%s' in '%s':\n", line, filename);
+                                state = HW_NONE;
+                                udev_list_cleanup(&match_list);
+                                break;
+                        }
+
+                        insert_data(trie, &match_list, line, filename);
+                        break;
+                };
         }
+
         fclose(f);
+        udev_list_cleanup(&match_list);
         return 0;
 }
 
@@ -549,7 +608,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
                 }
                 STRV_FOREACH(f, files) {
                         log_debug("reading file '%s'", *f);
-                        import_file(trie, *f);
+                        import_file(udev, trie, *f);
                 }
                 strv_free(files);
 
@@ -589,7 +648,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
 
                         udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, test, 0))
                                 printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
-                        hwdb = udev_hwdb_unref(hwdb);
+                        udev_hwdb_unref(hwdb);
                 }
         }
 out: