filename.h 10.9 KB
#ifndef STIM_FILENAME_H
#define STIM_FILENAME_H

#include <stdio.h>  /* defines FILENAME_MAX */
#ifdef _WIN32
	#include <windows.h>
    #include <direct.h>
    #define GetCurrentDir _getcwd
	#define STIM_FILENAME_DIV '\\'
#else
    #include <unistd.h>
    #define GetCurrentDir getcwd
	#define STIM_FILENAME_DIV '/'
 #endif

#include <string>
#include <sstream>
#include <iomanip>
#include <vector>
#include <stack>
#include <algorithm>
#include <iostream>

#include "../parser/parser.h"
#ifdef BOOST_PRECOMPILED
#include <boost/filesystem.hpp>
#endif
namespace stim{

//filename class designed to work with both Windows and Unix

class filename{

private:
	void init(){

		absolute = false;

	}

protected:

	std::string drive;				//drive letter (for Windows)
	std::vector<std::string> path;	//directory hierarchy
	std::string prefix;				//file prefix (without extension)
	std::string	ext;				//file extension

	bool absolute;					//filename is an absolute path

	//replaces win32 dividers with the Linux standard
	std::string unix_div(std::string s) {
		std::replace( s.begin(), s.end(), '\\', '/');
		return s;
	}

	//break up a filename into a prefix and extension
	void parse_name(std::string fname){

		//find the extension
		size_t xpos = fname.find_last_of('.');			//find the position of the extension period (if there is one)
		if(xpos != std::string::npos){						//split the prefix and extension
			prefix = fname.substr(0, xpos);
			ext = fname.substr(xpos + 1);
		}
		else
			prefix = fname;
	}

	//parse a file locator string
	void parse(std::string loc){

		loc = unix_div(loc);

		//determine the drive (if Windows)
		drive = "";
		#ifdef _WIN32
			//if the second character is a colon, the character right before it is the drive
			if(loc[1] == ':'){
				absolute = true;			//this is an absolute path
				drive = loc[0];				//copy the drive letter
				loc = loc.substr(3);		//remove the drive information from the locator string
			}
		#else
			if(loc[0] == STIM_FILENAME_DIV){
				absolute = true;
				loc = loc.substr(1);
			}
		#endif

		//determine the file name
		std::string fname = loc.substr( loc.find_last_of('/') + 1 );	//find the file name (including extension)

		//parse the file name
		parse_name(fname);

		//find the directory hierarchy
		std::string dir = loc.substr(0, loc.find_last_of('/') + 1 );
		path = stim::parser::split(dir, '/');
	}

public:

	filename(){
		init();
	}

	filename(std::string loc){
		init();
		parse(loc);
	}


	/// Outputs the file name (including prefix and extension)
	std::string get_name(){
		if(prefix == "" && ext == "")
			return "";
		else
			return prefix + "." + ext;
	}

	/// Output the file extension only (usually three characters after a '.')
	std::string get_extension(){
		return ext;
	}

	/// Output the file prefix only (name before the extension)
	std::string get_prefix(){
		return prefix;
	}

	/// Output the entire path (not including the filename)
	///		ex. "c:\this\is\the\directory\"
	std::string dir(){
		std::stringstream ss;

		//if the path is absolute
		if(absolute){
			//output the drive letter if in Windows
			#ifdef _WIN32
				ss<<drive<<":";
			#endif
			ss<<STIM_FILENAME_DIV;
		}

		//output the directory
		for(unsigned int d = 0; d < path.size(); d++)
			ss<<path[d]<<STIM_FILENAME_DIV;

		return ss.str();

	}

	void clear()
	{
		drive = "";				//drive letter (for Windows)
		path.resize(0);
		prefix= "";				//file prefix (without extension)
		ext = "";				//file extension
	}

	/// Output the entire string, including the path and filename
	///		ex. "c:\this\is\a\directory\file.txt"
	std::string str(){
		std::stringstream ss;					//create a string stream
		ss<<dir()<<get_name();					//push the directory and filename to that stream
		return ss.str();						//convert the stream to a string and return it
	}

	//*****************************************************************************************************************
	// output is the directory and the prefix and the extension of the files, which are looking for in that directory.
	/// David: I have no idea what this is doing. It looks identical to dir()
	std::string dir_fname(){
		std::stringstream ss;

		//if the path is absolute
		if(absolute){
			//output the drive letter if in Windows
			#ifdef _WIN32
				ss<<drive<<":";
			#endif
			ss<<STIM_FILENAME_DIV;
		}


		for(unsigned int d = 0; d < path.size(); d++)
			ss<<path[d]<<STIM_FILENAME_DIV;
		ss<<get_name();

		return ss.str();

	}

	// output is the directory and the name of the file which are found in that given directory
	std::string f_name(std::string file_name){
		std::stringstream ss;

		if(absolute){ 								//if the path is absolute
			#ifdef _WIN32
				ss<<drive<<":";						//output the drive letter if in Windows
			#endif
			ss<<STIM_FILENAME_DIV;					//output a path divider
		}

		for(unsigned int d = 0; d < path.size(); d++)	//for each directory in the current path
			ss<<path[d]<<STIM_FILENAME_DIV;				//add that directory, interspersing path dividers

		stim::filename fn = file_name;					
			std::string fn_prefix = fn.prefix;
			std::string fn_ext = fn.ext;
		ss<<fn_prefix + '.' + fn_ext;

		return ss.str();

	}

	/// Returns a list of files using the current filename as a template.
	/// For example:
	///			C:\this\is\a\path\file*.txt
	///		can be used as a template to find a series of files file001.txt, file002.txt, file003.txt, etc.
	std::vector<stim::filename> get_list(){
		//this is OS dependent, so we're starting with Windows
		//the Unix version requires Boost

#ifdef _WIN32
		stim::filename filepath(dir_fname());									//initialize filepath with the mask					

		HANDLE            hFind = INVALID_HANDLE_VALUE;							//allocate data structures for looping through files
		WIN32_FIND_DATAA   FindFileData;
		std::vector<stim::filename> file_list;									//initialize a list to hold all matching filenames

		hFind = FindFirstFileA((filepath.str().c_str()), &FindFileData);		//find the first file that matches the specified file path

		if (hFind == INVALID_HANDLE_VALUE) { 									//if there are no matching files
			printf ("Invalid file handle. Error is %u.\n", GetLastError());		//print an error
		}
		else {
			std::string file_name = FindFileData.cFileName;						//save the file name
			std::string file_path = dir();										//the file is in the specified directory, so save it
			stim::filename current_file(file_path + file_name);					//create a stim::filename structure representing this file
			file_list.push_back(current_file);									//push the new stim::filename to the file list

			// List all the other files in the directory.
			while (FindNextFileA(hFind, &FindFileData) != 0){ 					//iterate until there are no more matching files
				file_name = FindFileData.cFileName;								//save the next file
				current_file = (f_name(file_name));								//append the directory
				file_list.push_back(current_file);								//push it to the list
			}
			FindClose(hFind);													//close the file data structure
		}
		return file_list;														//return the list of files

#elif BOOST_PRECOMPILED

		boost::filesystem::path p(dir());	//create a path from the current filename
		std::vector<stim::filename> file_list;
		if(boost::filesystem::exists(p)){
			if(boost::filesystem::is_directory(p)){
				typedef std::vector<boost::filesystem::path> vec;             // store paths,
				vec v;                                // so we can sort them later
				std::copy(boost::filesystem::directory_iterator(p), boost::filesystem::directory_iterator(), back_inserter(v));
				std::sort(v.begin(), v.end());             // sort, since directory iteration
				  // is not ordered on some file systems
				//compare file names to the current template (look for wild cards)
				for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
				{
					//if the filename is a wild card *or* it matches the read file name
					if( prefix == "*" || prefix == (*it).filename().stem().string()){
						//if the extension is a wild card *or* it matches the read file extension
						if( ext == "*" || "." + ext == (*it).filename().extension().string()){
							file_list.push_back((*it).string());	//include it in the final list
						}

					}



				}

			}

		}
		return file_list;
#else
		std::cout<<"ERROR: UNIX systems don't support file lists without the Boost precompiled libraries."<<std::endl;
		exit(1);
#endif
		
	}
	//**************************************************************************************************

	//gets the current working directory
	static stim::filename cwd(){


		char cCurrentPath[FILENAME_MAX];

		 if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath))){
			 std::cout<<"ERROR getting current working directory."<<std::endl;
			 exit(1);
		 }

		 std::cout<<cCurrentPath<<std::endl;
		 return stim::filename(std::string(cCurrentPath) + STIM_FILENAME_DIV);
	}

	void set_name(std::string fname){
		parse_name(fname);
	}

	//get a path relative to the current one
	stim::filename get_relative(std::string rel){

		std::vector<std::string> rel_path = stim::parser::split(rel, STIM_FILENAME_DIV);

		//if the relative path doesn't contain a file, add a blank string to be used as the filename
		if(rel[rel.size()-1] == STIM_FILENAME_DIV)
			rel_path.push_back("");

		//create a stack representing the current absolute path
		std::stack<std::string, std::vector<std::string> > s(path);

		//for each token in the relative path
		for(int d=0; d<rel_path.size() - 1; d++){

			//if the token is a double-dot, pop a directory off of the stack
			if(rel_path[d] == "..")
				s.pop();
			else
				s.push(rel_path[d]);
		}

		std::string* end   = &s.top() + 1;
		std::string* begin = end - s.size();
		std::vector<std::string> result_path(begin, end);

		//create a new path to be returned
		stim::filename result = *this;
		result.path = result_path;
		result.set_name(rel_path.back());

		return result;
	}

	/// This function replaces a wildcard in the prefix with the specified string
	stim::filename insert(std::string str){

		stim::filename result = *this;				//initialize the result filename to the current filename
		size_t loc = result.prefix.find('*');		//find a wild card in the string
		if(loc == std::string::npos)						//if a wildcard isn't found
			result.prefix += str;							//append the value to the prefix
		else
			result.prefix.replace(loc, 1, str);			//replace the wildcard with the string
		return result;								//return the result
	}

	stim::filename insert(size_t i, size_t n = 2){

		std::stringstream ss;
		ss << std::setw(n) << std::setfill('0') << i;
		return insert(ss.str());
	}

	bool is_relative(){
		return !absolute;
	}

	bool is_absolute(){
		return absolute;
	}









};


}	//end namespace stim


#endif