/*
 * libc/inet/ethers.c
 *
 * Programmatic interface for the /etc/ethers file
 *
 * Copyright 2007 by Matthew Wilcox <matthew@wil.cx>
 *
 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
 */

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <netinet/ether.h>

#define ETHER_LINE_LEN	256

/*
 * Internal function which returns a pointer to the part of the line
 * with the start of the hostname, or NULL if we couldn't parse the line.
 * Note that this line may have a comment symbol on it somewhere; if so
 * it will return NULL if the # is before or within the ether_addr, and
 * succeed if the # is before or within the host.  It's up to the callers
 * to be aware of this.
 *
 * I would have preferred to write a NUL to the location of the comment
 * character, but ether_line takes a const argument.  See __ether_line_w.
 */
static const char *__ether_line(const char *line, struct ether_addr *addr)
{
	struct ether_addr *res = ether_aton_r(line, addr);
	if (!res)
		return NULL;

	while (*line && (*line != '\n') && (*line != ' ') && (*line != '\t'))
		line++;
	while (*line && (*line != '\n') && ((*line == ' ') || (*line == '\t')))
		line++;
	return (*line && (*line != '\n')) ? line : NULL;
}

/*
 * Strips out the comment before calling __ether_line.  We can do this,
 * since we know the buffer is writable.
 */
static const char *__ether_line_w(char *line, struct ether_addr *addr)
{
	char *end = strpbrk(line, "#\n");
	if (end)
		*end = '\0';
	return __ether_line(line, addr);
}

int ether_line(const char *line, struct ether_addr *addr, char *hostname)
{
	const char *name = __ether_line(line, addr);
	if (!name)
		return -1;

	while (*name) {
		if ((*name == '#') || isspace(*name))
			break;
		*hostname++ = *name++;
	}
	*hostname = '\0';

	return 0;
}

int ether_ntohost(char *hostname, const struct ether_addr *addr)
{
	int res = -1;
	FILE *fp;
	char buf[ETHER_LINE_LEN];

	fp = fopen(ETHER_FILE_NAME, "r");
	if (!fp)
		return -1;

	while (fgets(buf, sizeof(buf), fp)) {
		struct ether_addr tmp_addr;
		const char *cp = __ether_line_w(buf, &tmp_addr);
		if (!cp)
			continue;
		if (memcmp(addr, &tmp_addr, sizeof(tmp_addr)))
			continue;

		strcpy(hostname, cp);
		res = 0;
		break;
	}

	fclose(fp);
	return res;
}

int ether_hostton(const char *hostname, struct ether_addr *addr)
{
	int res = -1;
	FILE *fp;
	char buf[ETHER_LINE_LEN];

	fp = fopen(ETHER_FILE_NAME, "r");
	if (!fp)
		return -1;

	while (fgets(buf, sizeof(buf), fp)) {
		const char *cp = __ether_line_w(buf, addr);
		if (!cp)
			continue;
		if (strcasecmp(hostname, cp))
			continue;

		res = 0;
		break;
	}

	fclose(fp);
	return res;
}
