Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmListCommand.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "cmListCommand.h"
13 #include <cmsys/RegularExpression.hxx>
14 #include <cmsys/SystemTools.hxx>
15
16 #include <stdlib.h> // required for atoi
17 #include <ctype.h>
18 //----------------------------------------------------------------------------
19 bool cmListCommand
20 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
21 {
22   if(args.size() < 1)
23     {
24     this->SetError("must be called with at least one argument.");
25     return false;
26     }
27
28   const std::string &subCommand = args[0];
29   if(subCommand == "LENGTH")
30     {
31     return this->HandleLengthCommand(args);
32     }
33   if(subCommand == "GET")
34     {
35     return this->HandleGetCommand(args);
36     }
37   if(subCommand == "APPEND")
38     {
39     return this->HandleAppendCommand(args);
40     }
41   if(subCommand == "FIND")
42     {
43     return this->HandleFindCommand(args);
44     }
45   if(subCommand == "INSERT")
46     {
47     return this->HandleInsertCommand(args);
48     }
49   if(subCommand == "REMOVE_AT")
50     {
51     return this->HandleRemoveAtCommand(args);
52     }
53   if(subCommand == "REMOVE_ITEM")
54     {
55     return this->HandleRemoveItemCommand(args);
56     }
57   if(subCommand == "REMOVE_DUPLICATES")
58     {
59     return this->HandleRemoveDuplicatesCommand(args);
60     }
61   if(subCommand == "SORT")
62     {
63     return this->HandleSortCommand(args);
64     }
65   if(subCommand == "REVERSE")
66     {
67     return this->HandleReverseCommand(args);
68     }
69
70   std::string e = "does not recognize sub-command "+subCommand;
71   this->SetError(e.c_str());
72   return false;
73 }
74
75 //----------------------------------------------------------------------------
76 bool cmListCommand::GetListString(std::string& listString, const char* var)
77 {
78   if ( !var )
79     {
80     return false;
81     }
82   // get the old value
83   const char* cacheValue
84     = this->Makefile->GetDefinition(var);
85   if(!cacheValue)
86     {
87     return false;
88     }
89   listString = cacheValue;
90   return true;
91 }
92
93 //----------------------------------------------------------------------------
94 bool cmListCommand::GetList(std::vector<std::string>& list, const char* var)
95 {
96   std::string listString;
97   if ( !this->GetListString(listString, var) )
98     {
99     return false;
100     }
101   // if the size of the list 
102   if(listString.size() == 0)
103     {
104     return true;
105     }
106   // expand the variable into a list
107   cmSystemTools::ExpandListArgument(listString, list, true);
108   // check the list for empty values
109   bool hasEmpty = false;
110   for(std::vector<std::string>::iterator i = list.begin(); 
111       i != list.end(); ++i)
112     {
113     if(i->size() == 0)
114       {
115       hasEmpty = true;
116       break;
117       }
118     }
119   // if no empty elements then just return 
120   if(!hasEmpty)
121     {
122     return true;
123     }
124   // if we have empty elements we need to check policy CMP0007
125   switch(this->Makefile->GetPolicyStatus(cmPolicies::CMP0007))
126     {
127     case cmPolicies::WARN: 
128       {
129       // Default is to warn and use old behavior
130       // OLD behavior is to allow compatibility, so recall
131       // ExpandListArgument without the true which will remove
132       // empty values
133       list.clear();
134       cmSystemTools::ExpandListArgument(listString, list);
135       std::string warn = this->Makefile->GetPolicies()->
136         GetPolicyWarning(cmPolicies::CMP0007);
137       warn += " List has value = [";
138       warn += listString;
139       warn += "].";
140       this->Makefile->IssueMessage(cmake::AUTHOR_WARNING,
141                                    warn);
142       return true;
143       }
144     case cmPolicies::OLD:
145       // OLD behavior is to allow compatibility, so recall
146       // ExpandListArgument without the true which will remove
147       // empty values
148       list.clear();
149       cmSystemTools::ExpandListArgument(listString, list);
150       return true;
151     case cmPolicies::NEW:
152       return true;
153     case cmPolicies::REQUIRED_IF_USED:
154     case cmPolicies::REQUIRED_ALWAYS:
155       this->Makefile->IssueMessage(
156         cmake::FATAL_ERROR,
157         this->Makefile->GetPolicies()
158         ->GetRequiredPolicyError(cmPolicies::CMP0007)
159         );
160       return false;
161     }
162   return true;
163 }
164
165 //----------------------------------------------------------------------------
166 bool cmListCommand::HandleLengthCommand(std::vector<std::string> const& args)
167 {
168   if(args.size() != 3)
169     {
170     this->SetError("sub-command LENGTH requires two arguments.");
171     return false;
172     }
173
174   const std::string& listName = args[1];
175   const std::string& variableName = args[args.size() - 1];
176   std::vector<std::string> varArgsExpanded;
177   // do not check the return value here
178   // if the list var is not found varArgsExpanded will have size 0
179   // and we will return 0
180   this->GetList(varArgsExpanded, listName.c_str());
181   size_t length = varArgsExpanded.size();
182   char buffer[1024];
183   sprintf(buffer, "%d", static_cast<int>(length));
184
185   this->Makefile->AddDefinition(variableName.c_str(), buffer);
186   return true;
187 }
188
189 //----------------------------------------------------------------------------
190 bool cmListCommand::HandleGetCommand(std::vector<std::string> const& args)
191 {
192   if(args.size() < 4)
193     {
194     this->SetError("sub-command GET requires at least three arguments.");
195     return false;
196     }
197
198   const std::string& listName = args[1];
199   const std::string& variableName = args[args.size() - 1];
200   // expand the variable
201   std::vector<std::string> varArgsExpanded;
202   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
203     {
204     this->Makefile->AddDefinition(variableName.c_str(), "NOTFOUND");
205     return true;
206     }
207   // FIXME: Add policy to make non-existing lists an error like empty lists.
208   if(varArgsExpanded.empty())
209     {
210     this->SetError("GET given empty list");
211     return false;
212     }
213
214   std::string value;
215   size_t cc;
216   const char* sep = "";
217   for ( cc = 2; cc < args.size()-1; cc ++ )
218     {
219     int item = atoi(args[cc].c_str());
220     value += sep;
221     sep = ";";
222     size_t nitem = varArgsExpanded.size();
223     if ( item < 0 )
224       {
225       item = (int)nitem + item;
226       }
227     if ( item < 0 || nitem <= (size_t)item )
228       {
229       cmOStringStream str;
230       str << "index: " << item << " out of range (-"
231           << varArgsExpanded.size() << ", "
232           << varArgsExpanded.size()-1 << ")";
233       this->SetError(str.str().c_str());
234       return false;
235       }
236     value += varArgsExpanded[item];
237     }
238
239   this->Makefile->AddDefinition(variableName.c_str(), value.c_str());
240   return true;
241 }
242
243 //----------------------------------------------------------------------------
244 bool cmListCommand::HandleAppendCommand(std::vector<std::string> const& args)
245 {
246   if(args.size() < 2)
247     {
248     this->SetError("sub-command APPEND requires at least one argument.");
249     return false;
250     }
251
252   // Skip if nothing to append.
253   if(args.size() < 3)
254     {
255     return true;
256     }
257
258   const std::string& listName = args[1];
259   // expand the variable
260   std::string listString;
261   this->GetListString(listString, listName.c_str());
262   size_t cc;
263   for ( cc = 2; cc < args.size(); ++ cc )
264     {
265     if(listString.size())
266       {
267       listString += ";";
268       }
269     listString += args[cc];
270     }
271
272   this->Makefile->AddDefinition(listName.c_str(), listString.c_str());
273   return true;
274 }
275
276 //----------------------------------------------------------------------------
277 bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args)
278 {
279   if(args.size() != 4)
280     {
281     this->SetError("sub-command FIND requires three arguments.");
282     return false;
283     }
284
285   const std::string& listName = args[1];
286   const std::string& variableName = args[args.size() - 1];
287   // expand the variable
288   std::vector<std::string> varArgsExpanded;
289   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
290     {
291     this->Makefile->AddDefinition(variableName.c_str(), "-1");
292     return true;
293     }
294
295   std::vector<std::string>::iterator it;
296   unsigned int index = 0;
297   for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it )
298     {
299     if ( *it == args[2] )
300       {
301       char indexString[32];
302       sprintf(indexString, "%d", index);
303       this->Makefile->AddDefinition(variableName.c_str(), indexString);
304       return true;
305       }
306     index++;
307     }
308
309   this->Makefile->AddDefinition(variableName.c_str(), "-1");
310   return true;
311 }
312
313 //----------------------------------------------------------------------------
314 bool cmListCommand::HandleInsertCommand(std::vector<std::string> const& args)
315 {
316   if(args.size() < 4)
317     {
318     this->SetError("sub-command INSERT requires at least three arguments.");
319     return false;
320     }
321
322   const std::string& listName = args[1];
323
324   // expand the variable
325   int item = atoi(args[2].c_str());
326   std::vector<std::string> varArgsExpanded;
327   if((!this->GetList(varArgsExpanded, listName.c_str())
328       || varArgsExpanded.empty()) && item != 0)
329     {
330     cmOStringStream str;
331     str << "index: " << item << " out of range (0, 0)";
332     this->SetError(str.str().c_str());
333     return false;
334     }
335
336   if ( varArgsExpanded.size() != 0 )
337     {
338     size_t nitem = varArgsExpanded.size();
339     if ( item < 0 )
340       {
341       item = (int)nitem + item;
342       }
343     if ( item < 0 || nitem <= (size_t)item )
344       {
345       cmOStringStream str;
346       str << "index: " << item << " out of range (-"
347         << varArgsExpanded.size() << ", "
348         << (varArgsExpanded.size() == 0?0:(varArgsExpanded.size()-1)) << ")";
349       this->SetError(str.str().c_str());
350       return false;
351       }
352     }
353   size_t cc;
354   size_t cnt = 0;
355   for ( cc = 3; cc < args.size(); ++ cc )
356     {
357     varArgsExpanded.insert(varArgsExpanded.begin()+item+cnt, args[cc]);
358     cnt ++;
359     }
360
361   std::string value;
362   const char* sep = "";
363   for ( cc = 0; cc < varArgsExpanded.size(); cc ++ )
364     {
365     value += sep;
366     value += varArgsExpanded[cc];
367     sep = ";";
368     }
369
370   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
371   return true;
372 }
373
374 //----------------------------------------------------------------------------
375 bool cmListCommand
376 ::HandleRemoveItemCommand(std::vector<std::string> const& args)
377 {
378   if(args.size() < 3)
379     {
380     this->SetError("sub-command REMOVE_ITEM requires two or more arguments.");
381     return false;
382     }
383
384   const std::string& listName = args[1];
385   // expand the variable
386   std::vector<std::string> varArgsExpanded;
387   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
388     {
389     this->SetError("sub-command REMOVE_ITEM requires list to be present.");
390     return false;
391     }
392
393   size_t cc;
394   for ( cc = 2; cc < args.size(); ++ cc )
395     {
396     size_t kk = 0;
397     while ( kk < varArgsExpanded.size() )
398       {
399       if ( varArgsExpanded[kk] == args[cc] )
400         {
401         varArgsExpanded.erase(varArgsExpanded.begin()+kk);
402         }
403       else
404         {
405         kk ++;
406         }
407       }
408     }
409
410   std::string value;
411   const char* sep = "";
412   for ( cc = 0; cc < varArgsExpanded.size(); cc ++ )
413     {
414     value += sep;
415     value += varArgsExpanded[cc];
416     sep = ";";
417     }
418
419   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
420   return true;
421 }
422
423 //----------------------------------------------------------------------------
424 bool cmListCommand
425 ::HandleReverseCommand(std::vector<std::string> const& args)
426 {
427   if(args.size() < 2)
428     {
429     this->SetError("sub-command REVERSE requires a list as an argument.");
430     return false;
431     }
432
433   const std::string& listName = args[1];
434   // expand the variable
435   std::vector<std::string> varArgsExpanded;
436   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
437     {
438     this->SetError("sub-command REVERSE requires list to be present.");
439     return false;
440     }
441
442   std::string value;
443   std::vector<std::string>::reverse_iterator it;
444   const char* sep = "";
445   for ( it = varArgsExpanded.rbegin(); it != varArgsExpanded.rend(); ++ it )
446     {
447     value += sep;
448     value += it->c_str();
449     sep = ";";
450     }
451
452   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
453   return true;
454 }
455
456 //----------------------------------------------------------------------------
457 bool cmListCommand
458 ::HandleRemoveDuplicatesCommand(std::vector<std::string> const& args)
459 {
460   if(args.size() < 2)
461     {
462     this->SetError(
463       "sub-command REMOVE_DUPLICATES requires a list as an argument.");
464     return false;
465     }
466
467   const std::string& listName = args[1];
468   // expand the variable
469   std::vector<std::string> varArgsExpanded;
470   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
471     {
472     this->SetError(
473       "sub-command REMOVE_DUPLICATES requires list to be present.");
474     return false;
475     }
476
477   std::string value;
478
479
480   std::set<std::string> unique;
481   std::vector<std::string>::iterator it;
482   const char* sep = "";
483   for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it )
484     {
485     if (unique.find(*it) != unique.end())
486       {
487       continue;
488       }
489     unique.insert(*it);
490     value += sep;
491     value += it->c_str();
492     sep = ";";
493     }
494
495
496   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
497   return true;
498 }
499
500 //----------------------------------------------------------------------------
501 bool cmListCommand
502 ::HandleSortCommand(std::vector<std::string> const& args)
503 {
504   if(args.size() < 2)
505     {
506     this->SetError("sub-command SORT requires a list as an argument.");
507     return false;
508     }
509
510   const std::string& listName = args[1];
511   // expand the variable
512   std::vector<std::string> varArgsExpanded;
513   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
514     {
515     this->SetError("sub-command SORT requires list to be present.");
516     return false;
517     }
518
519   std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
520
521   std::string value;
522   std::vector<std::string>::iterator it;
523   const char* sep = "";
524   for ( it = varArgsExpanded.begin(); it != varArgsExpanded.end(); ++ it )
525     {
526     value += sep;
527     value += it->c_str();
528     sep = ";";
529     }
530
531   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
532   return true;
533 }
534
535 //----------------------------------------------------------------------------
536 bool cmListCommand::HandleRemoveAtCommand(
537   std::vector<std::string> const& args)
538 {
539   if(args.size() < 3)
540     {
541     this->SetError("sub-command REMOVE_AT requires at least "
542                    "two arguments.");
543     return false;
544     }
545
546   const std::string& listName = args[1];
547   // expand the variable
548   std::vector<std::string> varArgsExpanded;
549   if ( !this->GetList(varArgsExpanded, listName.c_str()) )
550     {
551     this->SetError("sub-command REMOVE_AT requires list to be present.");
552     return false;
553     }
554   // FIXME: Add policy to make non-existing lists an error like empty lists.
555   if(varArgsExpanded.empty())
556     {
557     this->SetError("REMOVE_AT given empty list");
558     return false;
559     }
560
561   size_t cc;
562   std::vector<size_t> removed;
563   for ( cc = 2; cc < args.size(); ++ cc )
564     {
565     int item = atoi(args[cc].c_str());
566     size_t nitem = varArgsExpanded.size();
567     if ( item < 0 )
568       {
569       item = (int)nitem + item;
570       }
571     if ( item < 0 || nitem <= (size_t)item )
572       {
573       cmOStringStream str;
574       str << "index: " << item << " out of range (-"
575           << varArgsExpanded.size() << ", "
576           << varArgsExpanded.size()-1 << ")";
577       this->SetError(str.str().c_str());
578       return false;
579       }
580     removed.push_back(static_cast<size_t>(item));
581     }
582
583   std::string value;
584   const char* sep = "";
585   for ( cc = 0; cc < varArgsExpanded.size(); ++ cc )
586     {
587     size_t kk;
588     bool found = false;
589     for ( kk = 0; kk < removed.size(); ++ kk )
590       {
591       if ( cc == removed[kk] )
592         {
593         found = true;
594         }
595       }
596
597     if ( !found )
598       {
599       value += sep;
600       value += varArgsExpanded[cc];
601       sep = ";";
602       }
603     }
604
605   this->Makefile->AddDefinition(listName.c_str(), value.c_str());
606   return true;
607 }
608