/*============================================================================ 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 #include #include // required for atoi #include #include //---------------------------------------------------------------------------- bool cmListCommand ::InitialPass(std::vector const& args, cmExecutionStatus &) { if(args.size() < 2) { this->SetError("must be called with at least two arguments."); 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); return false; } //---------------------------------------------------------------------------- bool cmListCommand::GetListString(std::string& listString, const std::string& var) { // get the old value const char* cacheValue = this->Makefile->GetDefinition(var); if(!cacheValue) { return false; } listString = cacheValue; return true; } //---------------------------------------------------------------------------- bool cmListCommand::GetList(std::vector& list, const std::string& var) { std::string listString; if ( !this->GetListString(listString, var) ) { return false; } // if the size of the list if(listString.empty()) { 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::iterator i = list.begin(); i != list.end(); ++i) { if(i->empty()) { 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 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 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); size_t length = varArgsExpanded.size(); char buffer[1024]; sprintf(buffer, "%d", static_cast(length)); this->Makefile->AddDefinition(variableName, buffer); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleGetCommand(std::vector 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 varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->Makefile->AddDefinition(variableName, "NOTFOUND"); return true; } // FIXME: Add policy to make non-existing lists an error like empty lists. if(varArgsExpanded.empty()) { this->SetError("GET given empty list"); return false; } 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 ) { std::ostringstream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; this->SetError(str.str()); return false; } value += varArgsExpanded[item]; } this->Makefile->AddDefinition(variableName, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleAppendCommand(std::vector const& args) { assert(args.size() >= 2); // 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); if(!listString.empty() && !args.empty()) { listString += ";"; } listString += cmJoin(cmRange(args).advance(2), ";"); this->Makefile->AddDefinition(listName, listString.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleFindCommand(std::vector 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 varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->Makefile->AddDefinition(variableName, "-1"); return true; } std::vector::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, indexString); return true; } index++; } this->Makefile->AddDefinition(variableName, "-1"); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleInsertCommand(std::vector 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 varArgsExpanded; if((!this->GetList(varArgsExpanded, listName) || varArgsExpanded.empty()) && item != 0) { std::ostringstream str; str << "index: " << item << " out of range (0, 0)"; this->SetError(str.str()); return false; } if (!varArgsExpanded.empty()) { size_t nitem = varArgsExpanded.size(); if ( item < 0 ) { item = (int)nitem + item; } if ( item < 0 || nitem <= (size_t)item ) { std::ostringstream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << (varArgsExpanded.empty() ? 0 : (varArgsExpanded.size() - 1)) << ")"; this->SetError(str.str()); return false; } } varArgsExpanded.insert(varArgsExpanded.begin()+item, args.begin() + 3, args.end()); std::string value = cmJoin(varArgsExpanded, ";"); this->Makefile->AddDefinition(listName, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleRemoveItemCommand(std::vector 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 varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { 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 = cmJoin(varArgsExpanded, ";"); this->Makefile->AddDefinition(listName, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleReverseCommand(std::vector const& args) { assert(args.size() >= 2); if(args.size() > 2) { this->SetError( "sub-command REVERSE only takes one argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->SetError("sub-command REVERSE requires list to be present."); return false; } std::string value; std::vector::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, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleRemoveDuplicatesCommand(std::vector const& args) { assert(args.size() >= 2); if(args.size() > 2) { this->SetError( "sub-command REMOVE_DUPLICATES only takes one argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->SetError( "sub-command REMOVE_DUPLICATES requires list to be present."); return false; } std::string value; std::set unique; std::vector::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, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand ::HandleSortCommand(std::vector const& args) { assert(args.size() >= 2); if(args.size() > 2) { this->SetError( "sub-command SORT only takes one argument."); return false; } const std::string& listName = args[1]; // expand the variable std::vector varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->SetError("sub-command SORT requires list to be present."); return false; } std::sort(varArgsExpanded.begin(), varArgsExpanded.end()); std::string value = cmJoin(varArgsExpanded, ";"); this->Makefile->AddDefinition(listName, value.c_str()); return true; } //---------------------------------------------------------------------------- bool cmListCommand::HandleRemoveAtCommand( std::vector 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 varArgsExpanded; if ( !this->GetList(varArgsExpanded, listName) ) { this->SetError("sub-command REMOVE_AT requires list to be present."); return false; } // FIXME: Add policy to make non-existing lists an error like empty lists. if(varArgsExpanded.empty()) { this->SetError("REMOVE_AT given empty list"); return false; } size_t cc; std::vector 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 ) { std::ostringstream str; str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; this->SetError(str.str()); return false; } removed.push_back(static_cast(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, value.c_str()); return true; }