aboutsummaryrefslogblamecommitdiffstats
path: root/libgamepad_find_sound_devices.c
blob: 168810670c50c0332bc00b130da697580a3e3ad1 (plain) (tree)



























































































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