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