Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / cmStringCommand.cxx
blob8aa322d820f7fbd72509973ef8291d00e2ea9f0e
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmStringCommand.cxx,v $
5 Language: C++
6 <<<<<<< cmStringCommand.cxx
7 Date: $Date: 2008/01/23 15:27:59 $
8 Version: $Revision: 1.27 $
9 =======
10 Date: $Date: 2008-08-26 16:54:06 $
11 Version: $Revision: 1.28 $
12 >>>>>>> 1.28
14 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
15 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
17 This software is distributed WITHOUT ANY WARRANTY; without even
18 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 PURPOSE. See the above copyright notices for more information.
21 =========================================================================*/
22 #include "cmStringCommand.h"
23 #include <cmsys/RegularExpression.hxx>
24 #include <cmsys/SystemTools.hxx>
26 #include <stdlib.h> // required for atoi
27 #include <ctype.h>
28 #include <time.h>
30 //----------------------------------------------------------------------------
31 bool cmStringCommand
32 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
34 if(args.size() < 1)
36 this->SetError("must be called with at least one argument.");
37 return false;
40 const std::string &subCommand = args[0];
41 if(subCommand == "REGEX")
43 return this->HandleRegexCommand(args);
45 else if(subCommand == "REPLACE")
47 return this->HandleReplaceCommand(args);
49 else if(subCommand == "TOLOWER")
51 return this->HandleToUpperLowerCommand(args, false);
53 else if(subCommand == "TOUPPER")
55 return this->HandleToUpperLowerCommand(args, true);
57 else if(subCommand == "COMPARE")
59 return this->HandleCompareCommand(args);
61 else if(subCommand == "ASCII")
63 return this->HandleAsciiCommand(args);
65 else if(subCommand == "CONFIGURE")
67 return this->HandleConfigureCommand(args);
69 else if(subCommand == "LENGTH")
71 return this->HandleLengthCommand(args);
73 else if(subCommand == "SUBSTRING")
75 return this->HandleSubstringCommand(args);
77 else if(subCommand == "STRIP")
79 return this->HandleStripCommand(args);
81 else if(subCommand == "RANDOM")
83 return this->HandleRandomCommand(args);
86 std::string e = "does not recognize sub-command "+subCommand;
87 this->SetError(e.c_str());
88 return false;
91 //----------------------------------------------------------------------------
92 bool cmStringCommand::HandleToUpperLowerCommand(
93 std::vector<std::string> const& args, bool toUpper)
95 if ( args.size() < 3 )
97 this->SetError("no output variable specified");
98 return false;
101 std::string outvar = args[2];
102 std::string output;
104 if (toUpper)
106 output = cmSystemTools::UpperCase(args[1]);
108 else
110 output = cmSystemTools::LowerCase(args[1]);
113 // Store the output in the provided variable.
114 this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
115 return true;
118 //----------------------------------------------------------------------------
119 bool cmStringCommand::HandleAsciiCommand(std::vector<std::string> const& args)
121 if ( args.size() < 3 )
123 this->SetError("No output variable specified");
124 return false;
126 std::string::size_type cc;
127 std::string outvar = args[args.size()-1];
128 std::string output = "";
129 for ( cc = 1; cc < args.size()-1; cc ++ )
131 int ch = atoi(args[cc].c_str());
132 if ( ch > 0 && ch < 256 )
134 output += static_cast<char>(ch);
136 else
138 std::string error = "Character with code ";
139 error += ch;
140 error += " does not exist.";
141 this->SetError(error.c_str());
142 return false;
145 // Store the output in the provided variable.
146 this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
147 return true;
150 //----------------------------------------------------------------------------
151 bool cmStringCommand::HandleConfigureCommand(
152 std::vector<std::string> const& args)
154 if ( args.size() < 2 )
156 this->SetError("No input string specified.");
157 return false;
159 else if ( args.size() < 3 )
161 this->SetError("No output variable specified.");
162 return false;
165 // Parse options.
166 bool escapeQuotes = false;
167 bool atOnly = false;
168 for(unsigned int i = 3; i < args.size(); ++i)
170 if(args[i] == "@ONLY")
172 atOnly = true;
174 else if(args[i] == "ESCAPE_QUOTES")
176 escapeQuotes = true;
178 else
180 cmOStringStream err;
181 err << "Unrecognized argument \"" << args[i] << "\"";
182 this->SetError(err.str().c_str());
183 return false;
187 // Configure the string.
188 std::string output;
189 this->Makefile->ConfigureString(args[1], output, atOnly, escapeQuotes);
191 // Store the output in the provided variable.
192 this->Makefile->AddDefinition(args[2].c_str(), output.c_str());
194 return true;
197 //----------------------------------------------------------------------------
198 bool cmStringCommand::HandleRegexCommand(std::vector<std::string> const& args)
200 if(args.size() < 2)
202 this->SetError("sub-command REGEX requires a mode to be specified.");
203 return false;
205 std::string mode = args[1];
206 if(mode == "MATCH")
208 if(args.size() < 5)
210 this->SetError("sub-command REGEX, mode MATCH needs "
211 "at least 5 arguments total to command.");
212 return false;
214 return this->RegexMatch(args);
216 else if(mode == "MATCHALL")
218 if(args.size() < 5)
220 this->SetError("sub-command REGEX, mode MATCHALL needs "
221 "at least 5 arguments total to command.");
222 return false;
224 return this->RegexMatchAll(args);
226 else if(mode == "REPLACE")
228 if(args.size() < 6)
230 this->SetError("sub-command REGEX, mode REPLACE needs "
231 "at least 6 arguments total to command.");
232 return false;
234 return this->RegexReplace(args);
237 std::string e = "sub-command REGEX does not recognize mode "+mode;
238 this->SetError(e.c_str());
239 return false;
242 //----------------------------------------------------------------------------
243 bool cmStringCommand::RegexMatch(std::vector<std::string> const& args)
245 //"STRING(REGEX MATCH <regular_expression> <output variable>
246 // <input> [<input>...])\n";
247 std::string regex = args[2];
248 std::string outvar = args[3];
250 // Concatenate all the last arguments together.
251 std::string input = args[4];
252 for(unsigned int i=5; i < args.size(); ++i)
254 input += args[i];
257 this->ClearMatches(this->Makefile);
258 // Compile the regular expression.
259 cmsys::RegularExpression re;
260 if(!re.compile(regex.c_str()))
262 std::string e =
263 "sub-command REGEX, mode MATCH failed to compile regex \""+regex+"\".";
264 this->SetError(e.c_str());
265 return false;
268 // Scan through the input for all matches.
269 std::string output;
270 if(re.find(input.c_str()))
272 this->StoreMatches(this->Makefile, re);
273 std::string::size_type l = re.start();
274 std::string::size_type r = re.end();
275 if(r-l == 0)
277 std::string e =
278 "sub-command REGEX, mode MATCH regex \""+regex+
279 "\" matched an empty string.";
280 this->SetError(e.c_str());
281 return false;
283 output = input.substr(l, r-l);
286 // Store the output in the provided variable.
287 this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
288 return true;
291 //----------------------------------------------------------------------------
292 bool cmStringCommand::RegexMatchAll(std::vector<std::string> const& args)
294 //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
295 // [<input>...])\n";
296 std::string regex = args[2];
297 std::string outvar = args[3];
299 // Concatenate all the last arguments together.
300 std::string input = args[4];
301 for(unsigned int i=5; i < args.size(); ++i)
303 input += args[i];
306 this->ClearMatches(this->Makefile);
307 // Compile the regular expression.
308 cmsys::RegularExpression re;
309 if(!re.compile(regex.c_str()))
311 std::string e =
312 "sub-command REGEX, mode MATCHALL failed to compile regex \""+
313 regex+"\".";
314 this->SetError(e.c_str());
315 return false;
318 // Scan through the input for all matches.
319 std::string output;
320 const char* p = input.c_str();
321 while(re.find(p))
323 this->StoreMatches(this->Makefile, re);
324 std::string::size_type l = re.start();
325 std::string::size_type r = re.end();
326 if(r-l == 0)
328 std::string e = "sub-command REGEX, mode MATCHALL regex \""+
329 regex+"\" matched an empty string.";
330 this->SetError(e.c_str());
331 return false;
333 if(output.length() > 0)
335 output += ";";
337 output += std::string(p+l, r-l);
338 p += r;
341 // Store the output in the provided variable.
342 this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
343 return true;
346 //----------------------------------------------------------------------------
347 bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
349 //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
350 // <output variable> <input> [<input>...])\n"
351 std::string regex = args[2];
352 std::string replace = args[3];
353 std::string outvar = args[4];
355 // Pull apart the replace expression to find the escaped [0-9] values.
356 std::vector<RegexReplacement> replacement;
357 std::string::size_type l = 0;
358 while(l < replace.length())
360 std::string::size_type r = replace.find("\\", l);
361 if(r == std::string::npos)
363 r = replace.length();
364 replacement.push_back(replace.substr(l, r-l));
366 else
368 if(r-l > 0)
370 replacement.push_back(replace.substr(l, r-l));
372 if(r == (replace.length()-1))
374 this->SetError("sub-command REGEX, mode REPLACE: "
375 "replace-expression ends in a backslash.");
376 return false;
378 if((replace[r+1] >= '0') && (replace[r+1] <= '9'))
380 replacement.push_back(replace[r+1]-'0');
382 else if(replace[r+1] == 'n')
384 replacement.push_back("\n");
386 else if(replace[r+1] == '\\')
388 replacement.push_back("\\");
390 else
392 std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \"";
393 e += replace.substr(r, 2);
394 e += "\"in replace-expression.";
395 this->SetError(e.c_str());
396 return false;
398 r += 2;
400 l = r;
403 // Concatenate all the last arguments together.
404 std::string input = args[5];
405 for(unsigned int i=6; i < args.size(); ++i)
407 input += args[i];
410 this->ClearMatches(this->Makefile);
411 // Compile the regular expression.
412 cmsys::RegularExpression re;
413 if(!re.compile(regex.c_str()))
415 std::string e =
416 "sub-command REGEX, mode REPLACE failed to compile regex \""+
417 regex+"\".";
418 this->SetError(e.c_str());
419 return false;
422 // Scan through the input for all matches.
423 std::string output;
424 std::string::size_type base = 0;
425 while(re.find(input.c_str()+base))
427 this->StoreMatches(this->Makefile, re);
428 std::string::size_type l2 = re.start();
429 std::string::size_type r = re.end();
431 // Concatenate the part of the input that was not matched.
432 output += input.substr(base, l2);
434 // Make sure the match had some text.
435 if(r-l2 == 0)
437 std::string e = "sub-command REGEX, mode REPLACE regex \""+
438 regex+"\" matched an empty string.";
439 this->SetError(e.c_str());
440 return false;
443 // Concatenate the replacement for the match.
444 for(unsigned int i=0; i < replacement.size(); ++i)
446 if(replacement[i].number < 0)
448 // This is just a plain-text part of the replacement.
449 output += replacement[i].value;
451 else
453 // Replace with part of the match.
454 int n = replacement[i].number;
455 std::string::size_type start = re.start(n);
456 std::string::size_type end = re.end(n);
457 std::string::size_type len = input.length()-base;
458 if((start != std::string::npos) && (end != std::string::npos) &&
459 (start <= len) && (end <= len))
461 output += input.substr(base+start, end-start);
463 else
465 std::string e =
466 "sub-command REGEX, mode REPLACE: replace expression \""+
467 replace+"\" contains an out-of-range escape for regex \""+
468 regex+"\".";
469 this->SetError(e.c_str());
470 return false;
475 // Move past the match.
476 base += r;
479 // Concatenate the text after the last match.
480 output += input.substr(base, input.length()-base);
482 // Store the output in the provided variable.
483 this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
484 return true;
487 //----------------------------------------------------------------------------
488 void cmStringCommand::ClearMatches(cmMakefile* mf)
490 for (unsigned int i=0; i<10; i++)
492 char name[128];
493 sprintf(name, "CMAKE_MATCH_%d", i);
494 mf->AddDefinition(name, "");
498 //----------------------------------------------------------------------------
499 void cmStringCommand::StoreMatches(cmMakefile* mf,cmsys::RegularExpression& re)
501 for (unsigned int i=0; i<10; i++)
503 char name[128];
504 sprintf(name, "CMAKE_MATCH_%d", i);
505 mf->AddDefinition(name, re.match(i).c_str());
509 //----------------------------------------------------------------------------
510 bool cmStringCommand::HandleCompareCommand(std::vector<std::string> const&
511 args)
513 if(args.size() < 2)
515 this->SetError("sub-command COMPARE requires a mode to be specified.");
516 return false;
518 std::string mode = args[1];
519 if((mode == "EQUAL") || (mode == "NOTEQUAL") ||
520 (mode == "LESS") || (mode == "GREATER"))
522 if(args.size() < 5)
524 std::string e = "sub-command COMPARE, mode ";
525 e += mode;
526 e += " needs at least 5 arguments total to command.";
527 this->SetError(e.c_str());
528 return false;
531 const std::string& left = args[2];
532 const std::string& right = args[3];
533 const std::string& outvar = args[4];
534 bool result;
535 if(mode == "LESS")
537 result = (left < right);
539 else if(mode == "GREATER")
541 result = (left > right);
543 else if(mode == "EQUAL")
545 result = (left == right);
547 else // if(mode == "NOTEQUAL")
549 result = !(left == right);
551 if(result)
553 this->Makefile->AddDefinition(outvar.c_str(), "1");
555 else
557 this->Makefile->AddDefinition(outvar.c_str(), "0");
559 return true;
561 std::string e = "sub-command COMPARE does not recognize mode "+mode;
562 this->SetError(e.c_str());
563 return false;
566 //----------------------------------------------------------------------------
567 bool cmStringCommand::HandleReplaceCommand(std::vector<std::string> const&
568 args)
570 if(args.size() < 5)
572 this->SetError("sub-command REPLACE requires four arguments.");
573 return false;
576 const std::string& matchExpression = args[1];
577 const std::string& replaceExpression = args[2];
578 const std::string& variableName = args[3];
580 std::string input = args[4];
581 for(unsigned int i=5; i < args.size(); ++i)
583 input += args[i];
586 cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
587 replaceExpression.c_str());
589 this->Makefile->AddDefinition(variableName.c_str(), input.c_str());
590 return true;
593 //----------------------------------------------------------------------------
594 bool cmStringCommand::HandleSubstringCommand(std::vector<std::string> const&
595 args)
597 if(args.size() != 5)
599 this->SetError("sub-command REPLACE requires four arguments.");
600 return false;
603 const std::string& stringValue = args[1];
604 int begin = atoi(args[2].c_str());
605 int end = atoi(args[3].c_str());
606 const std::string& variableName = args[4];
608 size_t stringLength = stringValue.size();
609 int intStringLength = static_cast<int>(stringLength);
610 if ( begin < 0 || begin > intStringLength )
612 cmOStringStream ostr;
613 ostr << "begin index: " << begin << " is out of range 0 - "
614 << stringLength;
615 this->SetError(ostr.str().c_str());
616 return false;
618 int leftOverLength = intStringLength - begin;
619 if ( end < 0 || end > leftOverLength )
621 cmOStringStream ostr;
622 ostr << "end index: " << end << " is out of range " << 0 << " - "
623 << leftOverLength;
624 this->SetError(ostr.str().c_str());
625 return false;
628 this->Makefile->AddDefinition(variableName.c_str(),
629 stringValue.substr(begin, end).c_str());
630 return true;
633 //----------------------------------------------------------------------------
634 bool cmStringCommand
635 ::HandleLengthCommand(std::vector<std::string> const& args)
637 if(args.size() != 3)
639 this->SetError("sub-command LENGTH requires two arguments.");
640 return false;
643 const std::string& stringValue = args[1];
644 const std::string& variableName = args[2];
646 size_t length = stringValue.size();
647 char buffer[1024];
648 sprintf(buffer, "%d", static_cast<int>(length));
650 this->Makefile->AddDefinition(variableName.c_str(), buffer);
651 return true;
654 //----------------------------------------------------------------------------
655 bool cmStringCommand::HandleStripCommand(
656 std::vector<std::string> const& args)
658 if(args.size() != 3)
660 this->SetError("sub-command LENGTH requires two arguments.");
661 return false;
664 const std::string& stringValue = args[1];
665 const std::string& variableName = args[2];
666 size_t inStringLength = stringValue.size();
667 size_t startPos = inStringLength + 1;
668 size_t endPos = 0;
669 const char* ptr = stringValue.c_str();
670 size_t cc;
671 for ( cc = 0; cc < inStringLength; ++ cc )
673 if ( !isspace(*ptr) )
675 if ( startPos > inStringLength )
677 startPos = cc;
679 endPos = cc;
681 ++ ptr;
684 size_t outLength = 0;
686 // if the input string didn't contain any non-space characters, return
687 // an empty string
688 if (startPos > inStringLength)
690 outLength = 0;
691 startPos = 0;
693 else
695 outLength=endPos - startPos + 1;
698 this->Makefile->AddDefinition(variableName.c_str(),
699 stringValue.substr(startPos, outLength).c_str());
700 return true;
703 //----------------------------------------------------------------------------
704 bool cmStringCommand
705 ::HandleRandomCommand(std::vector<std::string> const& args)
707 if(args.size() < 2 || args.size() == 3 || args.size() == 5)
709 this->SetError("sub-command RANDOM requires at least one argument.");
710 return false;
713 int length = 5;
714 const char cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
715 "QWERTYUIOPASDFGHJKLZXCVBNM"
716 "0123456789";
717 std::string alphabet;
719 if ( args.size() > 3 )
721 size_t i = 1;
722 size_t stopAt = args.size() - 2;
724 for ( ; i < stopAt; ++i )
726 if ( args[i] == "LENGTH" )
728 ++i;
729 length = atoi(args[i].c_str());
731 else if ( args[i] == "ALPHABET" )
733 ++i;
734 alphabet = args[i];
738 if ( !alphabet.size() )
740 alphabet = cmStringCommandDefaultAlphabet;
743 double sizeofAlphabet = alphabet.size();
744 if ( sizeofAlphabet < 1 )
746 this->SetError("sub-command RANDOM invoked with bad alphabet.");
747 return false;
749 if ( length < 1 )
751 this->SetError("sub-command RANDOM invoked with bad length.");
752 return false;
754 const std::string& variableName = args[args.size()-1];
756 std::vector<char> result;
757 srand((int)time(NULL));
758 const char* alphaPtr = alphabet.c_str();
759 int cc;
760 for ( cc = 0; cc < length; cc ++ )
762 int idx=(int) (sizeofAlphabet* rand()/(RAND_MAX+1.0));
763 result.push_back(*(alphaPtr + idx));
765 result.push_back(0);
767 this->Makefile->AddDefinition(variableName.c_str(), &*result.begin());
768 return true;