+struct uid_gid {
+ unsigned int name_off;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+};
+
+/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */
+struct udev_rules {
+ struct udev *udev;
+ int resolve_names;
+
+ /* every key in the rules file becomes a token */
+ struct token *tokens;
+ unsigned int token_cur;
+ unsigned int token_max;
+
+ /* all key strings are copied to a single string buffer */
+ char *buf;
+ size_t buf_cur;
+ size_t buf_max;
+ unsigned int buf_count;
+
+ /* during rule parsing, we cache uid/gid lookup results */
+ struct uid_gid *uids;
+ unsigned int uids_cur;
+ unsigned int uids_max;
+ struct uid_gid *gids;
+ unsigned int gids_cur;
+ unsigned int gids_max;
+};
+
+enum operation_type {
+ OP_UNSET,
+
+ OP_MATCH,
+ OP_NOMATCH,
+ OP_MATCH_MAX,
+
+ OP_ADD,
+ OP_ASSIGN,
+ OP_ASSIGN_FINAL,
+};
+
+enum string_glob_type {
+ GL_UNSET,
+ GL_PLAIN, /* no special chars */
+ GL_GLOB, /* shell globs ?,*,[] */
+ GL_SPLIT, /* multi-value A|B */
+ GL_SPLIT_GLOB, /* multi-value with glob A*|B* */
+ GL_SOMETHING, /* commonly used "?*" */
+ GL_FORMAT,
+};
+
+/* tokens of a rule are sorted/handled in this order */
+enum token_type {
+ TK_UNSET,
+ TK_RULE,
+
+ TK_M_ACTION, /* val */
+ TK_M_DEVPATH, /* val */
+ TK_M_KERNEL, /* val */
+ TK_M_DEVLINK, /* val */
+ TK_M_NAME, /* val */
+ TK_M_ENV, /* val, attr */
+ TK_M_SUBSYSTEM, /* val */
+ TK_M_DRIVER, /* val */
+ TK_M_WAITFOR, /* val */
+ TK_M_ATTR, /* val, attr */
+
+ TK_M_PARENTS_MIN,
+ TK_M_KERNELS, /* val */
+ TK_M_SUBSYSTEMS, /* val */
+ TK_M_DRIVERS, /* val */
+ TK_M_ATTRS, /* val, attr */
+ TK_M_PARENTS_MAX,
+
+ TK_M_TEST, /* val, mode_t */
+ TK_M_PROGRAM, /* val */
+ TK_M_IMPORT_FILE, /* val */
+ TK_M_IMPORT_PROG, /* val */
+ TK_M_IMPORT_PARENT, /* val */
+ TK_M_RESULT, /* val */
+ TK_M_MAX,
+
+ TK_A_IGNORE_DEVICE,
+ TK_A_STRING_ESCAPE_NONE,
+ TK_A_STRING_ESCAPE_REPLACE,
+ TK_A_NUM_FAKE_PART, /* int */
+ TK_A_DEVLINK_PRIO, /* int */
+ TK_A_OWNER, /* val */
+ TK_A_GROUP, /* val */
+ TK_A_MODE, /* val */
+ TK_A_OWNER_ID, /* uid_t */
+ TK_A_GROUP_ID, /* gid_t */
+ TK_A_MODE_ID, /* mode_t */
+ TK_A_ENV, /* val, attr */
+ TK_A_NAME, /* val */
+ TK_A_DEVLINK, /* val */
+ TK_A_EVENT_TIMEOUT, /* int */
+ TK_A_IGNORE_REMOVE,
+ TK_A_ATTR, /* val, attr */
+ TK_A_RUN, /* val, bool */
+ TK_A_GOTO, /* size_t */
+ TK_A_LAST_RULE,
+
+ TK_END,
+};
+
+/* we try to pack stuff in a way that we take only 12 bytes per token */
+struct token {
+ union {
+ unsigned char type; /* same as in rule and key */
+ struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned short token_count;
+ unsigned int label_off;
+ unsigned short filename_off;
+ unsigned short filename_line;
+ } rule;
+ struct {
+ unsigned char type;
+ unsigned char flags;
+ unsigned char op;
+ unsigned char glob;
+ unsigned int value_off;
+ union {
+ unsigned int attr_off;
+ int ignore_error;
+ int i;
+ unsigned int rule_goto;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int num_fake_part;
+ int devlink_prio;
+ int event_timeout;
+ };
+ } key;
+ };
+};
+
+#define MAX_TK 64
+struct rule_tmp {
+ struct udev_rules *rules;
+ struct token rule;
+ struct token token[MAX_TK];
+ unsigned int token_cur;
+};
+
+#ifdef DEBUG
+static const char *operation_str[] = {
+ [OP_UNSET] = "UNSET",
+ [OP_MATCH] = "match",
+ [OP_NOMATCH] = "nomatch",
+ [OP_MATCH_MAX] = "MATCH_MAX",
+
+ [OP_ADD] = "add",
+ [OP_ASSIGN] = "assign",
+ [OP_ASSIGN_FINAL] = "assign-final",
+};
+
+static const char *string_glob_str[] = {
+ [GL_UNSET] = "UNSET",
+ [GL_PLAIN] = "plain",
+ [GL_GLOB] = "glob",
+ [GL_SPLIT] = "split",
+ [GL_SPLIT_GLOB] = "split-glob",
+ [GL_SOMETHING] = "split-glob",
+ [GL_FORMAT] = "format",
+};
+
+static const char *token_str[] = {
+ [TK_UNSET] = "UNSET",
+ [TK_RULE] = "RULE",
+
+ [TK_M_ACTION] = "M ACTION",
+ [TK_M_DEVPATH] = "M DEVPATH",
+ [TK_M_KERNEL] = "M KERNEL",
+ [TK_M_DEVLINK] = "M DEVLINK",
+ [TK_M_NAME] = "M NAME",
+ [TK_M_ENV] = "M ENV",
+ [TK_M_SUBSYSTEM] = "M SUBSYSTEM",
+ [TK_M_DRIVER] = "M DRIVER",
+ [TK_M_WAITFOR] = "M WAITFOR",
+ [TK_M_ATTR] = "M ATTR",
+
+ [TK_M_PARENTS_MIN] = "M PARENTS_MIN",
+ [TK_M_KERNELS] = "M KERNELS",
+ [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS",
+ [TK_M_DRIVERS] = "M DRIVERS",
+ [TK_M_ATTRS] = "M ATTRS",
+ [TK_M_PARENTS_MAX] = "M PARENTS_MAX",
+
+ [TK_M_TEST] = "M TEST",
+ [TK_M_PROGRAM] = "M PROGRAM",
+ [TK_M_IMPORT_FILE] = "M IMPORT_FILE",
+ [TK_M_IMPORT_PROG] = "M IMPORT_PROG",
+ [TK_M_IMPORT_PARENT] = "M MPORT_PARENT",
+ [TK_M_RESULT] = "M RESULT",
+ [TK_M_MAX] = "M MAX",
+
+ [TK_A_IGNORE_DEVICE] = "A IGNORE_DEVICE",
+ [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
+ [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
+ [TK_A_NUM_FAKE_PART] = "A NUM_FAKE_PART",
+ [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO",
+ [TK_A_OWNER] = "A OWNER",
+ [TK_A_GROUP] = "A GROUP",
+ [TK_A_MODE] = "A MODE",
+ [TK_A_OWNER_ID] = "A OWNER_ID",
+ [TK_A_GROUP_ID] = "A GROUP_ID",
+ [TK_A_MODE_ID] = "A MODE_ID",
+ [TK_A_ENV] = "A ENV",
+ [TK_A_NAME] = "A NAME",
+ [TK_A_DEVLINK] = "A DEVLINK",
+ [TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT",
+ [TK_A_IGNORE_REMOVE] = "A IGNORE_REMOVE",
+ [TK_A_ATTR] = "A ATTR",
+ [TK_A_RUN] = "A RUN",
+ [TK_A_GOTO] = "A GOTO",
+ [TK_A_LAST_RULE] = "A LAST_RULE",
+
+ [TK_END] = "END",
+};
+
+static void dump_token(struct udev_rules *rules, struct token *token)