<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ty/common.h"
#include "compat.h"
#include <libudev.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "ty/device.h"
#include "device_priv.h"
#include "device_posix_priv.h"
#include "ty/system.h"

struct ty_device_monitor {
    TY_DEVICE_MONITOR

    struct udev_monitor *monitor;
};

struct udev_aggregate {
    struct udev_device *dev;
    struct udev_device *usb;
    struct udev_device *iface;
};

static const char *device_subsystems[] = {
    "input",
    "hidraw",
    "tty",
    NULL
};

static struct udev *udev;

static int compute_device_location(const char *key, char **rlocation)
{
    const char *end;
    uint8_t buf;
    char *location;
    int r, len;

    key = strstr(key, "/usb");
    if (!key || strlen(key) < 5)
        return 0;
    key += 5;

    r = sscanf(key, "/%hhu%n", &buf, &len);
    if (r < 1)
        return 0;
    end = key + len;

    do {
        len = 0;
        r = sscanf(end, "-%hhu%n", &buf, &len);
        end += len;
    } while (r == 1);

    if (*end != '/')
        return 0;

    key++;

    // Account for 'usb-' prefix and NUL byte
    location = malloc((size_t)(end - key) + 5);
    if (!location)
        return ty_error(TY_ERROR_MEMORY, NULL);

    strcpy(location, "usb-");
    strncat(location, key, (size_t)(end - key));

    *rlocation = location;
    return 1;
}

static int fill_device_details(ty_device *dev, struct udev_aggregate *agg)
{
    const char *buf;
    int r;

    buf = udev_device_get_subsystem(agg->dev);
    if (!buf)
        return 0;

    if (strcmp(buf, "hidraw") == 0) {
        dev->type = TY_DEVICE_HID;
    } else if (strcmp(buf, "tty") == 0) {
        dev->type = TY_DEVICE_SERIAL;
    } else {
        return 0;
    }
    dev->vtable = &_ty_posix_device_vtable;

    buf = udev_device_get_devnode(agg->dev);
    if (!buf || access(buf, F_OK) != 0)
        return 0;
    dev->path = strdup(buf);
    if (!dev->path)
        return ty_error(TY_ERROR_MEMORY, NULL);

    dev->key = strdup(udev_device_get_devpath(agg->dev));
    if (!dev->key)
        return ty_error(TY_ERROR_MEMORY, NULL);

    r = compute_device_location(dev->key, &dev->location);
    if (r <= 0)
        return r;

    errno = 0;
    buf = udev_device_get_property_value(agg->usb, "ID_VENDOR_ID");
    if (!buf)
        return 0;
    dev->vid = (uint16_t)strtoul(buf, NULL, 16);
    if (errno)
        return 0;

    errno = 0;
    buf = udev_device_get_property_value(agg->usb, "ID_MODEL_ID");
    if (!buf)<commit_msg>Fix device location parsing on Linux<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ty/common.h"
#include "compat.h"
#include <libudev.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "ty/device.h"
#include "device_priv.h"
#include "device_posix_priv.h"
#include "ty/system.h"

struct ty_device_monitor {
    TY_DEVICE_MONITOR

    struct udev_monitor *monitor;
};

struct udev_aggregate {
    struct udev_device *dev;
    struct udev_device *usb;
    struct udev_device *iface;
};

static const char *device_subsystems[] = {
    "input",
    "hidraw",
    "tty",
    NULL
};

static struct udev *udev;

static int compute_device_location(struct udev_device *dev, char **rlocation)
{
    const char *busnum, *devpath;
    char *location;
    int r;

    busnum = udev_device_get_sysattr_value(dev, "busnum");
    devpath = udev_device_get_sysattr_value(dev, "devpath");

    if (!busnum || !devpath)
        return 0;

    r = asprintf(&location, "usb-%s-%s", busnum, devpath);
    if (r < 0)
        return ty_error(TY_ERROR_MEMORY, NULL);

    for (char *ptr = location; *ptr; ptr++) {
        if (*ptr == '.')
            *ptr = '-';
    }

    *rlocation = location;
    return 1;
}

static int fill_device_details(ty_device *dev, struct udev_aggregate *agg)
{
    const char *buf;
    int r;

    buf = udev_device_get_subsystem(agg->dev);
    if (!buf)
        return 0;

    if (strcmp(buf, "hidraw") == 0) {
        dev->type = TY_DEVICE_HID;
    } else if (strcmp(buf, "tty") == 0) {
        dev->type = TY_DEVICE_SERIAL;
    } else {
        return 0;
    }
    dev->vtable = &_ty_posix_device_vtable;

    buf = udev_device_get_devnode(agg->dev);
    if (!buf || access(buf, F_OK) != 0)
        return 0;
    dev->path = strdup(buf);
    if (!dev->path)
        return ty_error(TY_ERROR_MEMORY, NULL);

    dev->key = strdup(udev_device_get_devpath(agg->dev));
    if (!dev->key)
        return ty_error(TY_ERROR_MEMORY, NULL);

    r = compute_device_location(agg->usb, &dev->location);
    if (r <= 0)
        return r;

    errno = 0;
    buf = udev_device_get_property_value(agg->usb, "ID_VENDOR_ID");
    if (!buf)
        return 0;
    dev->vid = (uint16_t)strtoul(buf, NULL, 16);
    if (errno)
        return 0;

    errno = 0;
    buf = udev_device_get_property_value(agg->usb, "ID_MODEL_ID");
    if (!buf)
{'fraction_non_alphanumeric': 0.14296534747206968, 'fraction_numerical': 0.009846619958341223, 'mean_word_length': 2.75408670931059, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6495115', 'n_tokens_mistral': 2030, 'n_tokens_neox': 1954, 'n_words': 489}