#include <u.h>
#include <libc.h>

#include "common.h"

#define isempty(s) (*(s) == '\0')

static char *
urichr(char *s, char c, long n)
{
	for (; n; --n, ++s) {
		if (*s == c) return s;
	}
	return nil;
}

/*
	Returns an open descriptor of the file indicated by uri that is relative to the
	working directory. The uri may contain nil, and its length is denoted by urilen,
	however the buffer backing the uri must be at least one character longer than
	urilen to accomodate canonicalization for directories. The starting `/' does not
	change the semantics of the uri. Single dot segments do not get recognized.
	Dot-dot segments are respected, but breaking out of the root yields an error.
*/
int
traverse(char *uri, long *urilen, int *canonical)
{
	char *sep;
	int depth, fd;
	Dir *dir;
	if (urichr(uri, '\0', *urilen)) return -1; /* Paths can't contain `\0' anyway. */
	uri[*urilen] = '\0';
	if (uri[0] == '/') ++uri;
	depth = 0;
	while ((sep = strchr(uri, '/'))) {
		*sep = '\0';
		if (strcmp(uri, ".") == 0) return -1;
		if (strcmp(uri, "..") == 0) {
			if (depth == 0) return -1;
			if (chdir("..") < 0) sysfatal("chdir ..: %r");
			--depth;
		} else {
			if (chdir(uri) < 0) return -1;
			++depth;
		}
		uri = sep + 1;
		*sep = '/';
	}
	if (isempty(uri)) {
		/* No more segments and we chdired successfully to the last one. */
		if ((fd = open(".", OREAD)) < 0) sysfatal("open .: %r");
		*canonical = 1;
		return fd;
	}
	if (strcmp(uri, ".") == 0) return -1;
	if (strcmp(uri, "..") == 0) {
		if (depth == 0) return -1;
		if ((fd = open("..", OREAD)) < 0) sysfatal("open ..: %r");
		goto canonicalize;
	}
	if ((fd = open(uri, OREAD)) < 0) return -1;
	if (!(dir = dirfstat(fd))) sysfatal("stat %s: %r", uri);
	if ((dir->mode & DMDIR) == 0) {
		free(dir);
		*canonical = 1;
		return fd;
	}
	free(dir);
	if (chdir(uri) < 0) sysfatal("chdir %s: %r", uri);
canonicalize:
	uri[strlen(uri)] = '/';
	*urilen = *urilen + 1;
	*canonical = 0;
	return fd;
}

void
printfile(int fd)
{
	char buf[8192];
	long n;
	while ((n = read(fd, buf, (long)sizeof(buf))) > 0) {
		write(1, buf, n);
	}
}

static char *CMD;
static char **ARGS;

void
setcmd(char *cmd, char *args[])
{
	CMD = cmd;
	ARGS = args;
}

void
runcmd(void)
{
	int pid;
	if ((pid = fork()) < 0) sysfatal("fork: %r");
	if (pid == 0) {
		exec(CMD, ARGS);
		sysfatal("exec: %r");
	}
	waitpid();
}

char *
mimetype(char *filename)
{
	char *ext;
	if (!(ext = strrchr(filename, '.'))) {
		if (strcmp(filename, "Makefile") == 0) return "text/plain; charset=utf-8";
		return nil;
	}
	++ext;
	if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0) return "text/html; charset=utf-8";
	if (strcmp(ext, "css") == 0) return "text/css; charset=utf-8";
	if (strcmp(ext, "txt") == 0) return "text/plain; charset=utf-8";
	if (strcmp(ext, "jpg") == 0 || strcmp(ext, "jpeg") == 0) return "image/jpeg";
	if (strcmp(ext, "png") == 0) return "image/png";
	if (strcmp(ext, "gif") == 0) return "image/gif";
	if (strcmp(ext, "svg") == 0) return "image/svg+xml";
	if (strcmp(ext, "mp4") == 0) return "video/mp4";
	if (strcmp(ext, "wav") == 0) return "audio/wav";
	if (strcmp(ext, "pdf") == 0) return "application/pdf";
	if (strcmp(ext, "ico") == 0) return "image/vnd.microsoft.icon";
	if (strcmp(ext, "gmi") == 0) return "text/gemini; charset=utf-8";
	if (strcmp(ext, "csv") == 0) return "text/csv; charset=utf-8";
	if (strcmp(ext, "xml") == 0) return "text/xml; charset=utf-8";
	if (strcmp(ext, "opml") == 0) return "text/x-opml; charset=utf-8";
	if (strcmp(ext, "tal") == 0) return "text/plain; charset=utf-8";
	if (strcmp(ext, "tex") == 0) return "text/plain; charset=utf-8";
	if (strcmp(ext, "c") == 0) return "text/plain; charset=utf-8";
	if (strcmp(ext, "xxd") == 0) return "text/plain; charset=utf-8";
	if (strcmp(ext, "md") == 0) return "text/markdown; charset=utf-8";
	if (strcmp(ext, "exe") == 0) return "application/x-msdownload";
	return nil;
}
