root/tools/usb/usbip/libsrc/names.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *      names.c  --  USB name database manipulation routines
 *
 *      Copyright (C) 1999, 2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *      Copyright (C) 2005 Takahiro Hirofuchi
 *              - names_deinit() is added.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>

#include "names.h"
#include "usbip_common.h"

struct vendor {
        struct vendor *next;
        u_int16_t vendorid;
        char name[1];
};

struct product {
        struct product *next;
        u_int16_t vendorid, productid;
        char name[1];
};

struct class {
        struct class *next;
        u_int8_t classid;
        char name[1];
};

struct subclass {
        struct subclass *next;
        u_int8_t classid, subclassid;
        char name[1];
};

struct protocol {
        struct protocol *next;
        u_int8_t classid, subclassid, protocolid;
        char name[1];
};

struct genericstrtable {
        struct genericstrtable *next;
        unsigned int num;
        char name[1];
};


#define HASH1  0x10
#define HASH2  0x02
#define HASHSZ 16

static unsigned int hashnum(unsigned int num)
{
        unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27;

        for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1)
                if (num & mask1)
                        num ^= mask2;
        return num & (HASHSZ-1);
}


static struct vendor *vendors[HASHSZ] = { NULL, };
static struct product *products[HASHSZ] = { NULL, };
static struct class *classes[HASHSZ] = { NULL, };
static struct subclass *subclasses[HASHSZ] = { NULL, };
static struct protocol *protocols[HASHSZ] = { NULL, };

const char *names_vendor(u_int16_t vendorid)
{
        struct vendor *v;

        v = vendors[hashnum(vendorid)];
        for (; v; v = v->next)
                if (v->vendorid == vendorid)
                        return v->name;
        return NULL;
}

const char *names_product(u_int16_t vendorid, u_int16_t productid)
{
        struct product *p;

        p = products[hashnum((vendorid << 16) | productid)];
        for (; p; p = p->next)
                if (p->vendorid == vendorid && p->productid == productid)
                        return p->name;
        return NULL;
}

const char *names_class(u_int8_t classid)
{
        struct class *c;

        c = classes[hashnum(classid)];
        for (; c; c = c->next)
                if (c->classid == classid)
                        return c->name;
        return NULL;
}

const char *names_subclass(u_int8_t classid, u_int8_t subclassid)
{
        struct subclass *s;

        s = subclasses[hashnum((classid << 8) | subclassid)];
        for (; s; s = s->next)
                if (s->classid == classid && s->subclassid == subclassid)
                        return s->name;
        return NULL;
}

const char *names_protocol(u_int8_t classid, u_int8_t subclassid,
                           u_int8_t protocolid)
{
        struct protocol *p;

        p = protocols[hashnum((classid << 16) | (subclassid << 8)
                              | protocolid)];
        for (; p; p = p->next)
                if (p->classid == classid && p->subclassid == subclassid &&
                    p->protocolid == protocolid)
                        return p->name;
        return NULL;
}

/* add a cleanup function by takahiro */
struct pool {
        struct pool *next;
        void *mem;
};

static struct pool *pool_head;

static void *my_malloc(size_t size)
{
        struct pool *p;

        p = calloc(1, sizeof(struct pool));
        if (!p)
                return NULL;

        p->mem = calloc(1, size);
        if (!p->mem) {
                free(p);
                return NULL;
        }

        p->next = pool_head;
        pool_head = p;

        return p->mem;
}

void names_free(void)
{
        struct pool *pool;

        if (!pool_head)
                return;

        for (pool = pool_head; pool != NULL; ) {
                struct pool *tmp;

                if (pool->mem)
                        free(pool->mem);

                tmp = pool;
                pool = pool->next;
                free(tmp);
        }
}

static int new_vendor(const char *name, u_int16_t vendorid)
{
        struct vendor *v;
        unsigned int h = hashnum(vendorid);

        v = vendors[h];
        for (; v; v = v->next)
                if (v->vendorid == vendorid)
                        return -1;
        v = my_malloc(sizeof(struct vendor) + strlen(name));
        if (!v)
                return -1;
        strcpy(v->name, name);
        v->vendorid = vendorid;
        v->next = vendors[h];
        vendors[h] = v;
        return 0;
}

static int new_product(const char *name, u_int16_t vendorid,
                       u_int16_t productid)
{
        struct product *p;
        unsigned int h = hashnum((vendorid << 16) | productid);

        p = products[h];
        for (; p; p = p->next)
                if (p->vendorid == vendorid && p->productid == productid)
                        return -1;
        p = my_malloc(sizeof(struct product) + strlen(name));
        if (!p)
                return -1;
        strcpy(p->name, name);
        p->vendorid = vendorid;
        p->productid = productid;
        p->next = products[h];
        products[h] = p;
        return 0;
}

static int new_class(const char *name, u_int8_t classid)
{
        struct class *c;
        unsigned int h = hashnum(classid);

        c = classes[h];
        for (; c; c = c->next)
                if (c->classid == classid)
                        return -1;
        c = my_malloc(sizeof(struct class) + strlen(name));
        if (!c)
                return -1;
        strcpy(c->name, name);
        c->classid = classid;
        c->next = classes[h];
        classes[h] = c;
        return 0;
}

static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid)
{
        struct subclass *s;
        unsigned int h = hashnum((classid << 8) | subclassid);

        s = subclasses[h];
        for (; s; s = s->next)
                if (s->classid == classid && s->subclassid == subclassid)
                        return -1;
        s = my_malloc(sizeof(struct subclass) + strlen(name));
        if (!s)
                return -1;
        strcpy(s->name, name);
        s->classid = classid;
        s->subclassid = subclassid;
        s->next = subclasses[h];
        subclasses[h] = s;
        return 0;
}

static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid,
                        u_int8_t protocolid)
{
        struct protocol *p;
        unsigned int h = hashnum((classid << 16) | (subclassid << 8)
                                 | protocolid);

        p = protocols[h];
        for (; p; p = p->next)
                if (p->classid == classid && p->subclassid == subclassid
                    && p->protocolid == protocolid)
                        return -1;
        p = my_malloc(sizeof(struct protocol) + strlen(name));
        if (!p)
                return -1;
        strcpy(p->name, name);
        p->classid = classid;
        p->subclassid = subclassid;
        p->protocolid = protocolid;
        p->next = protocols[h];
        protocols[h] = p;
        return 0;
}

static void parse(FILE *f)
{
        char buf[512], *cp;
        unsigned int linectr = 0;
        int lastvendor = -1;
        int lastclass = -1;
        int lastsubclass = -1;
        int lasthut = -1;
        int lastlang = -1;
        unsigned int u;

        while (fgets(buf, sizeof(buf), f)) {
                linectr++;
                /* remove line ends */
                cp = strchr(buf, '\r');
                if (cp)
                        *cp = 0;
                cp = strchr(buf, '\n');
                if (cp)
                        *cp = 0;
                if (buf[0] == '#' || !buf[0])
                        continue;
                cp = buf;
                if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' &&
                    buf[3] == 'S' && buf[4] == 'D' &&
                    buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/
                    buf[7] == ' ') {
                        continue;
                }
                if (buf[0] == 'P' && buf[1] == 'H' &&
                    buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') {
                        continue;
                }
                if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' &&
                    buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') {
                        continue;
                }
                if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') {
                        lasthut = lastclass = lastvendor = lastsubclass = -1;
                        /*
                         * set 1 as pseudo-id to indicate that the parser is
                         * in a `L' section.
                         */
                        lastlang = 1;
                        continue;
                }
                if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') {
                        /* class spec */
                        cp = buf+2;
                        while (isspace(*cp))
                                cp++;
                        if (!isxdigit(*cp)) {
                                err("Invalid class spec at line %u", linectr);
                                continue;
                        }
                        u = strtoul(cp, &cp, 16);
                        while (isspace(*cp))
                                cp++;
                        if (!*cp) {
                                err("Invalid class spec at line %u", linectr);
                                continue;
                        }
                        if (new_class(cp, u))
                                err("Duplicate class spec at line %u class %04x %s",
                                    linectr, u, cp);
                        dbg("line %5u class %02x %s", linectr, u, cp);
                        lasthut = lastlang = lastvendor = lastsubclass = -1;
                        lastclass = u;
                        continue;
                }
                if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) {
                        /* audio terminal type spec */
                        continue;
                }
                if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C'
                    && isspace(buf[3])) {
                        /* HID Descriptor bCountryCode */
                        continue;
                }
                if (isxdigit(*cp)) {
                        /* vendor */
                        u = strtoul(cp, &cp, 16);
                        while (isspace(*cp))
                                cp++;
                        if (!*cp) {
                                err("Invalid vendor spec at line %u", linectr);
                                continue;
                        }
                        if (new_vendor(cp, u))
                                err("Duplicate vendor spec at line %u vendor %04x %s",
                                    linectr, u, cp);
                        dbg("line %5u vendor %04x %s", linectr, u, cp);
                        lastvendor = u;
                        lasthut = lastlang = lastclass = lastsubclass = -1;
                        continue;
                }
                if (buf[0] == '\t' && isxdigit(buf[1])) {
                        /* product or subclass spec */
                        u = strtoul(buf+1, &cp, 16);
                        while (isspace(*cp))
                                cp++;
                        if (!*cp) {
                                err("Invalid product/subclass spec at line %u",
                                    linectr);
                                continue;
                        }
                        if (lastvendor != -1) {
                                if (new_product(cp, lastvendor, u))
                                        err("Duplicate product spec at line %u product %04x:%04x %s",
                                            linectr, lastvendor, u, cp);
                                dbg("line %5u product %04x:%04x %s", linectr,
                                    lastvendor, u, cp);
                                continue;
                        }
                        if (lastclass != -1) {
                                if (new_subclass(cp, lastclass, u))
                                        err("Duplicate subclass spec at line %u class %02x:%02x %s",
                                            linectr, lastclass, u, cp);
                                dbg("line %5u subclass %02x:%02x %s", linectr,
                                    lastclass, u, cp);
                                lastsubclass = u;
                                continue;
                        }
                        if (lasthut != -1) {
                                /* do not store hut */
                                continue;
                        }
                        if (lastlang != -1) {
                                /* do not store langid */
                                continue;
                        }
                        err("Product/Subclass spec without prior Vendor/Class spec at line %u",
                            linectr);
                        continue;
                }
                if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) {
                        /* protocol spec */
                        u = strtoul(buf+2, &cp, 16);
                        while (isspace(*cp))
                                cp++;
                        if (!*cp) {
                                err("Invalid protocol spec at line %u",
                                    linectr);
                                continue;
                        }
                        if (lastclass != -1 && lastsubclass != -1) {
                                if (new_protocol(cp, lastclass, lastsubclass,
                                                 u))
                                        err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s",
                                            linectr, lastclass, lastsubclass,
                                            u, cp);
                                dbg("line %5u protocol %02x:%02x:%02x %s",
                                    linectr, lastclass, lastsubclass, u, cp);
                                continue;
                        }
                        err("Protocol spec without prior Class and Subclass spec at line %u",
                            linectr);
                        continue;
                }
                if (buf[0] == 'H' && buf[1] == 'I' &&
                    buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') {
                        continue;
                }
                if (buf[0] == 'H' && buf[1] == 'U' &&
                    buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') {
                        lastlang = lastclass = lastvendor = lastsubclass = -1;
                        /*
                         * set 1 as pseudo-id to indicate that the parser is
                         * in a `HUT' section.
                         */
                        lasthut = 1;
                        continue;
                }
                if (buf[0] == 'R' && buf[1] == ' ')
                        continue;

                if (buf[0] == 'V' && buf[1] == 'T')
                        continue;

                err("Unknown line at line %u", linectr);
        }
}


int names_init(char *n)
{
        FILE *f;

        f = fopen(n, "r");
        if (!f)
                return errno;

        parse(f);
        fclose(f);
        return 0;
}