
/*
 * jwm-menu generator
 * copyright Israel Dahl 2015
 * GPL3 license
 * 
 * But whoso keepeth his word,
 * in him verily is the love of God perfected:
 * hereby know we that we are in him.
 * 
 * 1John2:5 KJV Public Domain
 * 
 */

/*
TODO
pugixml...
add global var vector for changed files.
Then check if is cached and then add in via pugixml the new entry. into the right place (how?)
This should cut regen time down significantly...
*/

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <sys/stat.h>
#include <sys/types.h>
#include <algorithm>
#include <float.h>
#include <sstream>
#include <float.h>
#include <dirent.h>
#include <sys/stat.h>
#include <langinfo.h>
#include "../include/common.hpp"
#include "../include/pugixml.hpp"

//////////////////////////////// Global Variables
std::string MENU_CACHE_FILE;
std::string MENU_TO_MERGE;
bool RELOAD;
bool USECOMMENTS;
bool DEBUG_OUT;
// this variable helps me know how deep I am in the menu :D
bool INCEPTION;
bool MERGE_inception;
bool ONLY_UNALLOCATED;
int MERGEFILES;
int MERGEMENUS;
//pugi
pugi::xml_document doc;
pugi::xml_document cache;
//vector
std::vector<std::string> MENU_NAMES;
std::vector<pugi::xml_node> MENU_NODES;
std::vector<std::string> DESKTOP_DIRS;
std::vector<std::string> MERGE_DIRS;
std::vector<std::string> DIRECTORY_FILES;
std::vector<std::string> CURRENT_CATEGORIES;
std::vector<std::string> NOT_CATEGORIES;
std::vector<std::string> NOTtheseFiles;
std::vector<std::string> PATHS;

//list
std::list<std::string> DESKTOP_FILES;
//modification time
struct tm *timer_result;
struct stat stat_structure;

///////////////////////////////////////////////////////////////Functions
//boolean
bool checkCache();
bool checkCacheDirStat(std::string dirname,std::string statFilename);
bool compare_nocase (const std::string& first, const std::string& second);
bool file_name(pugi::xml_node NODE,std::string filename);
bool LayoutMaker(pugi::xml_node Layout, pugi::xml_node RETURN);
bool load(std::string filename);
bool load();
bool make_menu_xdg(std::string infile,std::string outfile);
bool make_submenu(pugi::xml_node SUB, pugi::xml_node RETURN);
bool matchesALLRules(std::string filename, pugi::xml_node rule);
bool matchesAnyRule(std::string filename, pugi::xml_node rule);
bool Merge(pugi::xml_node MergeNode, pugi::xml_node RETURN);
bool no_display(std::string filename);
bool not_showin(std::string filename);
bool only_show(std::string filename);
bool terminal(std::string filename);
bool useComments();
//void
void ANDProcessor(pugi::xml_node SUB, pugi::xml_node RETURN);
void Category(std::string Cat, pugi::xml_node RETURN);
void Category(std::vector<std::string> Cats, pugi::xml_node RETURN);
void Excluded(pugi::xml_node SUB, pugi::xml_node RETURN);
void Included(pugi::xml_node SUB, pugi::xml_node RETURN);
void make_menu();
void NOTProcessor(pugi::xml_node SUB);
void OrProcessor(pugi::xml_node SUB, pugi::xml_node RETURN);
void outXML(std::string message);
void readCacheFile();
int test();
//string
std::string cat_line(std::string filename);
std::string chopTAG(std::string line);
std::string dirFileFullPath(std::string fileName);
std::string exec_line(std::string filename);
std::string getCacheDir();
std::string getCacheFile();
std::string getDDItem(std::string directory, std::string lineToGet);
std::string getDDName(std::string directory);
std::string getStatFile();
std::string grep(std::string args, std::string filename);
std::string get_comment(std::string filename);
std::string get_language_based_line(std::string name,std::string filename);
std::string get_name(std::string filename);
std::string icon_line(std::string filename);
std::string name_line(std::string filename);
std::string remove_percentage(std::string line);
//string vectors
std::vector<std::string> addMergeDirs(std::vector<std::string> directoryFiles);
std::vector<std::string> categoryDirs();
std::vector<std::string> CheckVectorAndRemove(std::vector<std::string> VectorToCheck, std::string thingToRemove);
std::vector<std::string> DefaultCategories();
std::vector<std::string> xdgmenufiles();
//string list
std::list <std::string> get_desktop_files();

//////////////////////////////////////////////////////////////////MAIN
int main(int argc, char* argv[]) {
	bool isCached=checkCache();
	RELOAD=false;
	INCEPTION=false;
	MERGE_inception=false;
	ONLY_UNALLOCATED=false;
	MERGEMENUS=0;
	MERGEFILES=0;
	DEBUG_OUT=false;
	PATHS=linuxcommon::split_paths("PATH","/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:");
	USECOMMENTS=useComments();
	MENU_CACHE_FILE=getCacheFile();
	std::string arg;
	if(argc>0){
		if(argv[1]!=NULL){
			arg=argv[1];
			RELOAD=true;
			//TODO desktop directories
			if(arg.compare("--debug")==0){DEBUG_OUT=true;}
			else if(arg.compare("-debug")==0){DEBUG_OUT=true;}
			else if(linuxcommon::test_file(arg)){
				if(!make_menu_xdg(arg,MENU_CACHE_FILE)){make_menu();}
				return 0;
			}
			else if(arg.compare("--test")==0){
				DEBUG_OUT=true;
				return test();
			}
			else if(arg.compare("--reload")==0){
				outXML("Reload!");
			}
			else{
				std::vector<std::string> tmpVec=linuxcommon::desktop_paths();
				std::string tmpfile=linuxcommon::test_file_in_vector_path(arg,tmpVec);
				if(tmpfile.compare("")!=0){
					if(!make_menu_xdg(tmpfile,MENU_CACHE_FILE)){make_menu();}
				}
				else{make_menu();}
				return 0;
			}
			
			
		}
	}
	if(!RELOAD && isCached && linuxcommon::test_file(MENU_CACHE_FILE.c_str())){
		outXML("Cached file");
		//get cached file
		readCacheFile();
		return 0;
	}
	else{
		if(!make_menu_xdg("/etc/xdg/menus/jwm-applications.menu",MENU_CACHE_FILE)){make_menu();}
	}
	return 0;
}
//////////////////////////////////////////////////////////////BOOL
bool checkCache(){
	std::string statFilename = getStatFile();
	MENU_CACHE_FILE=getCacheFile();
	char datestring[256];
	bool isCached = false;
	std::vector<std::string> uniqueDirs = linuxcommon::desktop_paths();
	std::string testPATH ="/usr/share/applications";
	for( std::vector<std::string>::iterator iter = uniqueDirs.begin();
		iter!=uniqueDirs.end();
		++iter){
		testPATH = *iter;
		bool prevCach=isCached;
		isCached=checkCacheDirStat(testPATH,statFilename);

		//if( (prevCach != isCached ) && (isCached) ){
//isCached=false;}
	}
	if(!linuxcommon::test_file(MENU_CACHE_FILE.c_str())){isCached=false;}
	return isCached;
}
bool checkCacheDirStat(std::string dirname,std::string statFilename){
	if(dirname.compare("")==0)return true;
	std::string uniqueFilename=dirname;
	uniqueFilename=uniqueFilename.erase(0,1);
	unsigned int found = uniqueFilename.find("/");
	if(found<uniqueFilename.length()){
		uniqueFilename=uniqueFilename.erase(found,std::string::npos);
	}
	else{
		uniqueFilename="usr";
	}
	statFilename+="-";
	statFilename+=uniqueFilename;
	unsigned int founddirname = dirname.rfind('/');
    if(founddirname != dirname.length()-1){dirname+="/";}
	dirname+="applications/";
	if(!linuxcommon::test_dir(dirname))return true;
	bool res = false;
	char datestring[256];
	stat(dirname.c_str(), &stat_structure);
	timer_result = gmtime(&(stat_structure.st_mtime));
	strftime(datestring, sizeof(datestring), nl_langinfo(D_T_FMT), timer_result);
	if(linuxcommon::test_file(statFilename.c_str())){
	  //get previous date
	  std::string line;
	  std::string currentTime;
	  std::ifstream thisfile(statFilename.c_str(), std::ifstream::in);
      if(thisfile.is_open()){
          while (getline(thisfile,line)){
              currentTime=line;
              if(currentTime.compare(datestring)==0){ res=true;}
          }
      }
     
	}
	if(statFilename.compare("")!=0){
		std::ofstream myfile;
		//outXML(statFilename);
		myfile.open (statFilename.c_str());
		myfile << datestring;
		myfile.close();
		std::string outfile=statFilename+".old";
		std::ifstream source(statFilename.c_str(),std::ios::binary);
		std::ofstream dest(outfile.c_str(),std::ios::binary);
		dest << source.rdbuf();
	}
	return res;
}
bool compare_nocase (const std::string& first, const std::string& second){
	unsigned int i=0;
	while ( (i<first.length()) && (i<second.length()) ){
		if (tolower(first[i])<tolower(second[i])) return true;
		else if (tolower(first[i])>tolower(second[i])) return false;
		++i;
	}
	return ( first.length() < second.length() );
}
bool file_name(pugi::xml_node NODE,std::string filename){
	if(filename.compare("")==0){return false;}
	if(!NODE){return false;}
	outXML("bool file_name(std::string "+filename+")");
	std::string fname;
	pugi::xml_node temp;
	fname = linuxcommon::look_for_desktop_file(filename);
	if(fname.compare("")==0){return false;}
	if(!NOT_CATEGORIES.empty()){
		std::string tempCat=linuxcommon::get_line_with_equal(fname,"Categories");
		std::vector<std::string> catty =linuxcommon::delimiter_vector_from_string(tempCat,";");
		for(std::vector<std::string>::iterator itn=NOT_CATEGORIES.begin();itn!=NOT_CATEGORIES.end();++itn){
			std::string Cat=*itn;
			outXML("DON'T USE::"+Cat);
			if(linuxcommon::look_for_string_in_vector(catty,Cat)){
				outXML("DON'T USE::Found:"+Cat+" in:"+fname);
				return false;
			}
		}
	}
	//outXML("Found filename="+fname);
	std::string NAME=get_name(fname);
	std::string EXEC=exec_line(fname);
	std::string ICON=icon_line(fname);
	std::string TOOLTIP=get_comment(fname);
	if(EXEC.compare("")==0){return false;}
	//outXML("NAME="+NAME+"\nEXEC="+EXEC+"\nICON="+ICON+"\nTOOLTIP="+TOOLTIP);
	if(NAME.compare("")==0){NAME=EXEC;}
	if(TOOLTIP.compare("")==0){TOOLTIP=EXEC;}
	temp=NODE.append_child("Program");
	temp.append_child(pugi::node_pcdata).set_value(EXEC.c_str());
	pugi::xml_attribute ATTRI = temp.append_attribute("label");
	ATTRI.set_value(NAME.c_str());
	ATTRI = temp.append_attribute("icon");
	ATTRI.set_value(ICON.c_str());
	if(USECOMMENTS){
		ATTRI=temp.append_attribute("tooltip");
		ATTRI.set_value(TOOLTIP.c_str());
	}
	return true;
}
bool LayoutMaker(pugi::xml_node Layout, pugi::xml_node RETURN){
	if(!Layout){return false;}
	if(!RETURN){return false;}
	outXML("bool LayoutMaker(pugi::xml_node Layout, pugi::xml_node RETURN)");
	MERGEFILES=0;
	MERGEMENUS=0;
	if(!Layout.child("Merge")){return false;}
	INCEPTION=true;
	NOT_CATEGORIES.clear();
	for(Layout=Layout.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		std::string val=Layout.text().as_string();
		if(namer.compare("Separator")==0){RETURN.append_child("Separator");}
		else if(namer.compare("Filename")==0){
			//outXML("Layout Filename");
			if(!file_name(RETURN,val)){
				//outXML("Skipping="+val);
			}
		}
		if(namer.compare("Menuname")==0){
			//outXML("Menuname");
			int it=0;
			for( std::vector<std::string>::iterator iter = MENU_NAMES.begin();
			iter!=MENU_NAMES.end();
			++iter){
				std::string testPATH = *iter;
				//outXML("Menu Name="+testPATH+" Trying to find:"+val);
				if(testPATH.compare(val)==0){
					outXML("Menuname found:"+testPATH);
					if(MENU_NODES.empty()){return false;}
					pugi::xml_node currentmenu_node=MENU_NODES[it];
					//RETURN=RETURN.parent();
					int count=0;
					for(std::vector<pugi::xml_node>::iterator item=MENU_NODES.begin();item!=MENU_NODES.end();++item){
						if(count==it){
							//outXML("Erasing node...");
							MERGE_inception=false;
							currentmenu_node=*item;
							if(!make_submenu(currentmenu_node,RETURN)){outXML("Did not make Menuname Menu for:"+val);}
							item=MENU_NODES.erase(item);
							break;
						}
						count++;
					}
					//else{RETURN=RETURN.parent();}
					
					
				//make_submenu
				}
				it++;
			}
			
		/* [show_empty="true"|"false"]
		 * [inline="true"|"false"]
		 * [inline_limit="integer"]
		 * [inline_header="true"|"false"]
		 * [inline_alias="true"|"false"]
		 * 
		*/
		}
		if(namer.compare("Merge")==0){
			if(!Merge(Layout,RETURN)){outXML("Didn't work out");}
			else{return true;}
		}
	}
	INCEPTION=false;
	CURRENT_CATEGORIES.clear();
	NOT_CATEGORIES.clear();
	return true;
}
bool load(){
	std::string fileName=getCacheFile();
	return load(fileName);
}
bool load(std::string filename){
	if(filename.compare("")==0){
		outXML("file does not exist and cannot be loaded");
		return false;
	}
	if(!doc.load_file( filename.c_str() )){
        if(!linuxcommon::test_file(filename.c_str())){
            outXML("The file: "+filename+" was not found.");
            return false;
        }
    }
    return true;
}
bool make_menu_xdg(std::string infile,std::string outfile){
	outXML("bool make_menu_xdg(std::string "+infile+",std::string "+outfile+")");
	if(!linuxcommon::test_file(infile)){return false;}
	if(outfile.compare("")==0){return false;}
	pugi::xml_node node,SUB,Layout,RETURN, DIRdirs, menuNameNode, APPdirs;
	//outXML("LOAD:"+infile);
	pugi::xml_parse_result result = doc.load_file(infile.c_str());
	if(!result){
		std::cout << "<!--Load result: " << result.description() << "\nError offset: " << result.offset <<"-->"<< std::endl;
		return false;
	}
	node=doc.child("Menu");
	if(!node){return false;}
	if(cache.load(outfile.c_str())){cache.reset();}
	RETURN=cache.append_child("JWM");
	SUB=node;
	std::vector<pugi::xml_node> WAITVector;
	for(menuNameNode=node.child("Menu");menuNameNode;menuNameNode=menuNameNode.next_sibling()){
		std::string thisNAME=menuNameNode.child("Name").text().as_string();
		if(thisNAME.compare("")!=0){
			if(menuNameNode.child("OnlyUnallocated")){
				WAITVector.push_back(menuNameNode);
			}
			else{
				//outXML("Adding "+thisNAME+" menu to MENU_NODES");
				MENU_NAMES.push_back(thisNAME);
				MENU_NODES.push_back(menuNameNode);
			}
				
		}
	}
	if(!WAITVector.empty()){
		for(std::vector<pugi::xml_node>::iterator itn=WAITVector.begin();itn!=WAITVector.end();++itn){
			menuNameNode=*itn;
			std::string thisNAME=menuNameNode.child("Name").text().as_string();
			//outXML("OnlyUnAllocated adding "+thisNAME+" to the end");
			if(thisNAME.compare("")!=0){
				MENU_NAMES.push_back(thisNAME);
				MENU_NODES.push_back(menuNameNode);
			}
		}
	}
	if(node.child("DefaultAppDirs")){
		DESKTOP_DIRS=linuxcommon::desktop_paths();
	}
	for(APPdirs=node.child("AppDir");APPdirs;APPdirs=APPdirs.next_sibling()){
		std::string tmp=APPdirs.text().as_string();
		if(tmp.compare("")!=0){DESKTOP_DIRS.push_back(tmp);}
	}
	for(pugi::xml_node mergerDir=node.child("MergeDir");mergerDir;mergerDir=APPdirs.next_sibling()){
		std::string tmp=mergerDir.text().as_string();
		if(tmp.compare("")!=0){
			if(!linuxcommon::test_file(tmp)){
				std::string path=linuxcommon::get_directory_from_filename(infile);
				tmp=path+tmp;
			}
			MERGE_DIRS.push_back(tmp);
		}
	}
	if(node.child("DefaultMergeDirs")){MERGE_DIRS=addMergeDirs(MERGE_DIRS);}
	//TODO actually support merge directories
	if(node.child("DefaultDirectoryDirs")){
		std::vector<std::string> tmpVector=categoryDirs();
		DIRECTORY_FILES.insert( DIRECTORY_FILES.end(), tmpVector.begin(), tmpVector.end());
	}
	
	for(DIRdirs=node.child("DirectoryDir");DIRdirs;DIRdirs=DIRdirs.next_sibling()){
		std::string tmp=DIRdirs.text().as_string();
		if(tmp.compare("")!=0){DIRECTORY_FILES.push_back(tmp);}
	}
	
	/* IF JWM ever supports naming the tray button here...
	std::string MENU_NAME="Applications";
	pugi::xml_node temp=node.child("Directory");
	if(temp){
		std::string TEMPstring=temp.text().as_string();
		if(TEMPstring.compare("")!=0){MENU_NAME=get_name(TEMPstring);}
	}
	*/
	DESKTOP_FILES=get_desktop_files();
	if(node.child("Include")){
		Included(node.child("Include"),RETURN);
	}
	if(node.child("Layout")){
		outXML("Main Layout");
		LayoutMaker(node.child("Layout"),RETURN);
	}
	else{
		if(!make_submenu(node,RETURN)){return false;}
	}
	if(!cache.save_file(outfile.c_str())){
		outXML("FAILED TO SAVE:"+outfile);
		return false;
	}
	linuxcommon::print_file_to_stdout(outfile);
	return true;	
}
bool make_submenu(pugi::xml_node SUB, pugi::xml_node RETURN){
	//outXML("make_submenu");
	if(!SUB)return false;
	if(!RETURN)return false;
	if(MERGE_inception){return false;}
	pugi::xml_node OnlyUnallocated,Layout;
	std::string DIR_FILE,NAME,ICON,TOOLTIP;
	if(!INCEPTION)CURRENT_CATEGORIES.clear();
	NAME=SUB.child("Name").text().as_string();
	TOOLTIP=NAME;
	ICON="application-default-icon";
	if(DIRECTORY_FILES.empty()){DIRECTORY_FILES=categoryDirs();}
	if(SUB.child("Directory")){
		std::string fileNoPath=SUB.child("Directory").text().as_string();
		
		DIR_FILE=linuxcommon::test_file_in_vector_path(fileNoPath,DIRECTORY_FILES);	
		//outXML("**********************"+fileNoPath+":"+DIR_FILE);
		if(DIR_FILE.compare("")!=0){
			TOOLTIP=get_comment(DIR_FILE);
			NAME=get_name(DIR_FILE);
			ICON=icon_line(DIR_FILE);	
		}
	}
	RETURN=RETURN.append_child("Menu");
	RETURN.append_attribute("label")=NAME.c_str();
	RETURN.append_attribute("icon")=ICON.c_str();
	if(USECOMMENTS){RETURN.append_attribute("tooltip")=TOOLTIP.c_str();}
	outXML("make_submenu:"+NAME);
	if(SUB.child("Include")){
		Included(SUB.child("Include"),RETURN);
	}
	if(SUB.child("Separator")){RETURN.append_child("Separator");}
	if(SUB.child("Filename")){
		std::string val=SUB.child("Filename").text().as_string();
		if(!file_name(RETURN,val)){
			//outXML("Skipping="+val);
		}
	}
	
	if(SUB.child("Layout")){
		if(!LayoutMaker(SUB.child("Layout"),RETURN)){return false;}
	}
	if(SUB.child("MergeFile")){
		//TODO
	}
	if(SUB.child("OnlyUnallocated")){
		//how do I do this for a middle Other??
		for( std::list<std::string>::iterator it=DESKTOP_FILES.begin();it!=DESKTOP_FILES.end();++it){
			std::string tempentry=*it;
			if(!file_name(RETURN,tempentry)){
				outXML("Failed to process:"+tempentry);
			}
			
		}
	}
	pugi::xml_node BASE= RETURN.parent();
	if( (!RETURN.child("Program")) && (!RETURN.child("Menu")) ){
		BASE.remove_child(RETURN);
	}
	RETURN=BASE;
	return true;
}
bool matchesALLRules(std::string filename, pugi::xml_node rule){
	for(pugi::xml_node ruleNode=rule.first_child();ruleNode;ruleNode=ruleNode.next_sibling()){
		std::string Name=ruleNode.name();
		std::string Value=ruleNode.text().as_string();
		if(Name.compare("Category")){
			std::string cat=cat_line(filename);
			if(cat.compare(Value)!=0){return false;}
		}
		if(Name.compare("Filename")){
			if(filename.compare(Value)!=0){return false;}
		}
	}
	return true;
}
bool matchesAnyRule(std::string filename, pugi::xml_node rule){
	for(pugi::xml_node ruleNode=rule.first_child();ruleNode;ruleNode=ruleNode.next_sibling()){
		std::string Name=ruleNode.name();
		std::string Value=ruleNode.text().as_string();
		if(Name.compare("Category")){
			std::string cat=cat_line(filename);
			if(cat.compare(Value)==0){return true;}
		}
		if(Name.compare("Filename")){if(filename.compare(Value)==0){return true;}}
		
	}
	return false;
}
bool Merge(pugi::xml_node MergeNode, pugi::xml_node RETURN){
	// if we don't have any more menu nodes, get out!!!
	if(MENU_NODES.empty()){return false;}
	/* We NEED to prevent getting lost in the dream world here...
	 * So, lets check for some crazy inception stuff
	 * 
	 * doc (Our loaded Document)
	 *   Menu (The main Menu XML element)
	 *      Menu (A category Menu)
	 *        Layout
	 *          Merge (Uh oh... this is getting inception-y)
	 */
	 //This should be doc->Menu->Layout->Merge
	pugi::xml_node inceptionNode=MergeNode.parent().parent();
	if(inceptionNode!=doc.child("Menu")){
		MERGE_inception=true;
	}
	else{MERGE_inception=false;}
	std::string mergetype=MergeNode.attribute("type").as_string();
	/* Find out what type of merge we are doing...
	 * 
	 * from freedesktop.org:
	 * type="menus"|"files"|"all"
	 * menus - all sub-menus that are not explicitly mentioned should be inserted in alphabetical order of their visual caption at this point
	 * files - all desktop entries contained in this menu that are not explicitly mentioned should be inserted in alphabetical order of their visual caption at this point.
	 * all -a mix of all sub-menus and all desktop entries that are not explicitly mentioned should be inserted in alphabetical order of their visual caption at this point.
	 */
	
	//outXML("Merge Menus type:"+mergetype);
	
	//If it is menus
	if(mergetype.compare("menus")==0){
		MERGEMENUS=1;
		//if we don't have merge files yet set it to -1 to avoid bring in ALL files just yet.
		if(MERGEFILES==0){MERGEFILES=-1;}
	}
	if(mergetype.compare("files")==0){
		MERGEFILES=1;
		// we can run merge files now after merge menus if merge type=menus exists as well
		if(MERGEMENUS==0){MERGEMENUS=-1;}
	}
	if(mergetype.compare("all")==0){
		MERGEMENUS=1;
		MERGEFILES=1;
	}
	if(MERGEMENUS==1){
		/* Menu
		 *   Name <--- THIS = comp
		 *   Layout
		 *     Merge <-- THIS = MergeNode
		 */
		std::string comp=MergeNode.parent().parent().child("Name").text().as_string();
		bool EVERYTHING=false;
		if(MergeNode.parent().parent()==doc.child("Menu")){
			EVERYTHING=true;
		}
		outXML("Merging Menus");
		std::vector<pugi::xml_node>::iterator iter = MENU_NODES.begin();
		std::string BAIL;
		while(iter!=MENU_NODES.end()){
			NOT_CATEGORIES.clear();
			pugi::xml_node tmp=*iter;
			std::string numero=tmp.child("Name").text().as_string();
			if(numero.compare(BAIL)==0){return false;}
			if(!MERGE_inception){
				BAIL=numero;
				make_submenu(tmp,RETURN);
				outXML("'EVERYTHING' Erasing:"+numero);
				iter=MENU_NODES.erase(iter);
			}
			else{
				std::string category;
				//outXML("Menu:"+numero);
				if(comp.compare(numero)==0){
					outXML("Found menu!! = "+numero);
					iter=MENU_NODES.erase(iter);
					
					return make_submenu(tmp,RETURN);
				}
				else{++iter;}
			}
				
		}
		if(!MERGE_inception){
			CURRENT_CATEGORIES.clear();
			MENU_NODES.clear();
			return true;
		}
	}
	else if((MERGEMENUS!=-1)&&(MERGEFILES==1)){
		outXML("Merging Files");
		for( std::list<std::string>::iterator it=DESKTOP_FILES.begin();it!=DESKTOP_FILES.end();++it){
			std::string tempentry=*it;
			if(!file_name(RETURN,tempentry)){outXML("Failed to process:"+tempentry);}
		}
		return false;
	}
	else{return false;}
	return false;
}
bool no_display(std::string filename) {
  std::string result = linuxcommon::get_line_with_equal(filename,"NoDisplay=");
  if(result.compare("")==0){
    return false;
  }
  std::transform(result.begin(),result.end(),result.begin(),::tolower);
  if (result.compare("true")==0){return true;}
  else{return false;}

  return false;
}
bool not_showin(std::string filename) {
  const char* desktop1 = getenv("XDG_CURRENT_DESKTOP");
  if(desktop1==NULL){desktop1="JWM";}
  std::string desktop=desktop1;
  std::transform(desktop.begin(),desktop.end(),desktop.begin(),::tolower);
  const char* desktop_session1 = getenv("DESKTOP_SESSION");
  if(desktop_session1==NULL){desktop_session1="JWM";}
  std::string desktop_session=desktop_session1;
  std::transform(desktop_session.begin(),desktop_session.end(),desktop_session.begin(),::tolower);
  std::string result = linuxcommon::get_line_with_equal(filename,"NotShowIn=");
  unsigned int found = 0;
  if(result.compare("")==0){
    return false;
  }
  std::transform(result.begin(),result.end(),result.begin(),::tolower);
  std::string temp1,temp2;
  temp2=result;
  for(unsigned int i = 0; i<result.length();i++){
    found = temp2.find(';');
    if(found < temp2.length()){
      temp2=temp2.erase(found,temp2.length());
      temp1 = result.erase(0,found+1);
    if (desktop.compare("")!=0){
      if (temp2.compare(desktop)==0){
      return true;
      }
    }
    if (desktop_session.compare("")!=0){
      if (temp2.compare(desktop_session)==0){
      return true;
      }
    }
      temp2=temp1;
    }
  }
  return false;
}
bool only_show(std::string filename) {
	const char* desktop1 = getenv("XDG_CURRENT_DESKTOP");
	if(desktop1==NULL){desktop1="JWM";}
	std::string desktop=desktop1;
	std::transform(desktop.begin(),desktop.end(),desktop.begin(),::tolower);
	const char* desktop_session1 = getenv("DESKTOP_SESSION");
	if(desktop_session1==NULL){desktop_session1="JWM";}
	std::string desktop_session=desktop_session1;
	std::transform(desktop_session.begin(),desktop_session.end(),desktop_session.begin(),::tolower);

	std::string result = linuxcommon::get_line_with_equal_after_header("Desktop Entry",filename,"OnlyShowIn");
	outXML("only_show(std::string "+filename+"\nRESULT:"+result);
	unsigned int found = 0;
	//Only show in line doesn't exist
	if(result.compare("")==0){return true;}
  //compare lower case words only
	std::transform(result.begin(),result.end(),result.begin(),::tolower);
	std::string temp1,temp2;
	temp2=result;

	for(unsigned int i = 0; i<result.length();i++){
		found = temp2.find(';');
		if(found < temp2.length()){
			temp2=temp2.erase(found,temp2.length());
			temp1 = result.erase(0,found+1);
			if (desktop.compare("")!=0){
				if (temp2.compare(desktop)==0){return true;}
			}
			if (desktop_session.compare("")!=0){
				if(temp2.compare(desktop_session)==0){return true;}
			}
			temp2=temp1;
		}
		else{
			if (temp2.compare(desktop)==0){return true;}
			if (temp2.compare(desktop_session)==0){return true;}
		}
	}
	return false;
}
bool terminal(std::string filename) {
  std::string result = linuxcommon::get_line_with_equal(filename,"Terminal=");
  if(result.compare("")==0){
  //Only show in line doesn't exist
    return false;
  }
  //compare lower case words only
  std::transform(result.begin(),result.end(),result.begin(),::tolower);
  if(result.find("true")<result.length()){return true;}
  return false;
}
bool useComments(){
    std::string jwmVersion = "jwm -v";// | grep JWM |sed 's/ by.*//' |sed 's/JWM v//'";
	std::string version = linuxcommon::term_out(jwmVersion);
	version=linuxcommon::remove_cruft(version,"JWM v");
	
	unsigned int found=0;
	found=version.find(" by");
	if(found<version.length()){
		version=version.erase(found,std::string::npos);
	}
	std::string newTagVersion = "2.3.3";
    if(version.compare(newTagVersion)==0){return true;}
    int dot1 = version.find('.');
    int dot2 = version.rfind('.');
    std::string major = version.substr(0,dot1);
    std::string minor = version.substr(dot1+1,dot2);
    std::string rev = version.substr(dot2+1,std::string::npos);
    int majorVersion = linuxcommon::convert_string_to_number(major.c_str());
    int minorVersion = linuxcommon::convert_string_to_number(minor.c_str());
    int revVersion = linuxcommon::convert_string_to_number(rev.c_str());
   // std::cout<<"Version:"<<majorVersion<<"."<<minorVersion<<"."<<revVersion<<std::endl;
    bool MAJ=false;
    int newTagVersionMajor = 2;
    if (majorVersion > newTagVersionMajor){return true;}
    if(majorVersion < newTagVersionMajor){return false;}
    int newTagVersionMinor = 3;
    if(minorVersion > newTagVersionMinor){return true;}
    int revno = 3;
    if(minorVersion == newTagVersionMinor){if(revVersion>=revno){return true;}}
    return false;
}

////////////////////////////////////////////////////////////////////VOID
void ANDProcessor(pugi::xml_node SUB, pugi::xml_node RETURN){
	outXML("ANDProcessor");
	if(!SUB)return;
	if(!RETURN)return;
	pugi::xml_node Layout;
	std::vector<std::string> theseCats;
	for(Layout=SUB.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		outXML("AND->"+namer);
		std::string val=Layout.text().as_string();
		if(namer.compare("Filename")==0){
			if(!file_name(RETURN,val)){
				//outXML("Skipping="+val);
			}
		}
		if(namer.compare("Not")==0){
			for(pugi::xml_node tmp=Layout.first_child();tmp;tmp=tmp.next_sibling()){
				std::string namer2=tmp.name();
				std::string val2=tmp.text().as_string();
				outXML("AND->"+namer+"->"+namer2+"="+val2);
				if(namer2.compare("Filename")==0){
					NOTtheseFiles.push_back(val2);
				}
				if(namer2.compare("Category")==0){
					outXML("Not this Category:"+val2);
					NOT_CATEGORIES.push_back(val2);
				}
			}
		}
		if(namer.compare("Category")==0){
			theseCats.push_back(val);
			outXML("AND adding:"+val);
			CURRENT_CATEGORIES.push_back(val);
			//Category(val,RETURN);
		}
	}
	Category(theseCats,RETURN);
}
void Category(std::string Cat, pugi::xml_node RETURN){
	if(Cat.compare("")==0)return;
	if(!RETURN)return;
	outXML("void Category(std::string "+Cat+", pugi::xml_node RETURN)");
	int iter=0;
	for( std::list<std::string>::iterator it=DESKTOP_FILES.begin();it!=DESKTOP_FILES.end();++it){
		std::string tempentry=*it;
		std::string tempCat=linuxcommon::get_line_with_equal(tempentry,"Categories");
		std::vector<std::string> catty =linuxcommon::delimiter_vector_from_string(tempCat,";");
		
		if(linuxcommon::look_for_string_in_vector(catty,Cat)){
			outXML("Category:"+tempCat+"\nFound:"+Cat);
			if(!file_name(RETURN,tempentry)){
				//outXML("Failed to process:"+tempentry);
			}
			else{
				//outXML("found category:"+Cat+" in:"+tempentry);
				//Remove desktop file from list??
				
				it=DESKTOP_FILES.erase(it);//it);
			}
		}
		iter++;
	}
}
void Category(std::vector<std::string> Cats, pugi::xml_node RETURN){
	if(Cats.empty())return;
	if(!RETURN)return;
	outXML("void Category(std::vector<std::string> Cats, pugi::xml_node RETURN)");
	std::list<std::string>::iterator it=DESKTOP_FILES.begin();//it!=DESKTOP_FILES.end();++it){
	while(it!=DESKTOP_FILES.end()){
		std::string tempentry=*it;
		bool WORKEDout=false;
		std::string tempCat=linuxcommon::get_line_with_equal(tempentry,"Categories");
		std::vector<std::string> catty =linuxcommon::delimiter_vector_from_string(tempCat,";");		
		for( std::vector<std::string>::iterator itera=Cats.begin();itera!=Cats.end();++itera){
			std::string Cat=*itera;			
			if(linuxcommon::look_for_string_in_vector(catty,Cat)){
				if(!file_name(RETURN,tempentry)){outXML("Failed to process:"+tempentry);}
				else{
					outXML("found category:"+Cat+" in:"+tempentry);
					WORKEDout=true;
					break;
				}
			}
		}
		if(WORKEDout){it=DESKTOP_FILES.erase(it);}
		else{++it;}
	}
	outXML("End of Category");
}
void Excluded(pugi::xml_node SUB, pugi::xml_node RETURN){
	if(!SUB)return;
	if(!RETURN)return;
	std::vector <std::string> NOTlist;
	std::vector <std::string> YESlist;
	pugi::xml_node Layout;
	for(Layout=SUB.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		std::string val=Layout.text().as_string();
		if(namer.compare("Filename")==0){
			if(!file_name(RETURN,val)){
				//outXML("Skipping="+val);
			}
		}
		if(namer.compare("Category")==0){
			Category(val,RETURN);
		}
		//these can hold category or filename
		for(std::list<std::string>::iterator it=DESKTOP_FILES.begin();it!=DESKTOP_FILES.end();++it){
			std::string tempentry=*it;
			if(namer.compare("All")==0){
				YESlist.push_back(tempentry);
			}
			if(namer.compare("Not")==0){
				if(matchesAnyRule(tempentry,Layout)){
					NOTlist.push_back(tempentry);
				}
				else{YESlist.push_back(tempentry);}
			}
			if(namer.compare("And")==0){
				if(matchesALLRules(tempentry,Layout)){
					YESlist.push_back(tempentry);
				}
				else{NOTlist.push_back(tempentry);}
			}
			if(namer.compare("Or")==0){
				if(matchesAnyRule(tempentry,Layout)){
					YESlist.push_back(tempentry);
				}
				else{NOTlist.push_back(tempentry);}
			}
		}
	}
	//TODO check not list and Yes list to remove from Not list
	for(std::vector<std::string>::iterator it=NOTlist.begin();it!=NOTlist.end();++it){
		std::string fileName=*it;
		if(linuxcommon::test_file(fileName)){
			pugi::xml_node tmp=RETURN.append_child("Program");
			std::string numero, icon,tooltip,exec;		
			exec=exec_line(fileName);
			if(exec.compare("")!=0){
				numero=get_name(fileName);
				icon=icon_line(fileName);
				if(icon.compare("")==0){icon="application-default-icon";}
				if(numero.compare("")==0){numero=exec;}
				tmp.append_attribute("label")=numero.c_str();
				tmp.append_attribute("icon")=icon.c_str();
				if(USECOMMENTS){
					tooltip=get_comment(fileName);
					if(tooltip.compare("")==0){tooltip=numero;}
					tmp.append_attribute("tooltip")=tooltip.c_str();
				}
				tmp.append_child(pugi::node_pcdata).set_value(exec.c_str());
			}
		}		
	}
}
void Included(pugi::xml_node SUB, pugi::xml_node RETURN){
	outXML("Included");
	NOTtheseFiles.clear();
	if(!SUB)return;
	if(!RETURN)return;
	std::vector <std::string> YESlist;
	std::copy( YESlist.begin(), YESlist.end(), std::back_inserter( DESKTOP_FILES ) );
	pugi::xml_node Layout;
	pugi::xml_node NAMEnode=SUB.parent();
	NAMEnode=NAMEnode.child("Name");
	std::string tmpCater=NAMEnode.name();
	std::vector<std::string> theseFiles;
	for(Layout=SUB.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		std::string val=Layout.text().as_string();
		if(namer.compare("Or")==0){
			OrProcessor(Layout,RETURN);
		}
		if(namer.compare("Filename")==0){
			if(!file_name(RETURN,val)){
				//outXML("Skipping="+val);
			}
		}
		if(namer.compare("Category")==0){
			outXML("Included:"+val);
			CURRENT_CATEGORIES.push_back(val);
			//theseCats.push_back(val);
		}
		//these can hold category or filename
		if(namer.compare("And")==0){
			ANDProcessor(Layout,RETURN);
		}
		if(namer.compare("Not")==0){
			NOTProcessor(Layout);
		}
		
	}
	if(CURRENT_CATEGORIES.empty()){CURRENT_CATEGORIES.push_back(tmpCater);}
	
	if(!NOTtheseFiles.empty()){
		for(std::vector<std::string>::iterator itn=NOTtheseFiles.begin();itn!=NOTtheseFiles.end();++itn){
			std::string tmpr=*itn;
			outXML("Removing:"+tmpr);
			YESlist=CheckVectorAndRemove(YESlist,*itn);
		}
	}
	//TODO check not list and Yes list to remove from yes list
	for(std::vector<std::string>::iterator it=YESlist.begin();it!=YESlist.end();++it){
		outXML("Yes List!");
		std::string fileName=*it;
		std::string cat=cat_line(fileName);
		std::vector<std::string> tmpCAT=linuxcommon::delimiter_vector_from_string(cat,";");
		if(!NOT_CATEGORIES.empty()){
			for(std::vector<std::string>::iterator itn=NOT_CATEGORIES.begin();itn!=NOT_CATEGORIES.end();++itn){
				if(!linuxcommon::look_for_string_in_vector(tmpCAT,*itn)){
					for(std::vector<std::string>::iterator itera=CURRENT_CATEGORIES.begin();itera!=CURRENT_CATEGORIES.end();++itera){
						std::string category=*itera;
						if(linuxcommon::look_for_string_in_vector(tmpCAT,category)){
							pugi::xml_node tmp=RETURN.append_child("Program");
							std::string numero, icon,tooltip,exec;
							exec=exec_line(fileName);
							if(exec.compare("")!=0){
								numero=get_name(fileName);
								icon=icon_line(fileName);
								if(icon.compare("")==0){icon="application-default-icon";}
								if(numero.compare("")==0){numero=exec;}
								tmp.append_attribute("label")=numero.c_str();
								tmp.append_attribute("icon")=icon.c_str();
								if(USECOMMENTS){
									tooltip=get_comment(fileName);
									if(tooltip.compare("")==0){tooltip=numero;}
									tmp.append_attribute("tooltip")=tooltip.c_str();
								}
								tmp.append_child(pugi::node_pcdata).set_value(exec.c_str());
							}
						}
					}
				}
			}
		}
	}
}
void make_menu(){
	outXML("Make Generic menu");
	DESKTOP_DIRS=linuxcommon::desktop_paths();
	DESKTOP_FILES=get_desktop_files();
	CURRENT_CATEGORIES=DefaultCategories();
	if(MENU_CACHE_FILE.compare("")==0)getCacheFile();
	doc.load_file(MENU_CACHE_FILE.c_str());
	doc.reset();
	doc.append_child("JWM");
	std::string fallbackIcon = "applications-";
	pugi::xml_node BASE=doc.first_child();
	for( std::vector<std::string>::iterator it = CURRENT_CATEGORIES.begin();
		it!=CURRENT_CATEGORIES.end();
		++it){
		std::string category=*it;
		std::string catIcon;
		std::string tmpIcon=category;
		std::transform(tmpIcon.begin(), tmpIcon.end(), tmpIcon.begin(), ::tolower);
		if(tmpIcon.compare("education")==0)tmpIcon="science";
		if(tmpIcon.compare("audiovideo")==0)tmpIcon="multimedia";
		if(tmpIcon.compare("utility")==0)tmpIcon="accessories";
		if(tmpIcon.compare("game")==0)tmpIcon="games";
		if(tmpIcon.compare("network")==0)tmpIcon="internet";
		catIcon=fallbackIcon+tmpIcon;
		if(tmpIcon.compare("Accessibility")==0)catIcon="preferences-desktop-accessibility";
		if(tmpIcon.compare("settings")==0)catIcon="preferences-desktop";
		pugi::xml_node node=BASE.append_child("Menu");
		node.append_attribute("label")=category.c_str();
		node.append_attribute("icon")=catIcon.c_str();
		for( std::list<std::string>::iterator iter = DESKTOP_FILES.begin();
			iter!=DESKTOP_FILES.end();
			++iter){
			std::string result=*iter;
			outXML("Processing:"+result);
			//if( (!no_display(result)) || (only_show(result)) || (!not_showin(result)) ){
				std::string thisCAT=cat_line(result);
				std::vector<std::string> CATS=linuxcommon::delimiter_vector_from_string(thisCAT,";");
				if(linuxcommon::look_for_string_in_vector(CATS,category)){
					std::string EXEC=exec_line(result);
					if(EXEC.compare("")==0)break;
					std::string NAME=name_line(result);
					if(NAME.compare("")==0){NAME=EXEC;}
					std::string ICON=icon_line(result);
					NAME=linuxcommon::quote_xml(NAME);				
					ICON=linuxcommon::quote_xml(ICON);
					EXEC=linuxcommon::quote_xml(EXEC);
					pugi::xml_node tmp=node.append_child("Program");
					tmp.append_attribute("label")=NAME.c_str();
					tmp.append_attribute("icon")=ICON.c_str();
					tmp.append_child(pugi::node_pcdata).set_value(EXEC.c_str());
				}
			//}
				
		}
	}
	if(!doc.save_file(MENU_CACHE_FILE.c_str())){
		outXML("FAILED TO SAVE:"+MENU_CACHE_FILE);
		return;
	}
	linuxcommon::print_file_to_stdout(MENU_CACHE_FILE);
}//make_menu
void NOTProcessor(pugi::xml_node SUB){
	if(!SUB)return;
	pugi::xml_node Layout;
	std::vector<std::string> theseCats;
	for(Layout=SUB.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		std::string val=Layout.text().as_string();
		if(namer.compare("Filename")==0){
			NOTtheseFiles.push_back(val);
		}
		if(namer.compare("Category")==0){
			outXML("Not this category:"+val);
			NOT_CATEGORIES.push_back(val);
			
		}
	}
}
void OrProcessor(pugi::xml_node SUB, pugi::xml_node RETURN){
	outXML("OrProcessor");
	if(!SUB)return;
	if(!RETURN)return;
	pugi::xml_node Layout;
	std::vector<std::string> theseCats;
	for(Layout=SUB.first_child();Layout;Layout=Layout.next_sibling()){
		std::string namer=Layout.name();
		std::string val=Layout.text().as_string();
		if(namer.compare("Filename")==0){
			if(!file_name(RETURN,val)){
				//outXML("Skipping="+val);
			}
		}
		if(namer.compare("Category")==0){
			theseCats.push_back(val);
			outXML("OR adding:"+val);
			CURRENT_CATEGORIES.push_back(val);
			//Category(val,RETURN);
		}
	}
	Category(theseCats,RETURN);
}
void outXML(std::string message){
	if(DEBUG_OUT)std::cout<<"<!--"<<message<<"-->"<<std::endl;
}
void readCacheFile(){
	std::string Filename=getCacheFile();
	if(Filename.compare("")==0){return;}
	std::string line;
	std::string currentTime;
	std::ifstream thisfile(Filename.c_str(), std::ifstream::in);
	if(thisfile.is_open()){
		while (getline(thisfile,line)){
			std::cout<<line<<std::endl;
		}
	}
}
int test(){
	if(!make_menu_xdg("/etc/xdg/menus/jwm-applications.menu","/home/israeldahl/temp.xml")){ //MENU_CACHE_FILE
		outXML("FAILED!!!");
		make_menu();
		return 1;
	}
	return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////string
std::string cat_line(std::string filename){
	std::string AV,DEV,ED,GAME,ART,NET,OFF,SCI,SET,SYS,ACC,OTH,WINE,CHROME,category,CAT;
	AV = "AudioVideo";
	DEV = "Development";
	ED = "Education";
	GAME = "Game";
	ART = "Graphics";
	NET = "Network";
	OFF = "Office";
	SCI = "Science";
	SET = "Settings";
	SYS = "System";
	ACC = "Utility";
	WINE= "Wine";
	OTH = "Other";
	CHROME = "Chrome Apps";
	std::string temp1,temp2;
	unsigned int found;
	std::string catline = linuxcommon::get_line_with_equal(filename,"Categories=");
	/*unsigned int chromefind = filename.find("chrome");
	if (chromefind < filename.length()){return CHROME;}*/
	if (catline.compare("")==0){return OTH;}
	temp2=catline;
	found = temp2.find(';');
	if(found>temp2.length()){return catline;}
	std::string thisCAT="";
	for(unsigned int i = 0; i<catline.length();i++){
		found = temp2.find(';');
		if(found < temp2.length()){
			temp2=temp2.erase(found,temp2.length());
			temp1 = catline.erase(0,found+1);
			category=temp2;
			temp2=temp1;
			if( category.compare(AV)==0){thisCAT=AV;}
			else if( category.compare(DEV)==0){thisCAT=DEV;}
			else if( category.compare(DEV)==0){thisCAT=DEV;}
			else if( category.compare(ED)==0){thisCAT=ED;}
			else if( category.compare(GAME)==0){thisCAT=GAME;}
			else if( category.compare(ART)==0){thisCAT=ART;}
			else if( category.compare(NET)==0){thisCAT=NET;}
			else if( category.compare(OFF)==0){thisCAT=OFF;}
			else if( category.compare(SCI)==0){thisCAT=SCI;}
			else if( category.compare(SET)==0){thisCAT=SET;}
			else if( category.compare(SYS)==0){thisCAT=SYS;}
			else if( category.compare(ACC)==0){thisCAT=ACC;}
			else if( category.compare(WINE)==0){thisCAT=WINE;}
			else{
				if(thisCAT.compare("")==0){thisCAT=OTH;}
			}
		}
	}
	return thisCAT; //category;
}
std::string chopTAG(std::string line){
	unsigned int tLen=line.find('>');
	std::string temp = line;
	temp=temp.erase(0,tLen+1);
	tLen=temp.find('<');
	temp=temp.erase(tLen,std::string::npos);	
	return temp;
}
std::string dirFileFullPath(std::string fileName){
	//Find the name in the file!
	std::string filename=linuxcommon::find_xdg_config_dir_subdir("menus");
	filename+=fileName;
	if(linuxcommon::test_file(filename)){return filename;}
	return "";
}
std::string exec_line(std::string filename) {
	std::string tmpExec=linuxcommon::get_line_with_equal(filename,"TryExec=");
	if(tmpExec.compare("")!=0){
		if(!linuxcommon::test_exec(tmpExec)){
			unsigned int finder=tmpExec.rfind("/");
			if(finder<tmpExec.length()){
				tmpExec=tmpExec.erase(0,finder+1);
			}
			tmpExec=linuxcommon::test_file_in_vector_path(tmpExec,PATHS);
			if(tmpExec.compare("")==0){return "";}
		}
	}
	std::string execline=linuxcommon::get_line_with_equal(filename,"Exec=");
	if(execline.compare("")==0){return execline;}
	execline=remove_percentage(execline);
	if(terminal(filename)){
		std::string myterm=linuxcommon::term_out("which x-terminal-emulator || which xterm");
		execline=myterm + " -e " + execline;
	}
	return execline;
}
std::string getCacheDir(){
	std::string cacheFile;
	const char* xdgcache =  getenv("XDG_CACHE_HOME");
	if(xdgcache==NULL){
		std::string homie = linuxcommon::home_path();
		if(homie.compare("")==0){return "";}
		else{
			std::string temp = homie;
			temp+=".cache";
			cacheFile=temp;
		}
	}
	else{
		cacheFile=xdgcache;
	}
    if(cacheFile.compare("")==0){return "";}
    if(!linuxcommon::test_dir(cacheFile)){mkdir(cacheFile.c_str(),0600);}
    unsigned int found = cacheFile.rfind('/');
    if(found != cacheFile.length()-1){cacheFile+="/";}
    return 	cacheFile;
}
std::string getCacheFile(){
	std::string cacheFile=getCacheDir();
	if(cacheFile.compare("")==0){return "";}
    cacheFile+="jwm-menu.cache";
	return cacheFile;
}
std::string getDDItem(std::string directory, std::string lineToGet){
	std::vector<std::string> uniqueDirs = linuxcommon::desktop_paths();
	std::string testPATH,line;
	unsigned int found = 0;
	DIR *dirent = NULL;
	for( std::vector<std::string>::iterator it = uniqueDirs.begin();
		it!=uniqueDirs.end();
		++it){
			
		testPATH = *it;
		//make sure this isn't blank
		if(testPATH.compare("")!=0){
			//look to see if we have a / at the end... remove it if we do...
			found = testPATH.rfind('/');
			if (found == testPATH.length()-1){testPATH=testPATH.substr(0,testPATH.length()-1);}
			std::string tempdirName=testPATH;
			tempdirName+="/desktop-directories/";
			dirent = opendir(tempdirName.c_str());
			if (dirent!=NULL){
				tempdirName+=directory;
				if(linuxcommon::get_line_with_equal(tempdirName,lineToGet).compare("")!=0){line=linuxcommon::get_line_with_equal(tempdirName,lineToGet);}
			}
		}
	}
	line==linuxcommon::quote_xml(line);
	return line;
}
std::string getDDName(std::string directory){
	std::vector<std::string> uniqueDirs = linuxcommon::desktop_paths();
	std::string testPATH,line;
	unsigned int found = 0;
	DIR *dirent = NULL;
	for( std::vector<std::string>::iterator it = uniqueDirs.begin();
		it!=uniqueDirs.end();
		++it){
			
		testPATH = *it;
		//make sure this isn't blank
		if(testPATH.compare("")!=0){
			//look to see if we have a / at the end... remove it if we do...
			found = testPATH.rfind('/');
			if (found == testPATH.length()-1){testPATH=testPATH.substr(0,testPATH.length()-1);}
			std::string tempdirName=testPATH;
			tempdirName+="/desktop-directories/";
			dirent = opendir(tempdirName.c_str());
			if (dirent!=NULL){
				tempdirName+=directory;
			}
			if(get_name(tempdirName).compare("")!=0){line=get_name(tempdirName);}
			closedir(dirent);
			
		}
	}
	line=linuxcommon::quote_xml(line);
	return line;
}
std::string getStatFile(){
	std::string cacheFile=getCacheDir();
	if(cacheFile.compare("")==0){return "";}
    cacheFile+="appmod";
	return cacheFile;	
}
std::string grep(std::string args, std::string filename) {
  ///Return the FIRST match of the 'args' from a file
  // this is like  line=`grep $args $filename` that only returns one line
      std::string line=linuxcommon::grep(args,filename);
      if(line.find("[Desktop Action")<line.length()){return "";}
      return line;
}
std::string get_comment(std::string filename){
	std::string name="Comment";
	return get_language_based_line(name,filename);
}
std::string get_language_based_line(std::string name,std::string filename){
	std::string tryGet,tempstr;
	std::string mylang;
	const char* thisLang= getenv("LANGUAGE");
	if(thisLang==NULL){
		const char* myLANG = getenv("LANG");
		if(myLANG !=NULL){
			mylang=myLANG;
			unsigned int found = mylang.find('.');
			if(found < mylang.length()){
				mylang = mylang.erase(found,std::string::npos);
			}
			tempstr=name;
			tempstr+="[";
			tempstr+=mylang;
			tempstr+="]=";
			tryGet=linuxcommon::get_line_with_equal(filename,tempstr);
		}
	}
	else{
		mylang=thisLang;
		tempstr=name;
		tempstr+="[";
		tempstr+=mylang;
		tempstr+="]=";
		tryGet=linuxcommon::get_line_with_equal(filename,tempstr);
	}
	if( (tryGet.compare("")==0) && (mylang.compare("")!=0) ){
		unsigned int dash = mylang.find('_');
		if(dash<mylang.length()){
			mylang = mylang.erase(dash,std::string::npos);
			tempstr=name;
			tempstr+="[";
			tempstr+=mylang;
		tempstr+="]=";
		tryGet=linuxcommon::get_line_with_equal(filename,tempstr);
	}
  }
  if (tryGet.compare("")==0){
    tryGet=linuxcommon::get_line_with_equal(filename,name);
  }
  return tryGet;
}
std::string get_name(std::string filename){
	std::string name="Name";
	return get_language_based_line(name,filename);
}
std::string icon_line(std::string filename) {
  std::string icon = linuxcommon::get_line_with_equal(filename,"Icon=");
  //outXML("icon:"+icon+"::");
  if ( icon.compare("")==0){return "application-default-icon";}
  return icon;
}
std::string name_line(std::string filename) {
  std::string base_lang,result,strlang,strlanguage,startbase,NAME,LOCALNAME,LOCALEND;
  NAME="Name";
  LOCALNAME="Name[";
  LOCALEND="]=";
  unsigned int underscore = 0;
  char* lang = getenv("LANG");
  char* language = getenv("LANGUAGE");
  if (lang!=NULL){
	strlang = lang;
    result=linuxcommon::get_line_with_equal(filename,(LOCALNAME+ strlanguage + LOCALEND));

    if(result.compare("")!=0){return result;}
    base_lang=strlanguage;
  }
  else{
    if (language!=NULL){
		strlanguage =language;
        unsigned int dot = 0;
        dot=strlang.find('.');
        if(dot<strlang.length()){
			startbase=strlang.erase(dot,strlang.length());
			result=linuxcommon::get_line_with_equal(filename, (LOCALNAME+ startbase + LOCALEND));

			if(result.compare("")!=0){return result;}
			base_lang=startbase;
		}
		else{return linuxcommon::get_line_with_equal(filename,"Name=");}
     }
     else{return linuxcommon::get_line_with_equal(filename,"Name=");}
  }

  underscore=base_lang.find('_');
  if(underscore<base_lang.length()){
	base_lang=base_lang.erase(underscore,base_lang.length());
	result=linuxcommon::get_line_with_equal( filename,(LOCALNAME+ base_lang + LOCALEND));
	if(result.compare("")!=0){return result;}
  }
  return linuxcommon::get_line_with_equal(filename,"Name=");
}
std::string remove_percentage(std::string line){
    if(line.compare("")==0){return line;}
    unsigned int percentfind=0;
    std::string tempstring;
    percentfind=line.find(" %",0);
    if(percentfind>line.length()){return line;}
    unsigned int semifind=line.find(";",percentfind);
  //  #if 0
    bool andis=false;
    bool semiis=false;
    if(semifind<line.length()){
		semiis=true;
	}
    unsigned int andfind=line.find("&",percentfind);
    if(andfind<line.length()){
		andis=true;
	}
	if(andis && semiis){
		if(andfind<semifind){
			percentfind=line.find(" %",0);
			if(percentfind<line.length()){tempstring=line.erase(percentfind, andfind);}
		}
		else{tempstring=line.erase(percentfind, semifind);}
	}
	else if(andis){
		andfind=line.find("&",percentfind);
		percentfind=line.find(" %",0);
		if(percentfind<line.length()){tempstring=line.erase(percentfind, andfind);}
	}
	else if(semiis){
		semifind=line.find(";",percentfind);
		percentfind=line.find(" %",0);
		if(percentfind<line.length()){tempstring=line.erase(percentfind, semifind);}
	}
	else{
		percentfind=line.find(" %",0);
		if(percentfind<line.length()){tempstring=line.erase(percentfind, 3);}
	}
	//#endif
	//tempstring=line.erase(percentfind, 3);
	return tempstring;
}
////////////////////////////////////////////////////////////////////////////////////////////////////String Vectors
std::vector<std::string> addMergeDirs(std::vector<std::string> directoryFiles){
	//outXML("std::vector<std::string> addMergeDirs(std::vector<std::string> directoryFiles)");
	std::string dirToOpen = "/etc/xdg/menus/applications-merged/";
	std::vector<std::string> MENUS= linuxcommon::split_paths("XDG_CONFIG_DIRS","/etc/xdg:/etc/xdg/xdg-torios");
	DIR *dir = NULL;
	struct dirent *entryPointer = NULL;
	std::string result;
	std::string fullpath="/menus/applications-merged/";
	for( std::vector<std::string>::iterator it = MENUS.begin();
		it!=MENUS.end();
		++it){
		
		dirToOpen=*it;
		dirToOpen+=fullpath;
		dir = opendir(dirToOpen.c_str());
		if (dir!=NULL){
			while ((entryPointer=readdir(dir)) != NULL){
				if ((entryPointer->d_type == DT_REG)&&(entryPointer->d_name[0] != '.')){
					result=dirToOpen;
					result += entryPointer->d_name;
					unsigned period = result.rfind(".");
					bool isMENU = false;
					if(result.compare("")!=0){
						if(period<result.length()){
							std::string testMenu = result.substr(period+1,std::string::npos);
							if(testMenu.compare("menu")==0){isMENU = true;}
						}
						if(isMENU){directoryFiles.push_back(dirToOpen);/*outXML("Merge Dir: "+dirToOpen);*/} //isMENU
					}//result !empty
				}//dirent is file
			}//dirent !NULL
		}//directory !NULL
	}
	closedir(dir);
	return directoryFiles;
}
std::vector<std::string> categoryDirs(){
	//outXML("std::vector<std::string> categoryDirs()");
	std::string DD="/desktop-directories/";
	std::string dirToOpen,testPATH;
	const char* home=getenv("HOME");
	std::vector<std::string> dirVector;
	std::vector<std::string> tmpVector;
	std::vector<std::string> returnVector;
	unsigned int found=0;
	std::string HOME;
	if(home != NULL){
		HOME=home;
	}
	std::string getdirs=HOME;
	if(HOME.compare("")!=0){
	  getdirs+="/.local/share/";
	  dirVector=linuxcommon::split_paths("XDG_DATA_HOME",getdirs.c_str());
	}
	//get the data dirs
	tmpVector=linuxcommon::split_paths("XDG_DATA_DIRS","/usr/share:/usr/local/share");
	//insert the data dirs on the data home (if it exists);
    dirVector.insert(dirVector.end(),tmpVector.begin(),tmpVector.end());
	for( std::vector<std::string>::iterator it = dirVector.begin();
		it!=dirVector.end();
		++it){
		//the current string in the vector
		testPATH = *it;
		//make sure this isn't blank
		if(testPATH.compare("")!=0){
			//look to see if we have a / at the end... remove it if we do...
			found = testPATH.rfind('/');
			if (found == testPATH.length()-1){testPATH=testPATH.substr(0,testPATH.length()-1);}
			//see if there is a pathToGet directory
			dirToOpen = testPATH + DD;
			//see if it has the desktop directories folder
			if(linuxcommon::test_dir(dirToOpen)){returnVector.push_back(dirToOpen);}
		}
	}
	return returnVector;
}
std::vector<std::string> CheckVectorAndRemove(std::vector<std::string> VectorToCheck, std::string thingToRemove){
	std::vector<std::string> TEMPvector=VectorToCheck;
	std::vector<std::string>::iterator it = find (TEMPvector.begin(), TEMPvector.end(), thingToRemove);
	if (it != TEMPvector.end()){
		TEMPvector.erase(it);
		return TEMPvector;
	}
	return TEMPvector;
}
std::vector<std::string> DefaultCategories(){
	std::string d1="Utility";
	std::string d2="Game";
	std::string d3="Graphics";
	std::string d4="AudioVideo";
	std::string a1="Development";
	std::string d5="Settings";
	std::string d6="System";
	std::string d7="Other";
	std::string d8="Education";
	std::string a8="Network";
	std::string aa8="Science";
	std::string aaa8="Office";
	std::string a2="Accessibility";
	std::vector<std::string> DEFAULT;
	DEFAULT.push_back(d1);
	DEFAULT.push_back(d2);
	DEFAULT.push_back(d8);
	DEFAULT.push_back(a1);
	DEFAULT.push_back(d3);
	DEFAULT.push_back(d4);
	DEFAULT.push_back(d5);
	DEFAULT.push_back(d6);
	DEFAULT.push_back(d7);
	DEFAULT.push_back(a8);
	DEFAULT.push_back(aa8);
	DEFAULT.push_back(aaa8);
	DEFAULT.push_back(a2);
	return DEFAULT;
}
std::vector<std::string> xdgmenufiles(){
		//$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu
	std::string dirToOpen = "/etc/xdg/menus/";
	std::string pathToGet = "/menus/";
	std::string testPATH,tester,result,fullpath;
	std::vector<std::string> MENUS;
    std::vector<std::string> uniqueDirs = linuxcommon::split_paths("XDG_CONFIG_DIRS","/etc/xdg:/etc/xdg/xdg-torios");
	unsigned int found = 0;
    //initialize directory reading variables to NULL
    DIR *dir = NULL;
	struct dirent *entryPointer = NULL;
   	for( std::vector<std::string>::iterator it = uniqueDirs.begin();
			it!=uniqueDirs.end();
			++it){
        //get the string equvalent for the integer representation if of the xdg path
        // something like
        /*
        1=/usr/share/
        2=/usr/local/share
        etc...
        */
		testPATH = *it;
		//make sure this isn't blank
		if(testPATH.compare("")!=0){
			//look to see if we have a / at the end... remove it if we do...
			found = testPATH.rfind('/');
			if (found == testPATH.length()-1){testPATH=testPATH.substr(0,testPATH.length()-1);}
			//see if there is a pathToGet directory
			dirToOpen = testPATH + pathToGet;
			dir = opendir(dirToOpen.c_str());
			if(dir!=NULL){
		    while ((entryPointer=readdir(dir)) != NULL){
				if ((entryPointer->d_type == DT_REG)&&(entryPointer->d_name[0] != '.')){
					fullpath = entryPointer->d_name;
					result=dirToOpen+fullpath;
					tester=dirToOpen+fullpath;
					unsigned period = result.rfind(".");
					bool isMENU = false;
					if(result.compare("")!=0){
						if(period<result.length()){
							std::string testmenu = result.substr(period+1,std::string::npos);
							if(testmenu.compare("menu")==0){isMENU = true;}
						}
						if(isMENU){
							MENUS.push_back(result); 
						} //isMENU
					}//result !empty
				}//it is a file
			}//readdir is not NULL
			}//dir!=NULL
			closedir(dir);
		}//testPath is not ""
	}//directory looper
	
	return MENUS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////String LIST
std::list <std::string> get_desktop_files(){
	std::list<std::string> DESKTOPS;
	DIR *dir = NULL;
	struct dirent *entryPointer = NULL;
	std::string dirToOpen,fullpath,result,tester;
	//TODO loop in ALL directories!!
	for( std::vector<std::string>::iterator it=DESKTOP_DIRS.begin();it!=DESKTOP_DIRS.end();++it){
		dirToOpen=*it;
		dirToOpen+="applications/";
		//outXML("Dir To Open:"+dirToOpen);
		dir = opendir(dirToOpen.c_str());
		if (dir!=NULL){
			while ((entryPointer=readdir(dir)) != NULL){
				if ((entryPointer->d_type == DT_REG)&&(entryPointer->d_name[0] != '.')){
					fullpath = entryPointer->d_name;
					result=dirToOpen+fullpath;
					tester=dirToOpen+fullpath;
					unsigned period = result.rfind(".");
					bool isDESKTOP = false;
					if(result.compare("")!=0){
						if(period<result.length()){
							std::string testDesktop = result.substr(period+1,std::string::npos);
							if(testDesktop.compare("desktop")==0){isDESKTOP = true;}
						}
					//make a line if it is a desktop
						if(isDESKTOP){
							if( (no_display(result)) || !(only_show(result)) || (not_showin(result)) ){
                        outXML("DON'T ADD:"+result);
							}
							else{
								outXML("ADD:"+result);
								DESKTOPS.push_back(result);}
						} //isDesktop
					}//result !empty
				}//dirent is file
			}//dirent !NULL
		}//directory !NULL
	}
	closedir(dir);
	DESKTOPS.unique();
	DESKTOPS.sort(compare_nocase);
	return DESKTOPS;
}
