/*
 * Utilities
 *
 * Copyright (C) 2010 by Multi-Tech Systems
 *
 * Author: James Maki <jmaki@multitech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <linux/limits.h>

#include "utils.h"
#include "log.h"

int tokcmp(const char *cmd, const char *pattern)
{
	int len = strlen(cmd);

	if (len > strlen(pattern)) {
		return -1;
	}

	return memcmp(pattern, cmd, len);
}

char *strrstrip(char *str)
{
	char *end;
	char *prev;

	if (!str || !*str) {
		return NULL;
	}
	prev = end = str + strlen(str);

	while (end > str && isspace(*(end - 1))) {
		end--;
	}
	*end = '\0';

	return prev == end ? NULL : str;
}

ssize_t safe_read(int fd, void *buf, size_t count)
{
	ssize_t n;

	do {
		n = read(fd, buf, count);
	} while (n < 0 && errno == EINTR);

	return n;
}

ssize_t safe_readn(int fd, void *buf, size_t len)
{
	ssize_t cc;
	ssize_t total;

	total = 0;

	while (len) {
		cc = safe_read(fd, buf, len);

		if (cc < 0) {
			if (total) {
				return total;
			}
			return cc;
		}

		total += cc;
		buf = buf + cc;
		len -= cc;
	}

	return total;
}

ssize_t safe_write(int fd, const void *buf, size_t count)
{
	ssize_t n;

	do {
		n = write(fd, buf, count);
	} while (n < 0 && errno == EINTR);

	return n;
}

ssize_t full_write(int fd, const void *buf, size_t len)
{
	ssize_t cc;
	ssize_t total;

	total = 0;

	while (len) {
		cc = safe_write(fd, buf, len);

		if (cc < 0) {
			if (total) {
				return total;
			}
			return cc;
		}

		total += cc;
		buf = ((const char *)buf) + cc;
		len -= cc;
	}

	return total;
}

int user_yesno(int def, const char *fmt, ...)
{
	char buf[128];
	char *line;
	va_list ap;

	while (1) {
		va_start(ap, fmt);
		vprintf(fmt, ap);
		va_end(ap);

		printf("? [%c/%c] ",
			def == USER_YESNO_YES ? 'Y' : 'y',
			def == USER_YESNO_NO ? 'N' : 'n');

		line = fgets(buf, sizeof(buf), stdin);
		if (!line) {
			return -1;
		}

		switch (toupper(*line)) {
		case '\n':
			return def;
		case 'Y':
			return USER_YESNO_YES;
		case 'N':
			return USER_YESNO_NO;
		}
	}

	return -1;
}

int systemf(const char *fmt, ...)
{
	int err;
	va_list ap;
	char *buf;

	va_start(ap, fmt);
	err = vasprintf(&buf, fmt, ap);
	va_end(ap);
	if (err < 0) {
		log_error("out of memory");
		return -1;
	}

	err = system(buf);
	free(buf);

	return err;
}

FILE *popenf(const char *mode, const char *fmt, ...)
{
	int err;
	va_list ap;
	char *buf;
	FILE *pipe;

	va_start(ap, fmt);
	err = vasprintf(&buf, fmt, ap);
	va_end(ap);
	if (err < 0) {
		log_error("out of memory");
		return NULL;
	}

	pipe = popen(buf, mode);
	free(buf);

	return pipe;
}

char *shell_path_expand(const char *path)
{
	char buf[PATH_MAX + 1];
	FILE *file;
	char *line;

	file = popenf("r", "echo -n \"%s\"", path);
	if (!file) {
		log_error("popen failed");
		return NULL;
	}

	line = fgets(buf, sizeof(buf), file);

	pclose(file);

	if (!line) {
		log_error("no line read");
		return NULL;
	}

	return strdup(line);
}