/* See LICENSE file for copyright and license details. */
#include "common.h"
int
libgamepad_list_superdevices(char ***devicesp, size_t *ndevicesp)
{
char *path, *target;
size_t path_size, target_size = 256;
const size_t path_size_base = sizeof("/sys/class/input//device/device");
int dirfd;
DIR *dir;
struct dirent *f;
ssize_t r;
char **paths = NULL;
size_t n = 0, i;
void *new;
int saved_errno = errno;
*devicesp = NULL;
*ndevicesp = 0;
dirfd = open("/sys/class/input/", O_DIRECTORY);
if (dirfd < 0)
return -1;
dir = fdopendir(dirfd);
if (!dir) {
close(dirfd);
return -1;
}
path_size = path_size_base + 255;
path_size = path_size > sizeof("/dev/fd/") + 3 * sizeof(int) ? path_size : sizeof("/dev/fd/") + 3 * sizeof(int);
path = malloc(path_size);
if (!path)
goto fail;
target = malloc(target_size);
if (!target)
goto fail;
while (errno = 0, (f = readdir(dir))) {
if (!strncmp(f->d_name, "event", 5) && isdigit(f->d_name[5])) {
if (path_size_base + strlen(f->d_name) > path_size) {
path_size = path_size_base + strlen(f->d_name);
new = realloc(path, path_size);
if (!new)
goto fail;
path = new;
}
stpcpy(stpcpy(stpcpy(path, "/sys/class/input/"), f->d_name), "/device/device");
dirfd = open(path, O_PATH);
if (dirfd < 0)
continue;
sprintf(path, "/dev/fd/%i", dirfd);
for (;;) {
r = readlink(path, target, target_size);
if (r < 0) {
close(dirfd);
goto next;
}
if ((size_t)r < target_size) {
close(dirfd);
target[r] = '\0';
break;
}
new = realloc(target, target_size += 256);
if (!new)
goto fail;
target = new;
}
if (paths)
for (i = 0; i < n; i++)
if (!strcmp(paths[i], target))
goto next;
stpcpy(stpcpy(path, target), "/input");
if (access(path, F_OK))
continue;
new = realloc(paths, (n + 1) * sizeof(*paths));
if (!new)
goto fail;
paths = new;
paths[n] = strdup(target);
if (!paths[n])
goto fail;
n += 1;
}
next:;
}
if (errno)
goto fail;
free(path);
free(target);
closedir(dir);
errno = saved_errno;
*devicesp = paths;
*ndevicesp = n;
return 0;
fail:
for (i = 0; i < n; i++)
free(paths[i]);
free(paths);
free(path);
free(target);
closedir(dir);
return -1;
}