#include <sys/types.h>
#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void
set_bit(uint8_t *bits, unsigned long index)
{
    bits[index / 8] |= (1 << (7 - index % 8));
}

void
clear_bit(uint8_t *bits, unsigned long index)
{
    bits[index / 8] &= ~(1 << (7 - index % 8));
}

int
has_bit(uint8_t *bits, unsigned long index)
{
    return bits[index / 8] & (1 << (7 - index % 8));
}

int
set_nonblocking(int fd)
{
    int oflags;
    if ((oflags = fcntl(fd, F_GETFL, 0)) == -1)
	return errno;
    if (fcntl(fd, F_SETFL, oflags | O_NONBLOCK) == -1)
	return errno;
    return 0;
}

int
set_blocking(int fd)
{
    int oflags;
    if ((oflags = fcntl(fd, F_GETFL, 0)) == -1)
	return errno;
    if (fcntl(fd, F_SETFL, oflags & ~O_NONBLOCK) == -1)
	return errno;
    return 0;
}

int
mkdirs(char *path)
{
    int err = 0;
    char *spos = strchr(path + 1, '/'); // Must ignore the root

    while (spos != NULL) {
	*spos = '\0';
	err = mkdir(path, 0777);
	*spos = '/';
	
	if (err != 0 && errno != EEXIST) {
	    err = errno;
	    break;
	}

	spos = strchr(spos + 1, '/');
    }
    return err;
}

int
vopen(int *res, int flags, const char *fmt, ...)
{
    int fd, didmkdirs;
    char path[PATH_MAX + 1];
    va_list ap;

    va_start(ap, fmt);
    if (vsnprintf(path, PATH_MAX, fmt, ap) >= PATH_MAX) {
	va_end(ap);
	return ENAMETOOLONG;
    }
    va_end(ap);

    didmkdirs = 0;
again:
    fd = open(path, flags, 0666);
    if (fd < 0 && errno == ENOENT && (flags & O_CREAT) != 0 && !didmkdirs) {
	if (mkdirs(path) == 0) {
	    didmkdirs = 1;
	    goto again;
	} else
	    return errno;
    }

    if (fd >= 0) {
	*res = fd;
	return 0;
    } else
	return errno;
}

int
canon_path(const char *path, char **res)
{
    char rp[PATH_MAX];

    if (realpath(path, rp) == NULL)
	return errno;
#if 0
    // This could be necessary on solaris.
    if (rp[0] != '/') {
	char wd[MAXPATHLEN];
	if (getcwd(wd, MAXPATHLEN) == NULL)
	    return errno;
	if (strlcat(wd, "/", MAXPATHLEN) >= MAXPATHLEN)
	    return ENAMETOOLONG;
	if (strlcat(wd, rp, MAXPATHLEN) >= MAXPATHLEN)
	    return ENAMETOOLONG;
	strcpy(rp, wd);
    }
#endif
    if ((*res = strdup(rp)) == NULL)
	return ENOMEM;

    return 0;
}

size_t
round_to_page(size_t size)
{
    size_t psize = getpagesize();
    size_t rem = size % psize;
    if (rem != 0)
	size += psize - rem;
    return size;
}