cake
/
libcasio
Archived
1
1
Fork 0
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libcasio/lib/stream/builtin/linux/list_serial.c

164 lines
3.9 KiB
C

/* ****************************************************************************
* comlist/builtin/linux.c -- find out Linux serial devices.
* Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libcasio.
* libcasio is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libcasio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libcasio; if not, see <http://www.gnu.org/licenses/>.
* ************************************************************************* */
#include "../builtin.h"
#ifndef LIBCASIO_DISABLED_LINUX_SERIAL
# include <sys/stat.h>
# include <dirent.h>
# include <fcntl.h>
# include <unistd.h>
# include <errno.h>
typedef struct lincookie {
char *path;
char *devname;
size_t path_max;
DIR *dp;
char *start;
} lincookie_t;
/**
* next_serial_port:
* Get the next serial port.
*
* @arg cookie the lincookie.
* @arg ptr the pointer to the next path.
* @return the error code (0 if ok).
*/
CASIO_LOCAL int CASIO_EXPORT next_serial_port(lincookie_t *cookie,
char const **ptr)
{
struct dirent *dr;
struct stat st;
ssize_t rl;
char *res;
while (1) {
/* Get the next entry. */
if (!(dr = readdir(cookie->dp)))
return (casio_error_iter);
/* Copy that into the path and check the type. */
strcpy(cookie->start, dr->d_name);
if (lstat(cookie->path, &st) || (st.st_mode & S_IFMT) != S_IFLNK)
continue;
/* Get the destination path. */
rl = readlink(cookie->path, cookie->devname, PATH_MAX + 1);
if (rl < 0)
continue;
cookie->devname[rl] = 0;
/* If the path is a relative one, adapt it. */
if (cookie->devname[0] != '/') {
strcpy(cookie->start, cookie->devname);
if (!(res = realpath(cookie->path, cookie->devname)))
continue;
}
break;
}
*ptr = cookie->devname;
return (0);
}
/**
* end_iter:
* End the iterator by freeing all related allocated resources.
*
* @arg cookie the cookie.
*/
CASIO_LOCAL void CASIO_EXPORT end_iter(lincookie_t *cookie)
{
closedir(cookie->dp);
casio_free(cookie);
}
/* Functions. */
CASIO_LOCAL casio_iter_funcs_t linux_serial_functions = {
(casio_next_t *)&next_serial_port,
NULL,
(casio_end_t *)&end_iter
};
/**
* casio_iter_linux_serial:
* List serial ports under Linux.
*
* Links in the /dev/serial/by-id/ should resolve to relative paths, but
* I also managed absolute paths, in case.
*
* @arg iterp the iterator to create.
* @return the error.
*/
int CASIO_EXPORT casio_iter_linux_serial(casio_iter_t **iterp)
{
lincookie_t *cookie;
size_t path_max;
/* Get the maximum path size.
* Snippet found in `man 3 realpath`, glibc version. */
#ifdef PATH_MAX
path_max = PATH_MAX;
#else
path_max = pathconf(path, _PC_PATH_MAX);
if (path_max <= 0)
path_max = 4096;
#endif
++path_max;
/* Create the cookie. */
if (!(cookie = casio_alloc(sizeof(*cookie) + path_max + path_max, 1)))
return (casio_error_alloc);
cookie->path = (char *)(void *)&cookie[1];
cookie->devname = cookie->path + path_max;
/* Open the directory stream. */
strcpy(cookie->path, "/dev/serial/by-id/");
if (!(cookie->dp = opendir(cookie->path))) {
casio_free(cookie);
/* TODO: check errno */
return (casio_error_unknown);
}
/* Prepare what's left to prepare in the cookie. */
cookie->start = strchr(cookie->path, '\0');
cookie->path_max = path_max;
/* Create the iterator. */
return (casio_iter(iterp, cookie, linux_serial_functions));
}
# endif /* __linux__ */