aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad_find_sound_devices.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libgamepad_find_sound_devices.c92
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;
+}