/* 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; }