diff options
Diffstat (limited to '')
-rw-r--r-- | libgamepad_find_sound_devices.c | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/libgamepad_find_sound_devices.c b/libgamepad_find_sound_devices.c new file mode 100644 index 0000000..1688106 --- /dev/null +++ b/libgamepad_find_sound_devices.c @@ -0,0 +1,92 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static int +search(int dirfd, const char *file, size_t **cardsp, size_t *countp, struct stat *st_buf) +{ + int fd; + DIR *dir; + struct dirent *f; + unsigned long num; + char *end; + void *new; + + fd = openat(dirfd, file, O_DIRECTORY); + if (fd < 0) + return -1; + dir = fdopendir(fd); + if (!dir) { + close(fd); + return -1; + } + + if (!strcmp(file, "sound")) { + while (errno = 0, (f = readdir(dir))) { + if (strncmp(f->d_name, "card", 4) || !isdigit(f->d_name[4])) + continue; + num = strtoul(&f->d_name[4], &end, 10); + if (errno || *end) + continue; + if (*countp == (size_t)SSIZE_MAX) + break; + new = realloc(*cardsp, (*countp + 1) * sizeof(**cardsp)); + if (!new) + goto fail; + *cardsp = new; + (*cardsp)[(*countp)++] = (size_t)num; + } + } else { + while (errno = 0, (f = readdir(dir))) { + if (f->d_name[0] == '.') + continue; + if (fstatat(fd, f->d_name, st_buf, AT_SYMLINK_NOFOLLOW)) + goto fail; + if (S_ISLNK(st_buf->st_mode)) + continue; + search(fd, f->d_name, cardsp, countp, st_buf); + } + } + + closedir(dir); + return errno ? -1 : 0; + +fail: + closedir(dir); + return -1; +} + + +ssize_t +libgamepad_find_sound_devices(const char *syspath, size_t **cardsp) +{ + int fd, i, saved_errno = errno; + size_t count = 0; + struct stat st; + const char *p = syspath; + + *cardsp = NULL; + + if (*p != '/') + return 0; + for (i = 0; i < 5; i++) { + p = strchr(&p[1], '/'); + if (!p) + return 0; + } + + fd = open(syspath, O_DIRECTORY); + if (fd < 0) + return -1; + + if (search(fd, "../..", cardsp, &count, &st)) { + close(fd); + free(*cardsp); + *cardsp = NULL; + return -1; + } + + close(fd); + errno = saved_errno; + return (ssize_t)count; +} |