+ if (strcmp(key, "DRIVERS") == 0) {
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid DRIVERS operation\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL);
+ continue;
+ }
+
+ if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) {
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid ATTRS operation\n");
+ goto invalid;
+ }
+ attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1);
+ if (attr == NULL) {
+ err(rules->udev, "error parsing ATTRS attribute\n");
+ goto invalid;
+ }
+ if (strncmp(attr, "device/", 7) == 0)
+ err(rules->udev, "the 'device' link may not be available in a future kernel, "
+ "please fix it in %s:%u", filename, lineno);
+ else if (strstr(attr, "../") != NULL)
+ err(rules->udev, "do not reference parent sysfs directories directly, "
+ "it may break with a future kernel, please fix it in %s:%u", filename, lineno);
+ rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
+ continue;
+ }
+
+ if (strncmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) {
+ if (!sysfs_warn) {
+ sysfs_warn = true;
+ err(rules->udev, "SYSFS{}= will be removed in a future udev version, "
+ "please use ATTR{}= to match the event device, or ATTRS{}= "
+ "to match a parent device, in %s:%u\n", filename, lineno);
+ }
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid ATTRS operation\n");
+ goto invalid;
+ }
+ attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1);
+ if (attr == NULL) {
+ err(rules->udev, "error parsing ATTRS attribute\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr);
+ continue;
+ }
+
+ if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) {
+ attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1);
+ if (attr == NULL) {
+ err(rules->udev, "error parsing ENV attribute\n");
+ goto invalid;
+ }
+ if (op < OP_MATCH_MAX) {
+ if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0)
+ goto invalid;
+ } else {
+ if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0)
+ goto invalid;
+ }
+ continue;
+ }
+
+ if (strcmp(key, "PROGRAM") == 0) {
+ rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
+ continue;
+ }
+
+ if (strcmp(key, "RESULT") == 0) {
+ if (op > OP_MATCH_MAX) {
+ err(rules->udev, "invalid RESULT operation\n");
+ goto invalid;
+ }
+ rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL);
+ continue;
+ }
+
+ if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) {
+ attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1);
+ if (attr != NULL && strstr(attr, "program")) {
+ dbg(rules->udev, "IMPORT will be executed\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
+ } else if (attr != NULL && strstr(attr, "file")) {
+ dbg(rules->udev, "IMPORT will be included as file\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);
+ } else if (attr != NULL && strstr(attr, "parent")) {
+ dbg(rules->udev, "IMPORT will include the parent values\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL);
+ } else {
+ /* figure it out if it is executable */
+ char file[UTIL_PATH_SIZE];
+ char *pos;
+ struct stat statbuf;
+
+ /* allow programs in /lib/udev called without the path */
+ if (value[0] != '/')
+ util_strscpyl(file, sizeof(file), LIBEXECDIR "/", value, NULL);
+ else
+ util_strscpy(file, sizeof(file), value);
+ pos = strchr(file, ' ');
+ if (pos)
+ pos[0] = '\0';
+ dbg(rules->udev, "IMPORT auto mode for '%s'\n", file);
+ if (stat(file, &statbuf) == 0 && (statbuf.st_mode & S_IXUSR)) {
+ dbg(rules->udev, "IMPORT will be executed (autotype)\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL);
+ } else {
+ dbg(rules->udev, "IMPORT will be included as file (autotype)\n");
+ rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL);