/*
 * Copyright (c) 2003, 2004 Nokia
 * Author: tsavola@movial.fi
 *
 * This program is licensed under GPL (see COPYING for details)
 */

#include "types.h"
#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>

const char *oom = "Out of memory";

void oom_error(void)
{
	errno = 0;
	error(oom);
}

/**
 * Sets a file descriptor (non-)blocking.
 * @param fd the behaviour of this file descriptor will be changed
 * @param nonblock if TRUE, fd is set nonblocking; if FALSE, fd is set blocking
 */
int set_nonblocking(int fd, bool_t nonblock)
{
	int val;

	val = fcntl(fd, F_GETFL, 0);
	if (val < 0)
		return -1;

	if (nonblock) {
		if (val & O_NONBLOCK)
			return 0;

		val |= O_NONBLOCK;
	} else {
		if (!(val & O_NONBLOCK))
			return 0;

		val &= ~O_NONBLOCK;
	}

	return fcntl(fd, F_SETFL, val);
}

/**
 * Sets a file descriptor to close-on-exec.
 */
int set_closeonexec(int fd)
{
	int val;

	val = fcntl(fd, F_GETFD, 0);
	if (val < 0)
		return -1;

	if (val & FD_CLOEXEC)
		return 0;

	return fcntl(fd, F_SETFD, val | FD_CLOEXEC);
}

int setsockopt_bool(int s, int level, int sockopt, bool_t value)
{
	return setsockopt(s, level, sockopt, &value, sizeof (value));
}

/**
 * Gets a neat progname.
 */
char *get_progname(char *name)
{
	char *p;

	p = strrchr(name, '/');
	if (p && strlen(p) >= 2)
		return &p[1];
	else
		return name;
}

uint32_t resolve(const char *hostname)
{
	struct hostent *host;
	uint32_t addr;

	host = gethostbyname(hostname);
	if (!host)
		goto _err;

	addr = *(uint32_t *) host->h_addr;
	if (!addr)
		goto _err;

	return ntohl(addr);

_err:
	error("Can't resolve host: %s", hostname);
	return 0;
}

/**
 * Frees all components of a null-terminated vector with a given function and
 * the vector itself. If the function is NULL, free(3) will be used. If vec is
 * NULL, nothing happens.
 * @param vec tor
 * @param func tion
 */
void free_vec(void **vec, free_func_t *func)
{
	void **p;

	if (!vec)
		return;

	if (!func)
		func = (free_func_t *) free;

	for (p = vec; *p; ++p)
		func(*p);

	free(vec);
}

size_t calc_vec_len(void **vec)
{
	size_t len = 0;

	if (vec)
		while (*vec++)
			++len;

	return len;
}

static ssize_t do_read_line(FILE *file, char *buf, size_t size,
                            char comment_char, bool_t *blankp)
{
	bool_t blank = TRUE;
	bool_t comm = FALSE;
	size_t i = 0;
	int c;

	if (feof(file))
		return -1;

	while (1) {
		c = fgetc(file);
		if (c == EOF || c == '\n')
			break;

		if (!isspace(c))
			blank = FALSE;

		if (c == comment_char)
			comm = TRUE;

		if (!comm && i < size - 1) {
			*buf++ = c;
			i++;
		}
	}
	*buf = '\0';

	if (blankp)
		*blankp = blank;

	return i;
}

ssize_t read_line(FILE *file, char *buf, size_t size)
{
	return do_read_line(file, buf, size, '#', NULL);
}

ssize_t read_line_blank(FILE *file, char *buf, size_t size, bool_t *blankp)
{
	return do_read_line(file, buf, size, '#', blankp);
}

/**
 * Reads a line, not stopping on comments.
 */
ssize_t read_line_nocomments(FILE *file, char *buf, size_t size)
{
	return do_read_line(file, buf, size, '\0', NULL);
}

char *skip_spaces(const char *buf)
{
	while (*buf && isspace(*buf))
		++buf;

	return (char *) buf;
}

char *find_space(const char *buf)
{
	while (*buf && !isspace(*buf))
		++buf;

	return (char *) buf;
}

char *find_line(char *buf)
{
	while (*buf && *buf++ != '\n')
		;

	return buf;
}

char *trim_string(char *buf)
{
	char *next;

	buf = skip_spaces(buf);

	for (next = buf; 1; ) {
		char *end = find_space(next);

		if (*end == '\0')
			break;

		next = skip_spaces(end);
		if (*next == '\0') {
			*end = '\0';
			break;
		}
	}

	return buf;
}

int split_string(char *line, ...)
{
	va_list arg;
	char **strp, *str;
	int n = 0;

	va_start(arg, line);

	while (1) {
		strp = va_arg(arg, char **);
		if (!strp)
			break;

		if (strlen(line) == 0) {
			*strp = NULL;
			continue;
		}

		str = line;

		line = find_space(line);
		if (*line) {
			*line++ = '\0';
			line = skip_spaces(line);
		}

		if (strlen(str) > 0) {
			*strp = str;
			n++;
		} else {
			*strp = NULL;
		}
	}

	va_end(arg);

	return n;
}

size_t string_cat(char *buf, size_t bufsize, const char *piece, ...)
{
	char *str;
	ssize_t avail;
	va_list arg;

	va_start(arg, piece);

	str = buf;
	*str = '\0';

	while (piece) {
		size_t len;

		avail = (bufsize - 1) - strlen(str);
		if (avail <= 0) {
			break;
		}

		len = strlen(piece);
		if (len > avail) {
			strncat(str, piece, avail);
			break;
		}

		strcat(str, piece);

		piece = va_arg(arg, char *);
	}

	va_end(arg);

	return strlen(buf);
}
