root/src/add-ons/kernel/drivers/input/hid_shared/DeviceList.cpp
/*
        Generic device list for use in drivers.
        Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
        Distributed under the terms of the MIT license.
*/
#include "DeviceList.h"
#include <util/kernel_cpp.h>
#include <stdlib.h>
#include <string.h>
#include <new>

struct device_list_entry {
        char *                          name;
        void *                          device;
        device_list_entry *     next;
};


DeviceList::DeviceList()
        :       fDeviceList(NULL),
                fDeviceCount(0),
                fPublishList(NULL)
{
}


DeviceList::~DeviceList()
{
        _FreePublishList();

        device_list_entry *current = fDeviceList;
        while (current) {
                device_list_entry *next = current->next;
                free(current->name);
                delete current;
                current = next;
        }
}


status_t
DeviceList::AddDevice(const char *name, void *device)
{
        device_list_entry *entry = new(std::nothrow) device_list_entry;
        if (entry == NULL)
                return B_NO_MEMORY;

        entry->name = strdup(name);
        if (entry->name == NULL) {
                delete entry;
                return B_NO_MEMORY;
        }

        entry->device = device;
        entry->next = NULL;

        if (fDeviceList == NULL)
                fDeviceList = entry;
        else {
                device_list_entry *current = fDeviceList;
                while (current) {
                        if (current->next == NULL) {
                                current->next = entry;
                                break;
                        }

                        current = current->next;
                }
        }

        fDeviceCount++;
        return B_OK;
}


status_t
DeviceList::RemoveDevice(const char *name, void *device)
{
        if (name == NULL && device == NULL)
                return B_BAD_VALUE;

        device_list_entry *previous = NULL;
        device_list_entry *current = fDeviceList;
        while (current) {
                if ((name != NULL && strcmp(current->name, name) == 0)
                        || (device != NULL && current->device == device)) {
                        if (previous == NULL)
                                fDeviceList = current->next;
                        else
                                previous->next = current->next;

                        free(current->name);
                        delete current;
                        fDeviceCount--;
                        return B_OK;
                }

                previous = current;
                current = current->next;
        }

        return B_ENTRY_NOT_FOUND;
}


void *
DeviceList::FindDevice(const char *name, void *device)
{
        if (name == NULL && device == NULL)
                return NULL;

        device_list_entry *current = fDeviceList;
        while (current) {
                if ((name != NULL && strcmp(current->name, name) == 0)
                        || (device != NULL && current->device == device))
                        return current->device;

                current = current->next;
        }

        return NULL;
}


int32
DeviceList::CountDevices(const char *baseName)
{
        if (baseName == NULL)
                return fDeviceCount;

        int32 count = 0;
        int32 baseNameLength = strlen(baseName);
        device_list_entry *current = fDeviceList;
        while (current) {
                if (strncmp(current->name, baseName, baseNameLength) == 0)
                        count++;

                current = current->next;
        }

        return count;
}


void *
DeviceList::DeviceAt(int32 index)
{
        device_list_entry *current = fDeviceList;
        while (current) {
                if (index-- == 0)
                        return current->device;

                current = current->next;
        }

        return NULL;
}


const char **
DeviceList::PublishDevices()
{
        _FreePublishList();

        fPublishList = (char **)malloc((fDeviceCount + 1) * sizeof(char *));
        if (fPublishList == NULL)
                return NULL;

        int32 index = 0;
        device_list_entry *current = fDeviceList;
        while (current) {
                fPublishList[index++] = strdup(current->name);
                current = current->next;
        }

        fPublishList[index] = NULL;
        return (const char **)fPublishList;
}


void
DeviceList::_FreePublishList()
{
        if (fPublishList == NULL)
                return;

        int32 index = 0;
        while (fPublishList[index] != NULL) {
                free(fPublishList[index]);
                index++;
        }

        free(fPublishList);
        fPublishList = NULL;
}