#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "iolib.h"
#include "freedt.h"
#include "config.h"
/* SuSv3 says this is in unistd.h, but glibc and others don't include it. */
extern char **environ;

void warn(const char *msg) {
	format(fd_err, "@c: @c\n", progname, msg);
}

void warn2(const char *msg1, const char *msg2) {
	format(fd_err, "@c: @c: @c\n", progname, msg1, msg2);
}

void die(const char *msg) {
	warn(msg);
	exit(111);
}

void die2(const char *msg1, const char *msg2) {
	warn2(msg1, msg2);
	exit(111);
}

static void show_version() {
	format(fd_out, "@c from freedt version " VERSION "\n", progname);
}

void version() {
	show_version();
	exit(1);
}

void help() {
	show_version();
	format(fd_out, "\n@c"
		"-V        Show version information\n"
		"-?        Show usage information\n", proghelp);
	exit(1);
}

void get_default_args(int argc, char **argv) {
	while (1) {
		int c = getopt(argc, argv, "+V?");
		if (c == -1)
			break;

		switch (c) {
		case 'V':
			version();
		default:
			help();
		}
	}
}

void setuidgidroot() {
	char *root, *uid, *gid;

	root = getenv("ROOT");
	if (root != NULL) {
		if (chdir(root) < 0)
			die2(root, "unable to chdir");
		if (chroot(root) < 0)
			die2(root, "unable to chroot");
	}

	gid = getenv("GID");
	if (gid != NULL) {
		gid_t g = atoi(gid);

		if (setgid(g) < 0)
			die("unable to setgid");
		if (setgroups(1, &g) < 0)
			die("unable to setgroups");
	}

	uid = getenv("UID");
	if (uid != NULL) {
		uid_t u = atoi(uid);

		if (setuid(u) < 0)
			die("unable to setuid");
	}
}

static int environ_size() {
	int num = 0;
	char **p;

	for (p = environ; *p != NULL; p++)
		++num;

	return num;
}

static int build_environ_copy() {
	static int have_run = 0;
	char **new_environ;
	int num, i;

	if (have_run)
		return 0;
	have_run = 1;

	num = environ_size();
	new_environ = malloc((num + 1) * sizeof *new_environ);
	if (new_environ == NULL)
		return -1;
	for (i = 0; i < num; i++) {
		new_environ[i] = strdup(environ[i]);
		if (new_environ[i] == NULL) {
			while (--i >= 0) {
				free(new_environ[i]);
			}
			free(new_environ);
			return -1;
		}
	}
	new_environ[num] = NULL;

	environ = new_environ;
	return 0;
}

static char **find_environ(const char *name) {
	char **p;
	int l = strlen(name);

	for (p = environ; *p != NULL; p++) {
		if (strlen(*p) <= l)
			continue;
		if (strncmp(*p, name, l) == 0 && (*p)[l] == '=')
			return p;
	}
	return NULL;
}

int fdt_setenv(const char *name, const char *value) {
	char *s;
	char **p;

	if (build_environ_copy() < 0)
		return -1;

	s = malloc(strlen(name) + strlen(value) + 2);
	if (s == NULL)
		return -1;
	strcpy(s, name);
	strcat(s, "=");
	strcat(s, value);

	p = find_environ(name);
	if (p == NULL) {
		int num = environ_size();
		char **new_environ;

		new_environ = realloc(environ, (num + 2) * sizeof *environ);
		if (new_environ == NULL) {
			free(s);
			return -1;
		}
		new_environ[num] = s;
		new_environ[num + 1] = NULL;
		environ = new_environ;
	} else {
		free(*p);
		*p = s;
	}

	return 0;
}

int fdt_unsetenv(const char *name) {
	char **p;
	
	if (build_environ_copy() < 0)
		return -1;

	p = find_environ(name);
	if (p == NULL)
		return -1;

	do {
		*p = *(p + 1);
	} while (*p++ != NULL);

	return 0;	
}

void change_fd_flags(int fd, int add, int remove) {
	int flags = fcntl(fd, F_GETFL);
	if (flags < 0)
		die("unable to read FD flags");

	flags |= add;
	flags &= ~remove;

	if (fcntl(fd, F_SETFL, flags) < 0)
		die("unable to set FD flags");
}

void set_fd_cloexec(int fd) {
	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
		die("unable to set FD_CLOEXEC");
}

void reliable_sleep(int seconds) {
	while (seconds > 0)
		seconds = sleep(seconds);
}

int lock_fd(int fd, int block) {
	struct flock lock;
	int rc;

	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	do {
		rc = fcntl(fd, block ? F_SETLKW : F_SETLK, &lock);
	} while (rc < 0 && errno == EINTR);
	return rc;
}


syntax highlighted by Code2HTML, v. 0.9.1