Skip to content
Snippets Groups Projects
util.cpp 29.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • toole1's avatar
    toole1 committed
    // CS 225 util.h
    // Created Spring 2011 by Jack Toole
    
    #include <ctype.h>
    #include <dirent.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <iostream>
    #include "util.h"
    
    extern char ** environ; // man 7 environ
    
    namespace util
    {
    
    namespace internal
    {
    const char * error_prefix = "";
    
    template<typename StrType>
    void exit_if_error_output(const char * file, int32_t line, StrType message)
    {
    	if (util::internal::error_prefix != NULL)
    		cerr << util::internal::error_prefix;
    	cerr << file << ":" << line << ": " << message;
    	if (errno != 0)
    		cerr << ": " << strerror(errno);
    	cerr << endl;
    	exit(-1);
    }
    
    }
    
    void SET_ERROR_MESSAGE(const char * message)
    {
    	internal::error_prefix = message;
    }
    
    
    toole1's avatar
    toole1 committed
    namespace internal
    {
    namespace exec
    {
    
    
    char * allocateCstr(const string & str)
    {
    	char * cstr = new char[str.size() + 1];
    	str.copy(cstr, str.size());
    	cstr[str.size()] = '\0';
    	return cstr;
    }
    
    
    toole1's avatar
    toole1 committed
    pair<int, int> getFdsFromStream(const ostream * stream)
    {
    	int redirect_fd;
    	int outpipe_fd = -1;
    
    	if (stream == NULL)
    		redirect_fd = -1;
    	else if (stream == &cout || stream == &cerr) // we don't handle cerr right now; it is just left unpiped
    		redirect_fd = STDOUT_FILENO;
    	else
    	{
    		int pipe_fds[2];
    		EXIT_IF_ERROR(pipe(pipe_fds) != 0);
    		redirect_fd = pipe_fds[1];
    		outpipe_fd = pipe_fds[0];
    	}
    
    	return make_pair(redirect_fd, outpipe_fd);
    }
    
    struct exec_timers
    {
    	struct itimerval remaining_real;
    	struct itimerval remaining_virtual;
    	struct itimerval remaining_prof;
    	bool supports_virtual;
    	bool supports_prof;
    	exec_timers() : supports_virtual(true), supports_prof(true) { }
    };
    
    exec_timers get_exec_timers()
    {
    	exec_timers result;
    	
    	EXIT_IF_ERROR(getitimer(ITIMER_REAL, &result.remaining_real));
    	
    	if (getitimer(ITIMER_VIRTUAL, &result.remaining_virtual) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			result.supports_virtual = false;
    			errno = 0;
    		}
    		else
    			exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_VIRTUAL) failed");
    	}
    	
    	if (getitimer(ITIMER_PROF, &result.remaining_prof) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			result.supports_prof = false;
    			errno = 0;
    		}
    		else
    			exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_PROF) failed");
    	}
    
    	return result;
    }
    
    void set_exec_timers(const exec_timers & timers)
    {
    	EXIT_IF_ERROR(setitimer(ITIMER_REAL, &timers.remaining_real, NULL));
    	if (timers.supports_virtual) EXIT_IF_ERROR(setitimer(ITIMER_VIRTUAL, &timers.remaining_virtual, NULL));
    	if (timers.supports_prof)    EXIT_IF_ERROR(setitimer(ITIMER_PROF,    &timers.remaining_prof, NULL));
    }
    
    void redirect_to_fd(const int redirect_fd)
    {
    	if (redirect_fd == -1)
    	{
    		int devnull_fd = open("/dev/null", O_WRONLY | O_NONBLOCK);
    		close(STDOUT_FILENO);
    		close(STDERR_FILENO);
    		dup2(devnull_fd, STDOUT_FILENO);
    		dup2(devnull_fd, STDERR_FILENO);
    		close(devnull_fd);
    	}
    	else if (redirect_fd != STDOUT_FILENO)
    	{
    		close(STDOUT_FILENO);
    		close(STDERR_FILENO);
    		dup2(redirect_fd, STDOUT_FILENO);
    		dup2(redirect_fd, STDERR_FILENO);
    		close(redirect_fd);
    	}
    }
    
    char ** cstr_array_from_vector_string(const string & command, const vector<string> & args)
    {
    	// TODO (toole1): check this doesn't cause a memleak
    	char ** args_cstr = new char*[args.size() + 2];
    	args_cstr[0] = allocateCstr(command);
    	for (size_t i = 0; i < args.size(); i++)
    		args_cstr[i + 1] = allocateCstr(args[i]);
    	args_cstr[args.size() + 1] = NULL;
    	return args_cstr;
    }
    
    } // namespace exec
    } // namespace internal
    
    // heavily refactored, but originally from:
    // http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c
    int8_t exec(const string & command, const vector<string> & args, ostream * output)
    {
    	using namespace internal;
    	using namespace internal::exec;
    
    	pair<int, int> fds = getFdsFromStream(output);
    	int redirect_fd = fds.first;
    	int outpipe_fd  = fds.second;
    
    	exec_timers timers = get_exec_timers();
    	
    	pid_t pid = fork();
    	EXIT_IF_ERROR(pid < 0);
    	if (pid == 0)
    	{
    		set_exec_timers(timers);
    		redirect_to_fd(redirect_fd);
    		if (outpipe_fd != -1)
    			close(outpipe_fd);
    
    		// Sanitize environment
    		char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin";
    		// Turn off glibc errors default write-to-terminal behaviour, because
    		// it does not get caught by stderr. This instead results in an abort.
    		char redirect_glibc[] = "LIBC_FATAL_STDERR_=1";
    		char * newenv[] = { path, redirect_glibc, NULL };
    		environ = newenv;
    
    		char ** args_cstr = cstr_array_from_vector_string(command, args);
    
    		// Swap out child process image with the command, searching
    		// in the specified path
    		execvp(command.c_str(), args_cstr);
    		
            // An error occured
    		cerr << "exec(" << '\"' << command << '\"';
    		for (size_t i = 0; i < args.size(); i++)
    			cerr << ", \"" << args[i] << "\"";
    		cerr << ") failed: " << strerror(errno) << endl;
    		exit(-1);
    	}
    	else
    	{
    		// Take the output of the pipe and put it into the *output stream
    		if (outpipe_fd != -1)
    		{
    			EXIT_IF_ERROR(output == NULL);
    			close(redirect_fd);
    
    			vector<char> buffer(1024);
    			ssize_t char_read_count;
    			do
    			{
    				errno = 0;
    				cerr << "START READ" << endl;
    				char_read_count = ::read(outpipe_fd, &buffer[0], buffer.size() - 1);
    				cerr << "END READ " << char_read_count << endl;
    				
    				if (char_read_count < 0 && errno == EINTR) continue;
    				EXIT_IF_ERROR(char_read_count < 0);
    				EXIT_IF_ERROR(static_cast<size_t>(char_read_count) + 1 >= buffer.size());
    				
    				buffer[char_read_count] = '\0';
    				*output << &buffer[0];
    
    			} while (char_read_count > 0);
    		}
    
    		int childExitStatus;
    		/* parent - wait for child - this has all error handling, you
    		 * could just call wait() as long as you are only expecting to
    		 * have one child process at a time.
    		 */
    		pid_t ws = waitpid( pid, &childExitStatus, 0);
    		EXIT_IF_ERROR(ws == -1);
    
    		if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
    		{
    			int8_t status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
    			/* handle non-zero as you wish */
    			return status;
    		}
    		else if (WIFSIGNALED(childExitStatus)) /* killed */
    		{
    			// No idea why I'm doing this anymore... if it segfaults, you do too??
    			kill(getpid(), WTERMSIG(childExitStatus));
    			return -1;
    		}
    		else if (WIFSTOPPED(childExitStatus)) /* stopped */
    		{
    			//cout << "exec error: " << __LINE__ << endl;
    			return -1;
    		}
    		else
    			EXIT_IF_ERROR(true);
    		exit(-1);
    	}
    }
    
    
    // TODO (toole1) this is a total hack, should use execvp like exec() below
    
    toole1's avatar
    toole1 committed
    // originally from:
    // http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c
    
    int8_t exec(int redirect_fd, const string & command, const vector<string> & args)
    {
    
    toole1's avatar
    toole1 committed
    	using namespace internal::exec;
    
    
    	// For debugging:
    #if 0
    	cerr << "exec(" << '\"' << command << '\"';
    	for (size_t i = 0; i < args.size(); i++)
    
    toole1's avatar
    toole1 committed
    		cerr << ", \"" << args[i] << "\"";
    
    	cerr << ")" << endl;
    #endif
    
    	// avoid self destruction errors from closing then trying to duplicate output
    	// you can't redirect to what's already there
    	if (redirect_fd == STDOUT_FILENO || redirect_fd == STDERR_FILENO)
    		redirect_fd = STDOUT_FILENO;
    
    	// Save timer values :)
    	// These are preserved across the parent, but not inherited by the child
    	// let's change that
    	struct itimerval remaining_real;
    	struct itimerval remaining_virtual;
    	struct itimerval remaining_prof;
    	bool supports_virtual = true;
    	bool supports_prof    = true;
    	EXIT_IF_ERROR(getitimer(ITIMER_REAL,    &remaining_real));
    	if (getitimer(ITIMER_VIRTUAL, &remaining_virtual) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			supports_virtual = false;
    			errno = 0;
    		}
    		else
    			internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_VIRTUAL) failed");
    	}
    	if (getitimer(ITIMER_PROF, &remaining_prof) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			supports_prof = false;
    			errno = 0;
    		}
    		else
    			internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_PROF) failed");
    	}
    
    	pid_t pid = fork();
    
    	if (pid == 0) /* child */
    	{
    
    		// Restore timers
    		EXIT_IF_ERROR(setitimer(ITIMER_REAL, &remaining_real, NULL));
    		if (supports_virtual) EXIT_IF_ERROR(setitimer(ITIMER_VIRTUAL, &remaining_virtual, NULL));
    		if (supports_prof)    EXIT_IF_ERROR(setitimer(ITIMER_PROF,    &remaining_prof, NULL));
    
    		if (redirect_fd == -1)
    		{
    			int devnull_fd = open("/dev/null", O_WRONLY | O_NONBLOCK);
    			close(STDOUT_FILENO);
    			close(STDERR_FILENO);
    			dup2(devnull_fd, STDOUT_FILENO);
    			dup2(devnull_fd, STDERR_FILENO);
    			close(devnull_fd);
    		}
    		else if (redirect_fd != STDOUT_FILENO)
    		{
    			close(STDOUT_FILENO);
    			close(STDERR_FILENO);
    			dup2(redirect_fd, STDOUT_FILENO);
    			dup2(redirect_fd, STDERR_FILENO);
    		}
    
    		// Sanitize the environment
    #if 1 //!! hack!
    		char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin";
    		// Turn off glibc errors default write-to-terminal behaviour, because
    		// it does not get caught by stderr. This instead results in an abort.
    		char redirect_glibc[] = "LIBC_FATAL_STDERR_=1";
    		char * newenv[] = { path, redirect_glibc, NULL };
    		//char * newenv[] = { path, NULL };
    		environ = newenv;
    #endif
    
    		// TODO (toole1): check this doesn't cause a memleak
    		char ** args_cstr = new char*[args.size() + 2];
    		args_cstr[0] = allocateCstr(command);
    		for (size_t i = 0; i < args.size(); i++)
    			args_cstr[i + 1] = allocateCstr(args[i]);
    		args_cstr[args.size() + 1] = NULL;
    		
    		// Swap out child process image with the command, searching
    		// in the specified path
    		execvp(command.c_str(), args_cstr);
    		
            // An error occured
    		cerr << "exec(" << '\"' << command << '\"';
    		for (size_t i = 0; i < args.size(); i++)
    			cerr << ", \"" << args[i] << "\"";
    		cerr << ") failed: " << strerror(errno) << endl;
    		exit(-1);
    	}
    	else if (pid < 0)
    	{
    		/* error - couldn't start process - you decide how to handle */
    		return -1;
    	}
    	else
    	{
    		int childExitStatus;
    		/* parent - wait for child - this has all error handling, you
    		 * could just call wait() as long as you are only expecting to
    		 * have one child process at a time.
    		 */
    		pid_t ws = waitpid( pid, &childExitStatus, 0);
    		if (ws == -1)
    		{ /* error - handle as you wish */
    			//cout << "exec error: " << __LINE__ << endl;
    			return -1;
    		}
    
    		if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
    		{
    			int8_t status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
    			/* handle non-zero as you wish */
    			return status;
    		}
    		else if (WIFSIGNALED(childExitStatus)) /* killed */
    		{
    			kill(getpid(), WTERMSIG(childExitStatus));
    			return -1;
    		}
    		else if (WIFSTOPPED(childExitStatus)) /* stopped */
    		{
    			//cout << "exec error: " << __LINE__ << endl;
    			return -1;
    		}
    		else
    			return -1;
    	}
    }
    
    
    
    toole1's avatar
    toole1 committed
    // originally from stackoverflow.com user plinth
    // http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c
    // Modified by Jack Toole
    int8_t exec(int redirect_fd, const char * command,
                const char * arg1,
                const char * arg2,
                const char * arg3,
                const char * arg4,
                const char * arg5,
                const char * arg6)
    {
    	int childExitStatus;
    	pid_t pid;
    
    
    toole1's avatar
    toole1 committed
    	const char * args[] = {arg1, arg2, arg3, arg4, arg5, arg6};
    	const size_t args_count = (sizeof args) / (sizeof args[0]);
    
    	// shift all nulls to end
    	size_t first_null = 0;
    	for (size_t i = 0; i < args_count; i++)
    		if (args[i] != NULL)
    			swap(args[i], args[first_null++]);
    
    
    toole1's avatar
    toole1 committed
    	// For debugging:
    #if 0
    
    toole1's avatar
    toole1 committed
    	cerr << "exec(" << '\"' << command << '\"';
    	for (size_t i = 0; i < args_count; i++)
    		if (args[i] != NULL) cerr << ", \"" << args[i] << "\"";
    	cerr << ")" << endl;
    
    toole1's avatar
    toole1 committed
    #endif
    
    	// avoid self destruction errors from closing then trying to duplicate output
    	// you can't redirect to what's already there
    	if (redirect_fd == STDOUT_FILENO || redirect_fd == STDERR_FILENO)
    		redirect_fd = STDOUT_FILENO;
    
    	// Save timer values :)
    	// These are preserved across the parent, but not inherited by the child
    	// let's change that
    	struct itimerval remaining_real;
    	struct itimerval remaining_virtual;
    	struct itimerval remaining_prof;
    
    toole1's avatar
    toole1 committed
    	bool supports_virtual = true;
    	bool supports_prof    = true;
    
    toole1's avatar
    toole1 committed
    	EXIT_IF_ERROR(getitimer(ITIMER_REAL,    &remaining_real));
    
    toole1's avatar
    toole1 committed
    	if (getitimer(ITIMER_VIRTUAL, &remaining_virtual) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			supports_virtual = false;
    			errno = 0;
    		}
    		else
    			internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_VIRTUAL) failed");
    	}
    	if (getitimer(ITIMER_PROF, &remaining_prof) != 0)
    	{
    		if (errno == EINVAL)
    		{
    			supports_prof = false;
    			errno = 0;
    		}
    		else
    			internal::exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_PROF) failed");
    	}
    
    toole1's avatar
    toole1 committed
    
    	pid = fork();
    
    	if (pid == 0) /* child */
    	{
    
    		// Restore timers
    
    toole1's avatar
    toole1 committed
    		EXIT_IF_ERROR(setitimer(ITIMER_REAL, &remaining_real, NULL));
    		if (supports_virtual) EXIT_IF_ERROR(setitimer(ITIMER_VIRTUAL, &remaining_virtual, NULL));
    		if (supports_prof)    EXIT_IF_ERROR(setitimer(ITIMER_PROF,    &remaining_prof, NULL));
    
    toole1's avatar
    toole1 committed
    
    		if (redirect_fd == -1)
    		{
    			int devnull_fd = open("/dev/null", O_WRONLY | O_NONBLOCK);
    			close(STDOUT_FILENO);
    			close(STDERR_FILENO);
    			dup2(devnull_fd, STDOUT_FILENO);
    			dup2(devnull_fd, STDERR_FILENO);
    			close(devnull_fd);
    		}
    		else if (redirect_fd != STDOUT_FILENO)
    		{
    			close(STDOUT_FILENO);
    			close(STDERR_FILENO);
    			dup2(redirect_fd, STDOUT_FILENO);
    			dup2(redirect_fd, STDERR_FILENO);
    		}
    
    		// Sanitize the environment
    
    toole1's avatar
    toole1 committed
    #if 1 //!! hack!
    
    toole1's avatar
    toole1 committed
    		char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin";
    
    toole1's avatar
    toole1 committed
    		// Turn off glibc errors default write-to-terminal behaviour, because
    		// it does not get caught by stderr. This instead results in an abort.
    
    toole1's avatar
    toole1 committed
    		char redirect_glibc[] = "LIBC_FATAL_STDERR_=1";
    		char * newenv[] = { path, redirect_glibc, NULL };
    		//char * newenv[] = { path, NULL };
    
    toole1's avatar
    toole1 committed
    		environ = newenv;
    
    toole1's avatar
    toole1 committed
    #endif
    
    toole1's avatar
    toole1 committed
    
    		// Swap out child process image with the command, searching
    		// in the specified path
    
    toole1's avatar
    toole1 committed
    		execlp(command, command, args[0], args[1], args[2], args[3], args[4], args[5], NULL);
    
    toole1's avatar
    toole1 committed
    		
            // An error occured
    
    massung1's avatar
    massung1 committed
    		cerr << "exec(" << '\"' << command << '\"';
    
    toole1's avatar
    toole1 committed
    		for (size_t i = 0; i < args_count; i++)
    			if (args[i] != NULL) cerr << ", \"" << args[i] << "\"";
    
    toole1's avatar
    toole1 committed
    		cerr << ") failed: " << strerror(errno) << endl;
    		exit(-1);
    	}
    	else if (pid < 0)
    	{
    		/* error - couldn't start process - you decide how to handle */
    		return -1;
    	}
    	else
    	{
    		/* parent - wait for child - this has all error handling, you
    		 * could just call wait() as long as you are only expecting to
    		 * have one child process at a time.
    		 */
    		pid_t ws = waitpid( pid, &childExitStatus, 0);
    		if (ws == -1)
    		{ /* error - handle as you wish */
    			//cout << "exec error: " << __LINE__ << endl;
    			return -1;
    		}
    
    		if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
    		{
    			int8_t status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
    			/* handle non-zero as you wish */
    			return status;
    		}
    		else if (WIFSIGNALED(childExitStatus)) /* killed */
    		{
    			kill(getpid(), WTERMSIG(childExitStatus));
    			return -1;
    		}
    		else if (WIFSTOPPED(childExitStatus)) /* stopped */
    		{
    			//cout << "exec error: " << __LINE__ << endl;
    			return -1;
    		}
    		else
    			return -1;
    	}
    }
    
    
    toole1's avatar
    toole1 committed
    const char * Signal::what() const
    {
    	const char * desc = strsignal(signum);
    	return (desc == NULL) ? "Unknown Signal" : desc;
    }
    
    
    toole1's avatar
    toole1 committed
    int chdir(const string & dir)
    {
    	return ::chdir(dir.c_str());
    }
    
    
    toole1's avatar
    toole1 committed
    void assertExists(const string & path, int exit_code /* = -1 */)
    {
    	if (!exists(path))
    	{
    		cerr << "Error: " << path << " does not exist." << endl;
    		exit(exit_code);
    	}
    }
    
    bool exists(const string & path)
    {
    	// Try stat-ing it
    	struct stat st;
    	if (stat(path.c_str(), &st) != 0) return false;
    	// Check for read permission
    	if ((st.st_mode & S_IRUSR) == 0) return false;
    
    	// Check for correct file/directory nature
    	if (path[path.length()-1] != '/') return S_ISREG(st.st_mode);
    
    	// Otherwise we want a directory
    	if ((st.st_mode & S_IXUSR) == 0) return false;
    	return S_ISDIR(st.st_mode);
    }
    
    
    mode_t permissions(const string & path)
    {
    	// Try stat-ing it
    	struct stat st;
    	if (stat(path.c_str(), &st) != 0) return -1;
    	// Check for read permission
    	if ((st.st_mode & S_IRUSR) == 0) return -1;
    
    	return (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
    }
    
    
    void forceRemoveDir(string dir)
    {
    	size_t len = dir.length();
    	if (dir[len-1] == '/') dir[len-1] = '\0';
    	EXIT_IF_ERROR(exec("rm","-rf",dir.c_str()) != 0);
    	if (dir[len-1] == '\0') dir[len-1] = '/';
    }
    
    
    string getcwdstr()
    {
    	int len = 256;
    	char * buffer = new char[len];
    
    	char * ret = getcwd(&buffer[0], len - 1);
    	while (ret == NULL && errno == ERANGE)
    	{
    		len *= 2;
    
    		delete buffer;
    		buffer = new char[len];
    
    		ret = getcwd(&buffer[0], len - 1);
    	}
    
    	EXIT_IF_ERROR(ret == NULL);
    
    	string cwdstr(buffer);
    	delete buffer;
    
    	return cwdstr;
    }
    
    
    void copyFile(const string & source, const string & dest)
    {
    	assertExists(source);
    	vector<string> folders = tokenize(dest, '/');
    	string currdir = "";
    	for (size_t i = 0; i < folders.size() - 1; i++)
    	{
    		currdir += folders[i] + '/';
    		if (!exists(currdir))
    			exec("mkdir", currdir.c_str());
    	}
    	exec("cp", source.c_str(), dest.c_str());
    }
    
    void copyFiles(const string & sourceFolder, const string & destFolder, const vector<string> & files)
    {
    	assertExists(destFolder);
    	for (size_t i = 0; i < files.size(); i++)
    	{
    		string sourceFile = sourceFolder + files[i];
    		assertExists(sourceFile);
    		copyFile(sourceFile, destFolder);
    	}
    }
    
    
    void protectFiles(const string & folder, const vector<string> & files)
    {
    #if 0 // (debug)
    	for (size_t i = 0; i < files.size(); i++)
    	{
    		string file = folder + files[i];
    		assertExists(file);
    
    		if (chmod(file.c_str(), S_IRUSR) != 0)
    		{
    			perror("chmod failed");
    			exit(-1);
    		}
    	}
    #endif
    }
    
    void protectDir(const string & dir)
    {
    	// (debug) EXIT_IF_ERROR(exec("/bin/chmod", "-R", "ugoa-w", dir.c_str()) != 0);
    }
    
    // Originally from Simon Biber
    // http://bytes.com/topic/c/answers/545614-list-files-current-directory
    
    toole1's avatar
    toole1 committed
    vector<string> get_files_in_dir(const string & dir, bool concatdir /* = true */)
    
    toole1's avatar
    toole1 committed
    	EXIT_IF_ERROR(dir == "" || dir[dir.length()-1] != '/', "Directory name must end in a '/'");
    	
    
    toole1's avatar
    toole1 committed
    	vector<string> files;
    	DIR * dirp = opendir(dir.c_str());
    	if (dirp == NULL) return files;
    	
    	struct dirent * ent = readdir(dirp);
    	while (ent != NULL)
    	{
    
    toole1's avatar
    toole1 committed
    		string file = ent->d_name;
    		if (file != "." && file != "..")
    
    toole1's avatar
    toole1 committed
    			files.push_back(concatdir ? dir + file : file);
    
    toole1's avatar
    toole1 committed
    		ent = readdir(dirp);
    	}
    
    	closedir(dirp);
    	return files;
    }
    
    
    toole1's avatar
    toole1 committed
    bool is_symlink(const string & file)
    {
    	// Try lstat-ing it
    	struct stat st;
    	if (lstat(file.c_str(), &st) != 0) return -1;
    	// Check for read permission
    	if ((st.st_mode & S_IRUSR) == 0) return false;
    
    	// & with symlink bit
    	return (S_ISLNK(st.st_mode)) != 0;
    }
    
    string get_symlink_target(const string & symlink)
    {
    	const size_t buf_size = 4096;
    	char buf[buf_size+1]; // TODO (toole1): hack-y value
    	ssize_t len = readlink(symlink.c_str(), buf, buf_size);
    	EXIT_IF_ERROR(len < 0 || static_cast<size_t>(len) == buf_size, "Error getting target of symlink " + symlink);
    	buf[len] = '\0';
    	return string(buf);
    }
    
    
    toole1's avatar
    toole1 committed
    void linkDirs(const string & sourceFolder, const string & destFolder, const vector<string> & dirs)
    {
    	assertExists(destFolder);
    	for (size_t i = 0; i < dirs.size(); i++)
    	{
    		string source = sourceFolder + dirs[i];
    		string target = destFolder   + dirs[i];
    
    		// Check for redundant monad/ directory
    		// This allows the monad/ dir to be safely renamed
    		if (replaceFirst(source, "/../monad/","/"))
    			replaceFirst(target, "/monad/","/");
    
    		assertExists(destFolder + source + '/');
    
    		if (symlink(source.c_str(), target.c_str()) != 0)
    		{
    			cerr << "symlink failed: " << target << ": ";
    			perror(NULL);
    			exit(-1);
    		}
    	}
    }
    
    
    bool replaceFirst(string & str, const string & toreplace, const string & with)
    {
    	size_t i = str.find(toreplace);
    	if (i != string::npos)
    	{
    		str.replace(i,toreplace.length(),with);
    		return true;
    	}
    	return false;
    }
    
    size_t replaceAll(string & str, const string & toreplace, const string & with)
    {
    	size_t i = str.find(toreplace);
    	size_t count = 0;
    
    	while (i != string::npos)
    	{
    		str.replace(i,toreplace.length(),with);
    		i = str.find(toreplace, i + with.length());
    		count++;
    	}
    
    	return count;
    }
    
    size_t replaceAllInternal(string & str, const string & toreplace, const string & with)
    {
    	size_t i = str.find(toreplace);
    	size_t count = 0;
    
    	while ((i != string::npos) && (i != str.length() - toreplace.length()))
    	{
    		str.replace(i,toreplace.length(),with);
    		i = str.find(toreplace, i + with.length());
    		count++;
    	}
    
    	return count;
    }
    
    
    size_t findNthLast(const string & str, char c, size_t n)
    {
    	if (str.length() == 0) return string::npos;
    	size_t i = str.length() - 1;
    
    	do
    	{
    		if (str[i] == c) n--;
    		if (n == 0) return i;
    	} while (i-- != 0);
    
    	return string::npos;
    }
    
    
    string read_string_from_FILE(FILE * file, size_t max_length /* = -1 */)
    {
    	vector<char> v;
    	v.reserve(256);
    
    	while (true) 
    	{
    		int nextchar = fgetc(file);
    		if (nextchar == '\0' || nextchar == EOF)
    			break;
    		if (v.size() < max_length)
    			v.push_back(nextchar);
    	}
    
    	if (v.size() == max_length)
    	{
    		v.push_back('.');
    		v.push_back('.');
    		v.push_back('.');
    	}
    
    	v.push_back('\0');
    
    	return string(&v[0]);
    }
    
    void write_string_to_FILE(FILE * file, const char * str)
    {
    	fflush(file);
    	size_t i = 0;
    	do
    	{
    //		cout << (int)str[i] << ' ';
    		fputc(str[i], file);
    
    		// We use a do-while because we want the \0 to be written to the stream
    		// for sending multiple strings
    	} while (str[i++] != 0);
    
    //	cout << endl;
    
    	fflush(file);
    }
    
    
    
    /**
    *
    *
    **/
    
    string readFile(const string & filename)
    {
    	ifstream file;
    	file.open(filename.c_str());
    	if (!file.good())
    		return "";
    	
    	stringbuf linebuf;
    	file.get(linebuf, '\0');
    	linebuf.pubsync();
    	return linebuf.str();
    }
    
    
    void readConfig(const string & testsFolder, FileMap & map, const string & discriminator /* = "" */)
    {
    	string file;
    	if (testsFolder == "" || testsFolder[testsFolder.length()-1] == '/')
    		file = testsFolder + "config.ini";
    	else
    		file = testsFolder;
    	readFileGeneric(file, &map, NULL, discriminator);
    }
    
    void readFile(const string & file, vector<string> & lines)
    {
    	readFileGeneric(file, NULL, &lines);
    }
    
    void readFileGeneric(const string & filename, FileMap * map, vector<string> * lines, const string & discriminator /* = "" */)
    {
    	ifstream infile;
    	istream * fileptr;
    	if (filename == "/dev/stdin")
    		fileptr = &cin;
    	else
    	{
    		fileptr = &infile;
    		infile.open(filename.c_str(), fstream::in);
    	}
    	istream & file = *fileptr;
    
    	vector<string> * section = NULL;
    	if (map != NULL) section = &(*map)[""];
    	else section = lines;
    
    	while ((file.good() && file.peek() == '\n') || file.peek() == '\r')
    		file.get(); // get '\n'
    
    	while (file.good())
    	{
    		// Read a line - A lot of code, I know, right?
    		stringbuf linebuf;
    		file.get(linebuf);
    		while ((file.good() && file.peek() == '\n') || file.peek() == '\r')
    			file.get(); // get '\n'
    		if (linebuf.in_avail() == 0) continue;
    		linebuf.pubsync();
    		string line = linebuf.str();
    		int len = line.length();
                    if (line[len-1] == '\r')
                        line.replace(--len,1,"");
    
    		if (len == 0 || line[0] == ';') continue; // skip comments
    		
    		if (map != NULL)
    		{
    			// Update the section
    			if (line[0] == '[' && line[len-1] == ']')
    			{
    				section = &(*map)[line.substr(1, len - 2)];
    				continue;
    			}
    			else if (line[0] == '[' || line[len-1] == ']')
    			{
    				cout << "config.ini: Format error: " << line << endl;
    				exit(-1);
    			}
    		}
    
    		// Or add the line/file to the section
    		size_t delim_pos = line.find_first_of("?:");
    		if (delim_pos == string::npos || map == NULL)
    			section->push_back(line);
    		else if ((line[delim_pos] == ':' && (delim_pos == 0 || discriminator == "")) ||
    		         line.compare(0, delim_pos, discriminator) == 0)
    			section->push_back(line.substr(delim_pos+1, line.size()-delim_pos-1));
    	}
    
    	if (filename != "/dev/stdin")
    		infile.close();
    }
    
    
    
    toole1's avatar
    toole1 committed
    OptionsParser::OptionsParser()
    {
    	valueMap[""]      = true;
    
    	valueMap["yes"]   = true;
    	valueMap["no"]    = false;
    
    	valueMap["on"]    = true;
    	valueMap["off"]   = false;
    
    	valueMap["true"]  = true;
    	valueMap["false"] = false;
    
    	valueMap["1"]     = true;
    	valueMap["0"]     = false;
    }
    
    
    toole1's avatar
    toole1 committed
    vector<string> OptionsParser::parse(int argc, const char * const * argv)
    
    toole1's avatar
    toole1 committed
    {
    	vector<string> unprocessedArgs;
    	size_t out_arg_i = 0;
    
    	for (int arg_i = 1; arg_i < argc; arg_i++)
    	{
    
    toole1's avatar
    toole1 committed
    		string originalCaseArg = argv[arg_i];
    		string currarg = toLower(originalCaseArg);
    
    toole1's avatar
    toole1 committed
    
    		if (currarg.compare(0, 2, "--") == 0) //long option
    		{
    			bool invert = (currarg.compare(2, 2, "no") == 0);
    			size_t name_i = (invert ? 4 : 2);
    			size_t equalspos = currarg.find_first_of("=-", name_i);