chiark / gitweb /
hwdb: allow list of lookup keys per given record
authorKay Sievers <kay@vrfy.org>
Wed, 10 Jul 2013 12:59:24 +0000 (14:59 +0200)
committerKay Sievers <kay@vrfy.org>
Wed, 10 Jul 2013 13:05:05 +0000 (15:05 +0200)
This allows to specify:
  dmi:bvn*:bvr*:bd*:svnVENDOR:pn:Model 231*:pvr*
  dmi:bvn*:bvr*:bd*:svnVENDOR:pn:Series 12*:pvr*
   KEY_A=value
   KEY_B=value

Instead of:
  dmi:bvn*:bvr*:bd*:svnVENDOR:pn:Model 231*:pvr*
   KEY_A=value
   KEY_B=value

  dmi:bvn*:bvr*:bd*:svnVENDOR:pn:Series 12*:pvr*
   KEY_A=value
   KEY_B=value

src/udev/udevadm-hwdb.c

index 10b490e..4136b17 100644 (file)
@@ -21,6 +21,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "util.h"
 #include "strbuf.h"
@@ -402,56 +403,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];
+        struct udev_list match_list;
+
+        udev_list_init(udev, &match_list, false);
 
         f = fopen(filename, "re");
         if (f == NULL)
                 return -errno;
 
-        match[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';
-                        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);
-                        continue;
-                }
+                        /* start of record, first match */
+                        state = HW_MATCH;
+                        udev_list_entry_add(&match_list, line, NULL);
+                        break;
 
-                /* value line */
-                if (line[0] == ' ') {
-                        char *value;
+                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;
+                        }
 
-                        value = strchr(line, '=');
-                        if (!value)
-                                continue;
-                        value[0] = '\0';
-                        value++;
-                        trie_insert(trie, trie->root, match, line, value);
-                        continue;
-                }
+                        /* another match */
+                        if (line[0] != ' ') {
+                                udev_list_entry_add(&match_list, line, NULL);
+                                break;
+                        }
+
+                        /* first data */
+                        state = HW_DATA;
+                        insert_data(trie, &match_list, line, filename);
+                        break;
+
+                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;
+                        }
 
-                log_error("Error parsing line '%s' in '%s\n", line, filename);
+                        insert_data(trie, &match_list, line, filename);
+                        break;
+                };
         }
+
         fclose(f);
+        udev_list_cleanup(&match_list);
         return 0;
 }
 
@@ -539,7 +606,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);