/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2000-2009 Kitware, Inc., Insight Software Consortium Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ #include "cmListCommand.h" #include <cmsys/RegularExpression.hxx> #include <cmsys/SystemTools.hxx> #include <stdlib.h> // required for atoi #include <ctype.h> //---------------------------------------------------------------------------- bool cmListCommand ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &) { if(args.size() < 1) { this->SetError("must be called with at least one argument."); return false; } const std::string &subCommand = args[0]; if(subCommand == "LENGTH") { return this->HandleLengthCommand(args); } if(subCommand == "GET") { return this->HandleGetCommand(args); } if(subCommand == "APPEND") { return this->HandleAppendCommand(args); } if(subCommand == "FIND") { return this->HandleFindCommand(args); } if(subCommand == "INSERT") { return this->HandleInsertCommand(args); } if(subCommand == "REMOVE_AT") { return this->HandleRemoveAtCommand(args); } if(subCommand == "REMOVE_ITEM") { return this->HandleRemoveItemCommand(args); } if(subCommand == "REMOVE_DUPLICATES") { return this->HandleRemoveDuplicatesCommand(args); } if(subCommand == "SORT") { return this->HandleSortCommand(args); } if(subCommand == "REVERSE") { return this->HandleReverseCommand(args); } std::string e = "does not recognize sub-command "+subCommand; this->SetError(e.c_str()); return false; } //---------------------------------------------------------------------------- bool cmListCommand::GetListString(std::string& listString, const char* var) { if ( !var ) { return false; } // get the old value const char* cacheValue = this->Makefile->GetDefinition(var); if(!cacheValue) { return false; } listString = cacheValue; return true; } //---------------------------------------------------------------------------- bool cmListCommand::GetList(std::vector<std::string>& list, const char* var) { std::string listString; if ( !this->GetListString(listString, var) ) { return false; } // if the size of the list if(listString.size() == 0) { return true; } // expand the variable into a list cmSystemTools::ExpandListArgument(listString, list, true); // check the list for empty values bool hasEmpty = false; for(std::vector<std::string>::iterator i = list.begin(); i != list.end(); ++i) { if(i->size() == 0) { hasEmpty = true; break; } } // if no empty elements then just return if(!hasEmpty) { return true; } // if we have empty elements we need to check policy CMP0007 switch(this->Makefile->GetPolicyStatus(cmPolicies::CMP0007)) { case cmPolicies::WARN: { // Default is to warn and use old behavior // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values list.clear(); cmSystemTools::ExpandListArgument(listString, list); std::string warn = this->Makefile->GetPolicies()-> GetPolicyWarning(cmPolicies::CMP0007); warn += " List has value = ["; warn += listString; warn += "]."; this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, warn); return true; } case cmPolicies::OLD: // OLD behavior is to allow compatibility, so recall // ExpandListArgument without the true which will remove // empty values list.clear(); cmSystemTools::ExpandListArgument(listString, list); return true; case cmPolicies::NEW: return true; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: this->Makefile->IssueMessage( cmake::FATAL_ERROR, this->Makefile->GetPolicies() ->GetRequiredPolicyError(cmPolicies::CMP0007) ); return false; } return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleLengthCommand(std::vector<std::string> const& args) { if(args.size() != 3) { this->SetError("sub-command LENGTH requires two arguments."); return false; } const std::string& listName = args[1]; const std::string& variableName = args[args.size() - 1]; std::vector<std::string> varArgsExpanded; // do not check the return value here // if the list var is not found varArgsExpanded will have size 0 // and we will return 0 this->GetList(varArgsExpanded, listName.c_str()); size_t length = varArgsExpanded.size(); char buffer[1024]; sprintf(buffer, "%d", static_cast<int>(length)); this->Makefile->AddDefinition(variableName.c_str(), buffer); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleGetCommand(std::vector<std::string> const& args) { if(args.size() < 4) { this->SetError("sub-command GET requires at least three arguments."); return false; } const std::string& listName = args[1]; const std::string& variableName = args[args.size() - 1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->Makefile->AddDefinition(variableName.c_str(), "NOTFOUND"); return true; } std::string value; size_t cc; const char* sep = ""; for ( cc = 2; cc < args.size()-1; cc ++ ) { int item = atoi(args[cc].c_str()); value += sep; sep = ";"; size_t nitem = varArgsExpanded.size(); if ( item < 0 ) { item = (int)nitem + item; } if ( item < 0 || nitem <= (size_t)item ) { cmOStringStream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; this->SetError(str.str().c_str()); return false; } value += varArgsExpanded[item]; } this->Makefile->AddDefinition(variableName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleAppendCommand(std::vector<std::string> const& args) { if(args.size() < 2) { this->SetError("sub-command APPEND requires at least one argument."); return false; } // Skip if nothing to append. if(args.size() < 3) { return true; } const std::string& listName = args[1]; // expand the variable std::string listString; this->GetListString(listString, listName.c_str()); size_t cc; for ( cc = 2; cc < args.size(); ++ cc ) { if(listString.size()) { listString += ";"; } listString += args[cc]; } this->Makefile->AddDefinition(listName.c_str(), listString.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args) { if(args.size() != 4) { this->SetError("sub-command FIND requires three arguments."); return false; } const std::string& listName = args[1]; const std::string& variableName = args[args.size() - 1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->Makefile->AddDefinition(variableName.c_str(), "-1"); return true; } std::vector<std::string>::iterator it; unsigned int index = 0; for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it ) { if ( *it == args[2] ) { char indexString[32]; sprintf(indexString, "%d", index); this->Makefile->AddDefinition(variableName.c_str(), indexString); return true; } index++; } this->Makefile->AddDefinition(variableName.c_str(), "-1"); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args) { if(args.size() < 4) { this->SetError("sub-command INSERT requires at least three arguments."); return false; } const std::string& listName = args[1]; // expand the variable int item = atoi(args[2].c_str()); std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) && item != 0) { cmOStringStream str; str << "index: " << item << " out of range (0, 0)"; this->SetError(str.str().c_str()); return false; } if ( varArgsExpanded.size() != 0 ) { size_t nitem = varArgsExpanded.size(); if ( item < 0 ) { item = (int)nitem + item; } if ( item < 0 || nitem <= (size_t)item ) { cmOStringStream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << (varArgsExpanded.size() == 0?0:(varArgsExpanded.size()-1)) << ")"; this->SetError(str.str().c_str()); return false; } } size_t cc; size_t cnt = 0; for ( cc = 3; cc < args.size(); ++ cc ) { varArgsExpanded.insert(varArgsExpanded.begin()+item+cnt, args[cc]); cnt ++; } std::string value; const char* sep = ""; for ( cc = 0; cc < varArgsExpanded.size(); cc ++ ) { value += sep; value += varArgsExpanded[cc]; sep = ";"; } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleRemoveItemCommand(std::vector<std::string> const& args) { if(args.size() < 3) { this->SetError("sub-command REMOVE_ITEM requires two or more arguments."); return false; } const std::string& listName = args[1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->SetError("sub-command REMOVE_ITEM requires list to be present."); return false; } size_t cc; for ( cc = 2; cc < args.size(); ++ cc ) { size_t kk = 0; while ( kk < varArgsExpanded.size() ) { if ( varArgsExpanded[kk] == args[cc] ) { varArgsExpanded.erase(varArgsExpanded.begin()+kk); } else { kk ++; } } } std::string value; const char* sep = ""; for ( cc = 0; cc < varArgsExpanded.size(); cc ++ ) { value += sep; value += varArgsExpanded[cc]; sep = ";"; } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleReverseCommand(std::vector<std::string> const& args) { if(args.size() < 2) { this->SetError("sub-command REVERSE requires a list as an argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->SetError("sub-command REVERSE requires list to be present."); return false; } std::string value; std::vector<std::string>::reverse_iterator it; const char* sep = ""; for ( it = varArgsExpanded.rbegin(); it != varArgsExpanded.rend(); ++ it ) { value += sep; value += it->c_str(); sep = ";"; } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleRemoveDuplicatesCommand(std::vector<std::string> const& args) { if(args.size() < 2) { this->SetError( "sub-command REMOVE_DUPLICATES requires a list as an argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->SetError( "sub-command REMOVE_DUPLICATES requires list to be present."); return false; } std::string value; std::set<std::string> unique; std::vector<std::string>::iterator it; const char* sep = ""; for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it ) { if (unique.find(*it) != unique.end()) { continue; } unique.insert(*it); value += sep; value += it->c_str(); sep = ";"; } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleSortCommand(std::vector<std::string> const& args) { if(args.size() < 2) { this->SetError("sub-command SORT requires a list as an argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->SetError("sub-command SORT requires list to be present."); return false; } std::sort(varArgsExpanded.begin(), varArgsExpanded.end()); std::string value; std::vector<std::string>::iterator it; const char* sep = ""; for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it ) { value += sep; value += it->c_str(); sep = ";"; } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleRemoveAtCommand( std::vector<std::string> const& args) { if(args.size() < 3) { this->SetError("sub-command REMOVE_AT requires at least " "two arguments."); return false; } const std::string& listName = args[1]; // expand the variable std::vector<std::string> varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName.c_str()) ) { this->SetError("sub-command REMOVE_AT requires list to be present."); return false; } size_t cc; std::vector<size_t> removed; for ( cc = 2; cc < args.size(); ++ cc ) { int item = atoi(args[cc].c_str()); size_t nitem = varArgsExpanded.size(); if ( item < 0 ) { item = (int)nitem + item; } if ( item < 0 || nitem <= (size_t)item ) { cmOStringStream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; this->SetError(str.str().c_str()); return false; } removed.push_back(static_cast<size_t>(item)); } std::string value; const char* sep = ""; for ( cc = 0; cc < varArgsExpanded.size(); ++ cc ) { size_t kk; bool found = false; for ( kk = 0; kk < removed.size(); ++ kk ) { if ( cc == removed[kk] ) { found = true; } } if ( !found ) { value += sep; value += varArgsExpanded[cc]; sep = ";"; } } this->Makefile->AddDefinition(listName.c_str(), value.c_str()); return true; }