/** ******************************************************************************* * @file etk/os/FSNode.cpp * @brief Ewol Tool Kit : File System node access abstraction (Sources) * @author Edouard DUPIN * @date 31/10/2012 * @par Project * Ewol TK * * @par Copyright * Copyright 2011 Edouard DUPIN, all right reserved * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY. * * Licence summary : * You can modify and redistribute the sources code and binaries. * You can send me the bug-fix * * Term of the licence in in the file licence.txt. * ******************************************************************************* */ #include #include #include #include #include #include #include extern "C" { // file browsing ... #include #include } #ifdef __TARGET_OS__Android # include # include #endif #ifdef __TARGET_OS__Windows // For ctime #include #endif // zip file of the apk file for Android ==> set to zip file apk access static etk::UString s_fileAPK = ""; static etk::UString baseApplName = "ewolNoName"; #if defined(__TARGET_OS__Android) static etk::UString baseFolderHome = "/sdcard/"; // home folder static etk::UString baseFolderData = "assets/"; // program Data static etk::UString baseFolderDataUser = "/sdcard/.tmp/userData/"; // Data specific user (local modification) static etk::UString baseFolderCache = "/sdcard/.tmp/cache/"; // Temporary data (can be removed the next time) #elif defined(__TARGET_OS__Windows) static etk::UString baseFolderHome = "c:/test"; // home folder static etk::UString baseFolderData = "c:/test/share/"; // program Data static etk::UString baseFolderDataUser = "c:/test/userData/"; // Data specific user (local modification) static etk::UString baseFolderCache = "c:/Windows/Temp/ewol/"; // Temporary data (can be removed the next time) #else static etk::UString baseFolderHome = "~"; // home folder static etk::UString baseFolderData = "share/"; // program Data static etk::UString baseFolderDataUser = "~/.tmp/userData/"; // Data specific user (local modification) static etk::UString baseFolderCache = "~/.tmp/cache/"; // Temporary data (can be removed the next time) #endif #ifdef __TARGET_OS__Android static struct zip * s_APKArchive = NULL; static int32_t s_APKnbFiles = 0; static void loadAPK(etk::UString& apkPath) { TK_DEBUG("Loading APK \"" << apkPath << "\""); s_APKArchive = zip_open(apkPath.c_str(), 0, NULL); TK_ASSERT(s_APKArchive != NULL, "Error loading APK ... \"" << apkPath << "\""); //Just for debug, print APK contents s_APKnbFiles = zip_get_num_files(s_APKArchive); TK_INFO("List all files in the APK : " << s_APKnbFiles << " files"); for (int iii=0; iii5000) { TK_CRITICAL("ERROR when getting the small path ... this is loop prevention..."); break; } } /* #ifndef __TARGET_OS__Windows // for the target that supported the Realpath system : char buf[MAX_FILE_NAME]; memset(buf, 0, MAX_FILE_NAME); char * ok = realpath(input.c_str(), buf); if (!ok) { TK_ERROR("Error to get the real path"); input = "/"; } else { input = buf; } #endif */ //TK_DEBUG(" ==> \"" << input << "\""); return input; } void etk::FSNode::SortElementList(etk::Vector &list) { etk::Vector tmpList = list; list.Clear(); for(int32_t iii=0; iiiGetNameFile() > list[jjj]->GetNameFile()) { findPos = jjj+1; } } } //EWOL_DEBUG("position="< go to the root Folder destFilename = "ROOT:"; } else { destFilename = newName; } bool isRoofFolder = false; #ifdef __TARGET_OS__Windows for (char iii='a' ; iii<='z' ; iii++) { char tmpVal[10]; char tmpValMaj[10]; sprintf(tmpVal, "%c:/", iii); sprintf(tmpValMaj, "%c:/", iii+'A'-'a'); if( true == destFilename.StartWith(tmpVal) || true == destFilename.StartWith(tmpValMaj)) { isRoofFolder = true; break; } } #else isRoofFolder = destFilename.StartWith('/'); #endif if (true == destFilename.StartWith(baseFolderHome) ) { destFilename.Remove(0, baseFolderHome.Size()); m_type = etk::FSN_TYPE_HOME; } else if(true == isRoofFolder) { #ifdef __TARGET_OS__Windows destFilename.Remove(0, 3); #else destFilename.Remove(0, 1); #endif m_type = etk::FSN_TYPE_DIRECT; } else if( true == destFilename.StartWith("ROOT:") || true == destFilename.StartWith("root:") ) { destFilename.Remove(0, 5); m_type = etk::FSN_TYPE_DIRECT; } else if( true == destFilename.StartWith("DIRECT:") || true == destFilename.StartWith("direct:") ) { destFilename.Remove(0, 7); m_type = etk::FSN_TYPE_DIRECT; } else if( true == destFilename.StartWith("DATA:") || true == destFilename.StartWith("data:") ) { destFilename.Remove(0, 5); m_type = etk::FSN_TYPE_DATA; } else if( true == destFilename.StartWith("USERDATA:") || true == destFilename.StartWith("userdata:") ) { destFilename.Remove(0, 9); m_type = etk::FSN_TYPE_USER_DATA; } else if( true == destFilename.StartWith("CACHE:") || true == destFilename.StartWith("cache:") ) { destFilename.Remove(0, 6); m_type = etk::FSN_TYPE_CACHE; } else if( true == destFilename.StartWith("THEME:") || true == destFilename.StartWith("theme:") ) { destFilename.Remove(0, 6); m_type = etk::FSN_TYPE_THEME; } else if(true == destFilename.StartWith("~")) { destFilename.Remove(0, 1); m_type = etk::FSN_TYPE_HOME; } else if( true == destFilename.StartWith("HOME:") || true == destFilename.StartWith("home:") ) { destFilename.Remove(0, 5); m_type = etk::FSN_TYPE_HOME; } else { // nothing to remove //Other type is Relative : m_type = etk::FSN_TYPE_RELATIF; } m_userFileName = destFilename; TK_DBG_MODE("3 : parse done : [" << m_type << "]->\"" << m_userFileName << "\""); // Now we reduce the path with all un-needed ../ and other thinks ... // TODO : Do it whith link and the other sub thinks ... m_userFileName = SimplifyPathAbstractPath(m_userFileName); TK_DBG_MODE("4 : Path simplification : [" << m_type << "]->\"" << m_userFileName << "\""); // Now we generate the real FS path: GenerateFileSystemPath(); TK_DBG_MODE("5 : file System Real name : \"" << m_systemFileName << "\""); // now we get all the right if the file existed: UpdateFileSystemProperty(); TK_DBG_MODE("6 : type : [" << m_typeNode << "] right :" << m_rights); } bool DirectCheckFile(etk::UString tmpFileNameDirect, bool checkInAPKIfNeeded=false) { #ifdef __TARGET_OS__Android if (true == checkInAPKIfNeeded) { for (int iii=0; iii \"" << basicName << "\""); themeName = etk::theme::GetName(themeName); TK_DBG_MODE(" ==> theme Folder \"" << themeName << "\""); // search the corect folder : if (themeName == "") { TK_WARNING("no theme name detected : set it to \"default\""); } else if (themeName != "default") { // Selected theme : // check in the user data : m_systemFileName = baseFolderDataUser + "theme/" + themeName + "/" + basicName; if (true==DirectCheckFile(m_systemFileName)) { return; } // check in the Appl data : m_systemFileName = baseFolderData + "theme/" + themeName + "/" + basicName; if (true==DirectCheckFile(m_systemFileName, true)) { m_type = etk::FSN_TYPE_THEME_DATA; return; } } themeName = "default"; // default theme : // check in the user data : m_systemFileName = baseFolderDataUser + "theme/" + themeName + "/" + basicName; if (true==DirectCheckFile(m_systemFileName)) { return; } // check in the Appl data : In every case we return this one ... m_systemFileName = baseFolderData + "theme/" + themeName + "/" + basicName; m_type = etk::FSN_TYPE_THEME_DATA; return; } break; } m_systemFileName += m_userFileName; } // now we get all the right if the file existed: void etk::FSNode::UpdateFileSystemProperty(void) { // clean general properties : m_rights.Clear(); m_timeCreate = 0; m_timeModify = 0; m_timeAccess = 0; m_idOwner = 0; m_idGroup = 0; // File type is not knowns ... m_typeNode=FSN_UNKNOW; #ifdef __TARGET_OS__Android if( m_type == etk::FSN_TYPE_DATA || m_type == etk::FSN_TYPE_THEME_DATA) { for (int iii=0; iii= s_APKnbFiles) { TK_ERROR("File Does not existed ... in APK : \"" << m_systemFileName << "\""); return; } // note : Zip does not support other think than file ... // TODO : Suport folder parsing ... m_typeNode=FSN_FILE; m_rights.SetUserReadable(true); // TODO : Set the time of the file (time program compilation) // TODO : Set the USER ID in the group and the user Id ... TK_DBG_MODE("File existed ... in APK : \"" << m_systemFileName << "\" ==> id=" << m_idZipFile); return; } #endif // tmpStat Buffer : struct stat statProperty; if (-1 == stat(m_systemFileName.c_str(), &statProperty)) { //Normal case when the file does not exist ... ==> the it was in unknow mode ... return; } switch (statProperty.st_mode & S_IFMT) { case S_IFBLK: m_typeNode=etk::FSN_BLOCK; break; case S_IFCHR: m_typeNode=etk::FSN_CHARACTER; break; case S_IFDIR: m_typeNode=etk::FSN_FOLDER; break; case S_IFIFO: m_typeNode=etk::FSN_FIFO; break; #ifndef __TARGET_OS__Windows case S_IFLNK: m_typeNode=etk::FSN_LINK; break; #endif case S_IFREG: m_typeNode=etk::FSN_FILE; break; #ifndef __TARGET_OS__Windows case S_IFSOCK: m_typeNode=etk::FSN_SOCKET; break; #endif default: m_typeNode=etk::FSN_UNKNOW; break; } // Right m_rights = statProperty.st_mode; m_idOwner = (int32_t) statProperty.st_uid; m_idGroup = (int32_t) statProperty.st_gid; m_timeCreate = statProperty.st_ctime; m_timeModify = statProperty.st_mtime; m_timeAccess = statProperty.st_atime; return; } /* All Right of the file */ bool etk::FSNode::SetRight(etk::FSNodeRight newRight) { // TODO : ... TK_ERROR("Can not set the new rights ..."); return false; } /* Common API : */ void etk::FSNode::SetName(etk::UString newName) { PrivateSetName(newName); } etk::UString etk::FSNode::GetNameFolder(void) const { int32_t lastPos = m_systemFileName.FindBack('/'); if (-1 != lastPos) { return m_systemFileName.Extract(0, lastPos); } return ""; } etk::UString etk::FSNode::GetName(void) const { etk::UString output; switch (m_type) { default: case etk::FSN_TYPE_UNKNOW: output = "HOME:"; break; case etk::FSN_TYPE_DIRECT: output = "/"; break; case etk::FSN_TYPE_RELATIF: output = ""; break; case etk::FSN_TYPE_HOME: output = "~"; break; case etk::FSN_TYPE_DATA: output = "DATA:"; break; case etk::FSN_TYPE_USER_DATA: output = "USERDATA"; break; case etk::FSN_TYPE_CACHE: output = "CACHE"; break; case etk::FSN_TYPE_THEME: case etk::FSN_TYPE_THEME_DATA: output = "THEME:"; break; } output += m_userFileName; return output; } etk::UString etk::FSNode::GetNameFile(void) const { int32_t lastPos = m_systemFileName.FindBack('/'); if (-1 != lastPos) { return m_systemFileName.Extract(lastPos+1); } return ""; } etk::UString etk::FSNode::GetRelativeFolder(void) const { etk::UString tmppp = GetName(); int32_t lastPos = tmppp.FindBack('/'); if (-1 != lastPos) { return tmppp.Extract(0, lastPos+1); } lastPos = tmppp.FindBack(':'); if (-1 != lastPos) { return tmppp.Extract(0, lastPos+1); } return ""; } bool etk::FSNode::Touch(void) { return false; } bool etk::FSNode::Remove(void) { return false; } uint64_t etk::FSNode::TimeCreated(void) const { return m_timeCreate; } etk::UString etk::FSNode::TimeCreatedString(void) const { time_t tmpVal = (int32_t)m_timeCreate; return ctime(&tmpVal); } uint64_t etk::FSNode::TimeModified(void) const { return m_timeModify; } etk::UString etk::FSNode::TimeModifiedString(void) const { time_t tmpVal = (int32_t)m_timeModify; return ctime(&tmpVal); } uint64_t etk::FSNode::TimeAccessed(void) const { return m_timeAccess; } etk::UString etk::FSNode::TimeAccessedString(void) const { time_t tmpVal = (int32_t)m_timeAccess; return ctime(&tmpVal); } /* Operator : */ const etk::FSNode& etk::FSNode::operator= (const etk::FSNode &obj ) { if( this != &obj ) // avoid copy to itself { if( NULL != m_PointerFile #ifdef __TARGET_OS__Android || NULL != m_zipData #endif ) { TK_ERROR("Missing close the file : " << *this); FileClose(); m_PointerFile = NULL; } #ifdef __TARGET_OS__Android m_idZipFile = obj.m_idZipFile; m_zipData = NULL; m_zipDataSize = 0; m_zipReadingOffset = 0; #endif etk::UString tmppp = obj.GetName(); PrivateSetName(tmppp); } return *this; } bool etk::FSNode::operator== (const etk::FSNode &obj ) const { if( this != &obj ) { if( obj.m_userFileName == m_userFileName && obj.m_systemFileName == m_systemFileName && obj.m_type == m_type ) { return true; } else { return false; } return true; } return true; } bool etk::FSNode::operator!= (const etk::FSNode &obj ) const { return !(*this == obj); } etk::CCout& etk::operator <<(etk::CCout &os, const etk::FSNode &obj) { os << "[" << obj.m_type << "]->\"" << obj.m_userFileName << "\""; return os; } etk::CCout& etk::operator <<(etk::CCout &os, const etk::FSNType_te &obj) { switch (obj) { case etk::FSN_TYPE_UNKNOW: os << "FSN_TYPE_UNKNOW"; break; case etk::FSN_TYPE_DIRECT: os << "FSN_TYPE_DIRECT"; break; case etk::FSN_TYPE_RELATIF: os << "FSN_TYPE_RELATIF"; break; case etk::FSN_TYPE_HOME: os << "FSN_TYPE_HOME"; break; case etk::FSN_TYPE_DATA: os << "FSN_TYPE_DATA"; break; case etk::FSN_TYPE_USER_DATA: os << "FSN_TYPE_USER_DATA"; break; case etk::FSN_TYPE_CACHE: os << "FSN_TYPE_CACHE"; break; case etk::FSN_TYPE_THEME: os << "FSN_TYPE_THEME"; break; case etk::FSN_TYPE_THEME_DATA: os << "FSN_TYPE_THEME(DATA)"; break; default: os << "FSN_TYPE_????"; break; } return os; } etk::CCout& etk::operator <<(etk::CCout &os, const etk::typeNode_te &obj) { switch (obj) { case etk::FSN_UNKNOW: os << "FSN_UNKNOW"; break; case etk::FSN_BLOCK: os << "FSN_BLOCK"; break; case etk::FSN_CHARACTER: os << "FSN_CHARACTER"; break; case etk::FSN_FOLDER: os << "FSN_FOLDER"; break; case etk::FSN_FIFO: os << "FSN_FIFO"; break; case etk::FSN_LINK: os << "FSN_LINK"; break; case etk::FSN_FILE: os << "FSN_FILE"; break; case etk::FSN_SOCKET: os << "FSN_SOCKET"; break; default: os << "FSN_????"; break; } return os; } /* Folder specific : */ int32_t etk::FSNode::FolderCount(void) { return 0; } etk::Vector etk::FSNode::FolderGetSubList(bool showHidenFile, bool getFolderAndOther, bool getFile, bool temporaryFile) { etk::Vector tmpp; if (m_typeNode != etk::FSN_FOLDER ) { return tmpp; } // regenerate the next list : etk::FSNode * tmpEmement; DIR *dir = NULL; struct dirent *ent = NULL; dir = opendir(m_systemFileName.c_str()); if (dir != NULL) { // for each element in the drectory... while ((ent = readdir(dir)) != NULL) { etk::UString tmpName(ent->d_name); TK_VERBOSE(" search in folder\"" << tmpName << "\""); if( tmpName=="." || tmpName==".." ) { // do nothing ... continue; } if( false == tmpName.StartWith(".") || true == showHidenFile) { tmpEmement = new etk::FSNode(GetRelativeFolder()+tmpName); if (NULL == tmpEmement) { TK_ERROR("allocation error ... of ewol::FSNode"); continue; } if(tmpEmement->GetNodeType() == etk::FSN_FILE) { if (true == getFile) { tmpp.PushBack(tmpEmement); } else { delete(tmpEmement); tmpEmement = NULL; } } else if (getFolderAndOther) { tmpp.PushBack(tmpEmement); } else { delete(tmpEmement); tmpEmement = NULL; } } } closedir(dir); } else { TK_ERROR("could not open directory : \"" << *this << "\""); } // reorder the files SortElementList(tmpp); return tmpp; } etk::FSNode etk::FSNode::FolderGetParent(void) { etk::FSNode tmpp; return tmpp; } /* File Specific : */ bool etk::FSNode::FileHasExtention(void) { int32_t lastPos = m_userFileName.FindBack('.'); if( -1 != lastPos // not find the . && 0 != lastPos // Find a . at the fist position .jdlskjdfklj ==> hiden file && m_userFileName.Size() != lastPos ) // Remove file ended with . { return true; } else { return false; } } etk::UString etk::FSNode::FileGetExtention(void) { etk::UString tmpExt = ""; int32_t lastPos = m_userFileName.FindBack('.'); if( -1 != lastPos // not find the . && 0 != lastPos // Find a . at the fist position .jdlskjdfklj ==> hiden file && m_userFileName.Size() != lastPos ) // Remove file ended with . { // Get the FileName tmpExt = m_userFileName.Extract(lastPos+1); } return tmpExt; } uint64_t etk::FSNode::FileSize(void) { if (etk::FSN_FILE != m_typeNode) { TK_ERROR("pppppppppppppppppppppp" << m_typeNode); return 0; } #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { if (true == LoadDataZip()) { return m_zipDataSize; } return 0; } #endif // Note : this is a proper methode to get the file size for Big files ... otherwithe the size is limited at 2^31 bytes // tmpStat Buffer : struct stat statProperty; if (-1 == stat(m_systemFileName.c_str(), &statProperty)) { //Normal case when the file does not exist ... ==> the it was in unknow mode ... TK_ERROR("mlkmlkmlkmlkmlkmlk"); return 0; } TK_INFO(" file size : " << (int64_t)statProperty.st_size << " bytes"); if ((uint64_t)statProperty.st_size<=0) { return 0; } return (uint64_t)statProperty.st_size; } bool etk::FSNode::FileOpenRead(void) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { return LoadDataZip(); } #endif if (NULL != m_PointerFile) { TK_CRITICAL("File Already open : " << *this); return true; } m_PointerFile=fopen(m_systemFileName.c_str(),"rb"); if(NULL == m_PointerFile) { TK_ERROR("Can not find the file " << *this ); return false; } return true; } bool etk::FSNode::FileOpenWrite(void) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { return false; } #endif if (NULL != m_PointerFile) { TK_CRITICAL("File Already open : " << *this); return true; } m_PointerFile=fopen(m_systemFileName.c_str(),"wb"); if(NULL == m_PointerFile) { TK_ERROR("Can not find the file " << *this); return false; } return true; } bool etk::FSNode::FileClose(void) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { if (NULL == m_zipData) { TK_CRITICAL("File Already closed : " << *this); return false; } delete[] m_zipData; m_zipData = NULL; m_zipDataSize = 0; m_zipReadingOffset = 0; return true; } #endif if (NULL == m_PointerFile) { TK_CRITICAL("File Already closed : " << *this); return false; } fclose(m_PointerFile); m_PointerFile = NULL; return true; } char* etk::FSNode::FileGets(char * elementLine, int32_t maxData) { memset(elementLine, 0, maxData); #ifdef __TARGET_OS__Android char * element = elementLine; int32_t outSize = 0; if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) {//char * tmpData = internalDataFiles[iii].data + m_readingOffset; if (NULL == m_zipData) { element[0] = '\0'; return NULL; } if (m_zipReadingOffset>m_zipDataSize) { element[0] = '\0'; return NULL; } while (m_zipData[m_zipReadingOffset] != '\0') { if( m_zipData[m_zipReadingOffset] == '\n' || m_zipData[m_zipReadingOffset] == '\r') { *element = m_zipData[m_zipReadingOffset]; element++; m_zipReadingOffset++; *element = '\0'; return elementLine; } *element = m_zipData[m_zipReadingOffset]; element++; m_zipReadingOffset++; if (m_zipReadingOffset>m_zipDataSize) { *element = '\0'; return elementLine; } // check maxData Size ... if (outSize>=maxData-1) { *element = '\0'; return elementLine; } outSize++; } if (outSize==0) { return NULL; } else { // send last line return elementLine; } } #endif return fgets(elementLine, maxData, m_PointerFile); } int32_t etk::FSNode::FileRead(void * data, int32_t blockSize, int32_t nbBlock) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { if (NULL == m_zipData) { ((char*)data)[0] = '\0'; return 0; } int32_t dataToRead = blockSize * nbBlock; if (dataToRead + m_zipReadingOffset > m_zipDataSize) { nbBlock = ((m_zipDataSize - m_zipReadingOffset) / blockSize); dataToRead = blockSize * nbBlock; } memcpy(data, &m_zipData[m_zipReadingOffset], dataToRead); m_zipReadingOffset += dataToRead; return nbBlock; } #endif return fread(data, blockSize, nbBlock, m_PointerFile); } int32_t etk::FSNode::FileWrite(void * data, int32_t blockSize, int32_t nbBlock) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { TK_CRITICAL("Can not write on data inside APK : " << *this); return 0; } #endif return fwrite(data, blockSize, nbBlock, m_PointerFile); } bool etk::FSNode::FileSeek(long int offset, int origin) { #ifdef __TARGET_OS__Android if( etk::FSN_TYPE_DATA == m_type || etk::FSN_TYPE_THEME_DATA == m_type) { if (NULL == m_zipData) { return false; } int32_t positionEnd = 0; switch(origin) { case SEEK_END: positionEnd = m_zipDataSize; break; case SEEK_CUR: positionEnd = m_zipReadingOffset; break; default: positionEnd = 0; break; } positionEnd += offset; if (positionEnd < 0) { positionEnd = 0; } else if (positionEnd > m_zipDataSize) { positionEnd = m_zipDataSize; } m_zipReadingOffset = positionEnd; return true; } #endif fseek(m_PointerFile, offset, origin); if(ferror(m_PointerFile)) { return false; } else { return true; } } // TODO : Add an INIT to reset all allocated parameter : class tmpThemeElement { public: etk::UString refName; etk::UString folderName; }; static etk::Vector g_listTheme; // set the Folder of a subset of a theme ... void etk::theme::SetName(etk::UString refName, etk::UString folderName) { for(int32_t iii=0; iiirefName==refName) { g_listTheme[iii]->folderName = folderName; // action done return; } } } // we did not find it ... tmpThemeElement* tmpp = new tmpThemeElement(); if (NULL==tmpp) { TK_ERROR("pb to add a reference theme"); return; } tmpp->refName = refName; tmpp->folderName = folderName; g_listTheme.PushBack(tmpp); } // get the folder from a Reference theme etk::UString etk::theme::GetName(etk::UString refName) { for(int32_t iii=0; iiirefName==refName) { return g_listTheme[iii]->folderName; } } } // We did not find the theme return refName; } // get the list of all the theme folder availlable in the user Home/appl etk::Vector etk::theme::List(void) { etk::Vector tmpp; return tmpp; // TODO : }