/*
 * Copyright (C) 2003  Serge van den Boom
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 * Nota bene: later versions of the GNU General Public License do not apply
 * to this program.
 *
 * 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
 *
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#ifdef __unix__
#	include <sys/wait.h>
#endif

#include "debug.h"
#include "uioport.h"
#include "io.h"
#include "utils.h"
#include "types.h"
#include "mem.h"
#include "uioutils.h"
#ifdef uio_MEM_DEBUG
#	include "memdebug.h"
#endif

#define LINEBUFLEN 1024

typedef struct DebugContext {
	uio_bool exit;
	FILE *in;
	FILE *out;
	FILE *err;
	uio_DirHandle *cwd;
} DebugContext;

typedef struct {
	const char *name;
	int (*fun)(DebugContext *, int, char *[]);
} DebugCommand;

#ifdef STAND_ALONE
void initRepository(void);
void unInitRepository(void);
#endif

static int debugCmdCat(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdCd(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdExec(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdExit(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdLs(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdMem(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdMount(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdMv(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdRm(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdStat(DebugContext *debugContext, int argc, char *argv[]);
static int debugCmdWriteTest(DebugContext *debugContext, int argc,
		char *argv[]);
static int debugCmdFwriteTest(DebugContext *debugContext, int argc,
		char *argv[]);

static void makeArgs(char *lineBuf, int *argc, char ***argv);
static int debugCallCommand(DebugContext *debugContext,
		int argc, char *argv[]);
uio_MountHandle *
debugMountOne(uio_Repository *destRep, const char *mountPoint,
		uio_FileSystemID fsType,
		uio_DirHandle *sourceDir, const char *sourcePath,
		const char *inPath, uio_AutoMount **autoMount, int flags,
		uio_MountHandle *relative);

// In alphabetic order:
DebugCommand debugCommands[] = {
	{ "cat", debugCmdCat },
	{ "cd", debugCmdCd },
	{ "exec", debugCmdExec },
	{ "exit", debugCmdExit },
	{ "fwritetest", debugCmdFwriteTest },
	{ "ls", debugCmdLs },
	{ "mem", debugCmdMem },
	{ "mkdir", debugCmdMkDir },
	{ "mount", debugCmdMount },
	{ "mv", debugCmdMv },
	{ "pwd", debugCmdPwd },
	{ "rm", debugCmdRm },
	{ "rmdir", debugCmdRmDir },
	{ "stat", debugCmdStat },
	{ "writetest", debugCmdWriteTest },
};


#ifndef STAND_ALONE
extern uio_Repository *repository;
#else
uio_Repository *repository;
uio_MountHandle *mountHandles[9];  // TODO: remove (this is just a test)

int
main(int argc, char *argv[]) {
	initRepository();
	uio_debugInteractive(stdin, stdout, stderr);
	unInitRepository();
	(void) argc;
	(void) argv;
	return EXIT_SUCCESS;
}

uio_MountHandle *
debugMountOne(uio_Repository *destRep, const char *mountPoint,
		uio_FileSystemID fsType,
		uio_DirHandle *sourceDir, const char *sourcePath,
		const char *inPath, uio_AutoMount **autoMount, int flags,
		uio_MountHandle *relative) {
	uio_MountHandle *mountHandle;

	mountHandle = uio_mountDir(destRep, mountPoint, fsType, sourceDir,
			sourcePath, inPath, autoMount, flags, relative);
	if (mountHandle == NULL) {
		int savedErrno = errno;
		fprintf(stderr, "Could not mount '%s' and graft '%s' from that "
				"into the repository at '%s': %s\n",
				sourcePath, inPath, mountPoint, strerror(errno));
		errno = savedErrno;
	}
	return mountHandle;
}

void
initRepository(void) {
	static uio_AutoMount autoMountZip = {
		.pattern =      "*.zip",
		.matchType =    match_MATCH_SUFFIX,
		.fileSystemID = uio_FSTYPE_ZIP,
		.mountFlags =   uio_MOUNT_BELOW | uio_MOUNT_RDONLY
	};
	static uio_AutoMount *autoMount[] = {
		&autoMountZip,
		NULL
	};

	uio_init();
	repository = uio_openRepository(0);

	memset(&mountHandles, '\0', sizeof mountHandles);
#if 1
	mountHandles[0] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
			NULL, NULL, "/home/svdb/cvs/sc2/content", autoMount,
			uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
#if 1
	mountHandles[1] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
			NULL, NULL, "/home/svdb/cvs/sc2/src/sc2code/ships",
			autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
#if 1
	mountHandles[2] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
			NULL, NULL, "/tmp/vfstest", autoMount, uio_MOUNT_TOP, NULL);
#endif
#if 1
	mountHandles[3] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
			NULL, NULL, "/tmp/vfstest2", autoMount, uio_MOUNT_TOP, NULL);
#endif

	// TODO: should work too:
#if 0
	mountHandle[4] = debugMountOne(repository, "/zip/", uio_FSTYPE_ZIP, NULL,
			NULL, "/ziptest/foo.zip", autoMount, uio_MOUNT_TOP, NULL);
#endif
	{
		uio_DirHandle *rootDir;
		rootDir = uio_openDir(repository, "/", 0);
		if (rootDir == NULL) {
			fprintf(stderr, "Could not open '/' dir.\n");
		} else {
#if 1
			mountHandles[4] = debugMountOne(repository, "/example/",
					uio_FSTYPE_ZIP, rootDir, "/example2.zip", "/", autoMount,
					uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
#if 1
			mountHandles[5] = debugMountOne(repository, "/example/",
					uio_FSTYPE_ZIP, rootDir, "/example/example.zip", "/",
					autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
#if 1
			mountHandles[6] = debugMountOne(repository, "/zip/",
					uio_FSTYPE_ZIP, rootDir, "/voice.zip", "/", autoMount,
					uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
#if 1
			mountHandles[7] = debugMountOne(repository, "/foo/",
					uio_FSTYPE_ZIP, rootDir, "/foo2.zip", "/", autoMount,
					uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif
			uio_closeDir(rootDir);
		}
	}
	mountHandles[8] = debugMountOne(repository, "/tmp/",
			uio_FSTYPE_STDIO, NULL, NULL, "/tmp/", autoMount,
			uio_MOUNT_TOP, NULL);

#if 1
	mountHandles[8] = debugMountOne(repository, "/root/root/",
			uio_FSTYPE_STDIO, NULL, NULL, "/", autoMount,
			uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
#endif

}

void
unInitRepository(void) {
#if 1
	int i;
//	uio_printMountTree(stderr, repository->mountTree, 0);
//	fprintf(stderr, "\n");
	for (i = 7; i >= 0; i--) {
		if (mountHandles[i] != NULL)
			uio_unmountDir(mountHandles[i]);
//		uio_printMountTree(stderr, repository->mountTree, 0);
//		uio_printMounts(stderr, repository);
//		fprintf(stderr, "\n");
	}
#endif
	uio_closeRepository(repository);
	uio_unInit();
}
#endif  /* STAND_ALONE */

void
uio_debugInteractive(FILE *in, FILE *out, FILE *err) {
	char lineBuf[LINEBUFLEN];
	size_t lineLen;
	int argc;
	char **argv;
	DebugContext debugContext;
	uio_bool interactive;
	
	memset(&debugContext, '\0', sizeof (DebugContext));
	debugContext.exit = false;
	debugContext.in = in;
	debugContext.out = out;
	debugContext.err = err;
	debugContext.cwd = uio_openDir(repository, "/", 0);
	if (debugContext.cwd == NULL) {
		fprintf(err, "Fatal: Could not open working dir.\n");
		abort();
	}
	
	interactive = isatty(fileno(in));
	do {
		if (interactive)
			fprintf(out, "> ");
		if (fgets(lineBuf, LINEBUFLEN, in) == NULL) {
			if (feof(in)) {
				// user pressed ^D
				break;
			}
			// error occured
			clearerr(in);
			continue;
		}
		lineLen = strlen(lineBuf);
		if (lineBuf[lineLen - 1] != '\n' && lineBuf[lineLen - 1] != '\r') {
			fprintf(err, "Too long command line.\n");
			// TODO: read until EOL
			continue;
		}
		makeArgs(lineBuf, &argc, &argv);
		if (argc == 0) {
			uio_free(argv);
			continue;
		}
		debugCallCommand(&debugContext, argc, argv);
		uio_free(argv);
	} while (!debugContext.exit);
	if (interactive)
		fprintf(out, "\n");
	uio_closeDir(debugContext.cwd);
}

static void
makeArgs(char *lineBuf, int *argc, char ***argv) {
	int numArg;
	char **args;
	char *ptr;

	numArg = 0;
	ptr = lineBuf;
	while(true) {
		while (isspace((int) *ptr))
			ptr++;
		if (*ptr == '\0')
			break;
		numArg++;
		while (!isspace((int) *ptr))
			ptr++;
	}

	args = uio_malloc((numArg + 1) * sizeof (char *));
	numArg = 0;
	ptr = lineBuf;
	while(true) {
		while (isspace((int) *ptr))
			ptr++;
		if (*ptr == '\0')
			break;
		args[numArg] = ptr;
		numArg++;
		while (!isspace((int) *ptr))
			ptr++;
		if (*ptr == '\0')
			break;
		*ptr = '\0';
		ptr++;
	}
	args[numArg] = NULL;
	*argv = args;
	*argc = numArg;
}

static int
debugCallCommand(DebugContext *debugContext, int argc, char *argv[]) {
	int i;
	int numDebugCommands;
	
	i = 0;
	numDebugCommands = sizeof debugCommands / sizeof debugCommands[0];
	// could be improved with binary search
	while(1) {
		if (i == numDebugCommands) {
			fprintf(debugContext->err, "Invalid command.\n");
			return 1;
		}
		if (strcmp(argv[0], debugCommands[i].name) == 0)
			break;		
		i++;
	}
	return debugCommands[i].fun(debugContext, argc, argv);
}

static int
debugCmdCat(DebugContext *debugContext, int argc, char *argv[]) {
	uio_Handle *handle;
#define READBUFSIZE 0x10000
	char readBuf[READBUFSIZE];
	char *bufPtr;
	ssize_t numInBuf, numWritten;
	size_t totalWritten;

	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	handle = uio_open(debugContext->cwd, argv[1], O_RDONLY
#ifdef WIN32
			| O_BINARY
#endif
			, 0);
	if (handle == NULL) {
		fprintf(debugContext->err, "Could not open file: %s\n",
				strerror(errno));
		return 1;
	}

	totalWritten = 0;
	while (1) {
		numInBuf = uio_read(handle, readBuf, READBUFSIZE);
		if (numInBuf == -1) {
			if (errno == EINTR)
				continue;
			fprintf(debugContext->err, "Could not read from file: %s\n",
					strerror(errno));
			uio_close(handle);
			return 1;
		}
		if (numInBuf == 0)
			break;
		bufPtr = readBuf;
		do {
			numWritten = write(fileno(debugContext->out), bufPtr, numInBuf);
			if (numWritten == -1)
			{
				if (errno == EINTR)
					continue;
				fprintf(debugContext->err, "Could not read from file: %s\n",
						strerror(errno));
				uio_close(handle);
			}
			numInBuf -= numWritten;
			bufPtr += numWritten;
			totalWritten += numWritten;
		} while (numInBuf > 0);
	}
	fprintf(debugContext->out, "[%u bytes]\n", (unsigned int) totalWritten);
	
	uio_close(handle);
	return 0;
}

static int
debugCmdCd(DebugContext *debugContext, int argc, char *argv[]) {
	uio_DirHandle *newWd;

	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	newWd = uio_openDirRelative(debugContext->cwd, argv[1], 0);
	if (newWd == NULL) {
		fprintf(debugContext->err, "Could not access new dir: %s\n",
				strerror(errno));
		return 1;
	}
	uio_closeDir(debugContext->cwd);
	debugContext->cwd = newWd;
	return 0;
}

static int
debugCmdExec(DebugContext *debugContext, int argc, char *argv[]) {
	int i;
	const char **newArgs;
	int errCode = 0;
	uio_StdioAccessHandle **handles;
	uio_DirHandle *tempDir;

	if (argc < 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}

	tempDir = uio_openDirRelative(debugContext->cwd, "/tmp", 0);
	if (tempDir == 0) {
		fprintf(debugContext->err, "Could not open temp dir: %s.\n",
				strerror(errno));
		return 1;
	}

	newArgs = uio_malloc(argc * sizeof (char *));
	newArgs[0] = argv[1];
	handles = uio_malloc(argc * sizeof (uio_StdioAccessHandle *));
	handles[0] = NULL;
	
	for (i = 2; i < argc; i++) {
#if 0
		if (argv[i][0] == '-') {
			// Don't try to parse arguments that start with '-'.
			// They are probably option flags.
			newArgs[i - 1] = argv[i];
		}
#endif
		handles[i - 1] = uio_getStdioAccess(debugContext->cwd, argv[i],
				O_RDONLY, tempDir);
		if (handles[i - 1] == NULL) {
			if (errno == ENOENT) {
				// No match; we keep what's typed litterally.
				newArgs[i - 1] = argv[i];
				continue;
			}

			// error
			fprintf(debugContext->err,
					"Cannot execute: Cannot get stdio access to %s: %s.\n",
					argv[i], strerror(errno));
			errCode = 1;
			argc = i + 1;
			goto err;
		}

		newArgs[i - 1] = uio_StdioAccessHandle_getPath(handles[i - 1]);
	}
	newArgs[argc - 1] = NULL;

	fprintf(debugContext->err, "Executing: %s", newArgs[0]);
	for (i = 1; i < argc - 1; i++)
		fprintf(debugContext->err, " %s", newArgs[i]);
	fprintf(debugContext->err, "\n");

#ifdef __unix__
	{
		pid_t pid;
		
		pid = fork();
		switch (pid) {
			case -1:
				fprintf(debugContext->err, "Error: fork() failed: %s.\n",
						strerror(errno));
				break;
			case 0:
				// child
				execvp(newArgs[0], (char * const *) newArgs);
				fprintf(debugContext->err, "Error: execvp() failed: %s.\n",
						strerror(errno));
				_exit(EXIT_FAILURE);
				break;
			default: {
				// parent
				int status;
				pid_t retVal;

				while (1) {
					retVal = waitpid(pid, &status, 0);
					if (retVal != -1)
						break;
					if (errno != EINTR) {
						fprintf(debugContext->err, "Error: waitpid() "
								"failed: %s\n", strerror(errno));
						break;
					}
				}
				if (retVal == -1)
					break;

				if (WIFEXITED(status)) {
					fprintf(debugContext->err, "Exit status: %d\n",
							WEXITSTATUS(status));
				} else if (WIFSIGNALED(status)) {
					fprintf(debugContext->err, "Terminated on signal %d.\n",
							WTERMSIG(status));
				} else {
					fprintf(debugContext->err, "Error: weird exit status.\n");
				}
				break;
			}
		}
	}
#else
	fprintf(debugContext->err, "Cannot execute: not supported on this "
			"platform.\n");
#endif

err:
	for (i = 1; i < argc - 1; i++) {
		if (handles[i] != NULL)
			uio_releaseStdioAccess(handles[i]);
	}
	uio_free(handles);
	uio_free((void *) newArgs);
	uio_closeDir(tempDir);

	return errCode;
}

static int
debugCmdExit(DebugContext *debugContext, int argc, char *argv[]) {
	debugContext->exit = true;
	(void) argc;
	(void) argv;
	return 0;
}

static int
debugCmdFwriteTest(DebugContext *debugContext, int argc, char *argv[]) {
	uio_Stream *stream;
	const char testString[] = "0123456789\n";

	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}

	stream = uio_fopen(debugContext->cwd, argv[1], "w+b");
	if (stream == NULL) {
		fprintf(debugContext->err, "Could not open file: %s\n",
				strerror(errno));
		goto err;
	}

	if (uio_fwrite(testString, strlen(testString), 1, stream) != 1) {
		fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
				strerror(errno));
		goto err;
	}
	if (uio_fputs(testString, stream) == EOF) {
		fprintf(debugContext->err, "uio_fputs() failed: %s\n",
				strerror(errno));
		goto err;
	}
	if (uio_fseek(stream, 15, SEEK_SET) != 0) {
		fprintf(debugContext->err, "uio_fseek() failed: %s\n",
				strerror(errno));
		goto err;
	}
	if (uio_fputc('A', stream) != 'A') {
		fprintf(debugContext->err, "uio_fputc() failed: %s\n",
				strerror(errno));
		goto err;
	}
	if (uio_fseek(stream, 0, SEEK_SET) != 0) {
		fprintf(debugContext->err, "uio_fseek() failed: %s\n",
				strerror(errno));
		goto err;
	}
	{
		char buf[6];
		char *ptr;
		int i;
		i = 1;
		while (1) {
			ptr = uio_fgets(buf, sizeof buf, stream);
			if (ptr == NULL)
				break;
			fprintf(debugContext->out, "%d: [%s]\n", i, ptr);
			i++;
		}
		if (uio_ferror(stream)) {
			fprintf(debugContext->err, "uio_fgets() failed: %s\n",
					strerror(errno));
			goto err;
		}
		uio_clearerr(stream);
	}
	if (uio_fseek(stream, 4, SEEK_END) != 0) {
		fprintf(debugContext->err, "uio_fseek() failed: %s\n",
				strerror(errno));
		goto err;
	}
	{
		char buf[2000];
		memset(buf, 'Q', sizeof buf);
		if (uio_fwrite(buf, 100, 20, stream) != 20) {
			fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
					strerror(errno));
			goto err;
		}
	}
	if (uio_fseek(stream, 5, SEEK_SET) != 0) {
		fprintf(debugContext->err, "uio_fseek() failed: %s\n",
				strerror(errno));
		goto err;
	}
	if (uio_fputc('B', stream) != 'B') {
		fprintf(debugContext->err, "uio_fputc() failed: %s\n",
				strerror(errno));
		goto err;
	}
	uio_fclose(stream);
	return 0;
err:
	uio_fclose(stream);
	return 1;
}

static int
listOneDir(DebugContext *debugContext, const char *arg) {
	uio_DirList *dirList;
	int i;
	const char *pattern;
	const char *cpath;

	if (arg[0] == '\0') {
		cpath = arg;
		pattern = "*";
	} else {
		pattern = strrchr(arg, '/');
		if (pattern == NULL) {
			cpath = "";
			pattern = arg;
		} else if (pattern[1] == '\0') {
			// argument ends on /
			cpath = arg;
			pattern = "*";
		} else {
			if (pattern == arg) {
				cpath = "/";
			} else {
				char *path;
				path = uio_alloca(pattern - arg + 1);
				memcpy(path, arg, pattern - arg);
				path[pattern - arg] = '\0';
				cpath = path;
			}
			pattern++;
		}
	}
#ifdef HAVE_GLOB
	dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
			match_MATCH_GLOB);
#else
	if (pattern[0] == '*' && pattern[1] == '\0') {
		dirList = uio_getDirList(debugContext->cwd, cpath, "",
				match_MATCH_PREFIX);
	} else {
		dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
				match_MATCH_LITTERAL);
	}
#endif
	if (dirList == NULL) {
		fprintf(debugContext->out, "Error in uio_getDirList(): %s.\n",
				strerror(errno));
		return 1;
	}
	for (i = 0; i < dirList->numNames; i++)
		fprintf(debugContext->out, "%s\n", dirList->names[i]);
	uio_DirList_free(dirList);
	return 0;
}

static int
debugCmdLs(DebugContext *debugContext, int argc, char *argv[]) {
	int argI;
	int retVal;

	if (argc == 1)
		return listOneDir(debugContext, unconst(""));
	
	for (argI = 1; argI < argc; argI++) {
		retVal = listOneDir(debugContext, argv[argI]);
		if (retVal != 0)
			return retVal;
	}
	
	return 0;
}

static int
debugCmdMem(DebugContext *debugContext, int argc, char *argv[]) {
#ifdef uio_MEM_DEBUG
	uio_MemDebug_printPointers(debugContext->out);
#else
	fprintf(debugContext->out, "Memory debugging not compiled in.\n");
#endif
	(void) argc;
	(void) argv;
	return 0;
}

static int
debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]) {
	int retVal;
	
	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	retVal = uio_mkdir(debugContext->cwd, argv[1], 0777);
	if (retVal == -1) {
		fprintf(debugContext->err, "Could not create directory: %s\n",
				strerror(errno));
		return 1;
	}
	return 0;
}

static int
debugCmdMount(DebugContext *debugContext, int argc, char *argv[]) {
	if (argc == 1) {
		uio_printMounts(debugContext->out, repository);
//		uio_printMountTree(debugContext->out, repository->mountTree, 0);
	}
	(void) argv;
	return 0;
}

static int
debugCmdMv(DebugContext *debugContext, int argc, char *argv[]) {
	int retVal;
	
	if (argc != 3) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	retVal = uio_rename(debugContext->cwd, argv[1],
			debugContext->cwd, argv[2]);
	if (retVal == -1) {
		fprintf(debugContext->err, "Could not rename: %s\n", strerror(errno));
		return 1;
	}
	return 0;
}

static int
debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]) {
	uio_DirHandle_print(debugContext->cwd, debugContext->out);
	(void) argc;
	(void) argv;
	return 0;
}

static int
debugCmdRm(DebugContext *debugContext, int argc, char *argv[]) {
	int retVal;
	
	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	retVal = uio_unlink(debugContext->cwd, argv[1]);
	if (retVal == -1) {
		fprintf(debugContext->err, "Could not remove file: %s\n",
				strerror(errno));
		return 1;
	}
	return 0;
}

static int
debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]) {
	int retVal;
	
	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	retVal = uio_rmdir(debugContext->cwd, argv[1]);
	if (retVal == -1) {
		fprintf(debugContext->err, "Could not remove directory: %s\n",
				strerror(errno));
		return 1;
	}
	return 0;
}

static int
debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) {
	struct stat statBuf;

	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	if (uio_stat(debugContext->cwd, argv[1], &statBuf) == -1) {
		// errno is set
		int savedErrno;
		savedErrno = errno;
		fprintf(debugContext->err, "Could not stat file: %s\n",
				strerror(errno));
		errno = savedErrno;
		return 1;
	}

	fprintf(debugContext->out,
			"size %ld bytes\n"
			"uid %d  gid %d  mode 0%o\n",
			(unsigned long) statBuf.st_size,
			(unsigned int) statBuf.st_uid,
			(unsigned int) statBuf.st_gid,
			(unsigned int) statBuf.st_mode &
			(S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID));
	// Can't do these next three in one fprintf, as ctime uses a static buffer
	// that is overwritten with each call.
	fprintf(debugContext->out,
			"last access:        %s", ctime(&statBuf.st_atime));
	fprintf(debugContext->out,
			"last modification:  %s", ctime(&statBuf.st_mtime));
	fprintf(debugContext->out,
			"last status change: %s", ctime(&statBuf.st_ctime));
	
	return 0;
}

static int
debugCmdWriteTest(DebugContext *debugContext, int argc, char *argv[]) {
	uio_Handle *handle;
	const char testString[] = "Hello world!\n";

	if (argc != 2) {
		fprintf(debugContext->err, "Invalid number of arguments.\n");
		return 1;
	}
	
	handle = uio_open(debugContext->cwd, argv[1],
			O_WRONLY | O_CREAT | O_EXCL
#ifdef WIN32
			| O_BINARY
#endif
			, 0644);
	if (handle == NULL) {
		fprintf(debugContext->err, "Could not open file: %s\n",
				strerror(errno));
		return 1;
	}

	if (uio_write(handle, testString, sizeof testString) == -1) {
		fprintf(debugContext->err, "Write failed: %s\n",
				strerror(errno));
		return 1;
	}
	
	uio_close(handle);
	return 0;
}


