/* * libudev - interface to udev device information * * Copyright (C) 2008 Kay Sievers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include "libudev.h" #include "libudev-private.h" /** * udev_list_entry: * * One entry in a list, containing a name and an optional value. */ struct udev_list_entry { struct udev_list_node node; struct udev *udev; struct udev_list_node *list; char *name; char *value; int flag; }; /* list head point to itself if empty */ void udev_list_init(struct udev_list_node *list) { list->next = list; list->prev = list; } int udev_list_is_empty(struct udev_list_node *list) { return list->next == list; } static void udev_list_node_insert_between(struct udev_list_node *new, struct udev_list_node *prev, struct udev_list_node *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) { udev_list_node_insert_between(new, list->prev, list); } void udev_list_node_remove(struct udev_list_node *entry) { struct udev_list_node *prev = entry->prev; struct udev_list_node *next = entry->next; next->prev = prev; prev->next = next; entry->prev = NULL; entry->next = NULL; } /* return list entry which embeds this node */ static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) { char *list; list = (char *)node; list -= offsetof(struct udev_list_entry, node); return (struct udev_list_entry *)list; } /* insert entry into a list as the last element */ void udev_list_entry_append(struct udev_list_entry *new, struct udev_list_node *list) { /* inserting before the list head make the node the last node in the list */ udev_list_node_insert_between(&new->node, list->prev, list); new->list = list; } /* remove entry from a list */ void udev_list_entry_remove(struct udev_list_entry *entry) { udev_list_node_remove(&entry->node); entry->list = NULL; } /* insert entry into a list, before a given existing entry */ void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) { udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); new->list = entry->list; } struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_node *list, const char *name, const char *value, int unique, int sort) { struct udev_list_entry *entry_loop = NULL; struct udev_list_entry *entry_new; if (unique) udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) { if (strcmp(entry_loop->name, name) == 0) { dbg(udev, "'%s' is already in the list\n", name); free(entry_loop->value); if (value == NULL) { entry_loop->value = NULL; dbg(udev, "'%s' value unset\n", name); return entry_loop; } entry_loop->value = strdup(value); if (entry_loop->value == NULL) return NULL; dbg(udev, "'%s' value replaced with '%s'\n", name, value); return entry_loop; } } if (sort) udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) { if (strcmp(entry_loop->name, name) > 0) break; } entry_new = malloc(sizeof(struct udev_list_entry)); if (entry_new == NULL) return NULL; memset(entry_new, 0x00, sizeof(struct udev_list_entry)); entry_new->udev = udev; entry_new->name = strdup(name); if (entry_new->name == NULL) { free(entry_new); return NULL; } if (value != NULL) { entry_new->value = strdup(value); if (entry_new->value == NULL) { free(entry_new->name); free(entry_new); return NULL; } } if (entry_loop != NULL) udev_list_entry_insert_before(entry_new, entry_loop); else udev_list_entry_append(entry_new, list); dbg(udev, "'%s=%s' added\n", entry_new->name, entry_new->value); return entry_new; } void udev_list_entry_delete(struct udev_list_entry *entry) { udev_list_node_remove(&entry->node); free(entry->name); free(entry->value); free(entry); } void udev_list_cleanup_entries(struct udev *udev, struct udev_list_node *list) { struct udev_list_entry *entry_loop; struct udev_list_entry *entry_tmp; udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) udev_list_entry_delete(entry_loop); } struct udev_list_entry *udev_list_get_entry(struct udev_list_node *list) { if (udev_list_is_empty(list)) return NULL; return list_node_to_entry(list->next); } /** * udev_list_entry_get_next: * @list_entry: current entry * * Returns: the next entry from the list, #NULL is no more entries are found. */ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) { struct udev_list_node *next; if (list_entry == NULL) return NULL; next = list_entry->node.next; /* empty list or no more entries */ if (next == list_entry->list) return NULL; return list_node_to_entry(next); } /** * udev_list_entry_get_by_name: * @list_entry: current entry * @name: name string to match * * Returns: the entry where @name matched, #NULL if no matching entry is found. */ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) { struct udev_list_entry *entry; udev_list_entry_foreach(entry, list_entry) { if (strcmp(udev_list_entry_get_name(entry), name) == 0) { dbg(entry->udev, "found '%s=%s'\n", entry->name, entry->value); return entry; } } return NULL; } /** * udev_list_entry_get_name: * @list_entry: current entry * * Returns: the name string of this entry. */ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) { if (list_entry == NULL) return NULL; return list_entry->name; } /** * udev_list_entry_get_value: * @list_entry: current entry * * Returns: the value string of this entry. */ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) { if (list_entry == NULL) return NULL; return list_entry->value; } int udev_list_entry_get_flag(struct udev_list_entry *list_entry) { if (list_entry == NULL) return -EINVAL; return list_entry->flag; } void udev_list_entry_set_flag(struct udev_list_entry *list_entry, int flag) { if (list_entry == NULL) return; list_entry->flag = flag; }