1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmStringCommand.cxx,v $
6 Date: $Date: 2008-08-26 16:54:06 $
7 Version: $Revision: 1.28 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmStringCommand.h"
18 #include <cmsys/RegularExpression.hxx>
19 #include <cmsys/SystemTools.hxx>
21 #include <stdlib.h> // required for atoi
25 //----------------------------------------------------------------------------
27 ::InitialPass(std::vector
<std::string
> const& args
, cmExecutionStatus
&)
31 this->SetError("must be called with at least one argument.");
35 const std::string
&subCommand
= args
[0];
36 if(subCommand
== "REGEX")
38 return this->HandleRegexCommand(args
);
40 else if(subCommand
== "REPLACE")
42 return this->HandleReplaceCommand(args
);
44 else if(subCommand
== "TOLOWER")
46 return this->HandleToUpperLowerCommand(args
, false);
48 else if(subCommand
== "TOUPPER")
50 return this->HandleToUpperLowerCommand(args
, true);
52 else if(subCommand
== "COMPARE")
54 return this->HandleCompareCommand(args
);
56 else if(subCommand
== "ASCII")
58 return this->HandleAsciiCommand(args
);
60 else if(subCommand
== "CONFIGURE")
62 return this->HandleConfigureCommand(args
);
64 else if(subCommand
== "LENGTH")
66 return this->HandleLengthCommand(args
);
68 else if(subCommand
== "SUBSTRING")
70 return this->HandleSubstringCommand(args
);
72 else if(subCommand
== "STRIP")
74 return this->HandleStripCommand(args
);
76 else if(subCommand
== "RANDOM")
78 return this->HandleRandomCommand(args
);
81 std::string e
= "does not recognize sub-command "+subCommand
;
82 this->SetError(e
.c_str());
86 //----------------------------------------------------------------------------
87 bool cmStringCommand::HandleToUpperLowerCommand(
88 std::vector
<std::string
> const& args
, bool toUpper
)
90 if ( args
.size() < 3 )
92 this->SetError("no output variable specified");
96 std::string outvar
= args
[2];
101 output
= cmSystemTools::UpperCase(args
[1]);
105 output
= cmSystemTools::LowerCase(args
[1]);
108 // Store the output in the provided variable.
109 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
113 //----------------------------------------------------------------------------
114 bool cmStringCommand::HandleAsciiCommand(std::vector
<std::string
> const& args
)
116 if ( args
.size() < 3 )
118 this->SetError("No output variable specified");
121 std::string::size_type cc
;
122 std::string outvar
= args
[args
.size()-1];
123 std::string output
= "";
124 for ( cc
= 1; cc
< args
.size()-1; cc
++ )
126 int ch
= atoi(args
[cc
].c_str());
127 if ( ch
> 0 && ch
< 256 )
129 output
+= static_cast<char>(ch
);
133 std::string error
= "Character with code ";
135 error
+= " does not exist.";
136 this->SetError(error
.c_str());
140 // Store the output in the provided variable.
141 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
145 //----------------------------------------------------------------------------
146 bool cmStringCommand::HandleConfigureCommand(
147 std::vector
<std::string
> const& args
)
149 if ( args
.size() < 2 )
151 this->SetError("No input string specified.");
154 else if ( args
.size() < 3 )
156 this->SetError("No output variable specified.");
161 bool escapeQuotes
= false;
163 for(unsigned int i
= 3; i
< args
.size(); ++i
)
165 if(args
[i
] == "@ONLY")
169 else if(args
[i
] == "ESCAPE_QUOTES")
176 err
<< "Unrecognized argument \"" << args
[i
] << "\"";
177 this->SetError(err
.str().c_str());
182 // Configure the string.
184 this->Makefile
->ConfigureString(args
[1], output
, atOnly
, escapeQuotes
);
186 // Store the output in the provided variable.
187 this->Makefile
->AddDefinition(args
[2].c_str(), output
.c_str());
192 //----------------------------------------------------------------------------
193 bool cmStringCommand::HandleRegexCommand(std::vector
<std::string
> const& args
)
197 this->SetError("sub-command REGEX requires a mode to be specified.");
200 std::string mode
= args
[1];
205 this->SetError("sub-command REGEX, mode MATCH needs "
206 "at least 5 arguments total to command.");
209 return this->RegexMatch(args
);
211 else if(mode
== "MATCHALL")
215 this->SetError("sub-command REGEX, mode MATCHALL needs "
216 "at least 5 arguments total to command.");
219 return this->RegexMatchAll(args
);
221 else if(mode
== "REPLACE")
225 this->SetError("sub-command REGEX, mode REPLACE needs "
226 "at least 6 arguments total to command.");
229 return this->RegexReplace(args
);
232 std::string e
= "sub-command REGEX does not recognize mode "+mode
;
233 this->SetError(e
.c_str());
237 //----------------------------------------------------------------------------
238 bool cmStringCommand::RegexMatch(std::vector
<std::string
> const& args
)
240 //"STRING(REGEX MATCH <regular_expression> <output variable>
241 // <input> [<input>...])\n";
242 std::string regex
= args
[2];
243 std::string outvar
= args
[3];
245 // Concatenate all the last arguments together.
246 std::string input
= args
[4];
247 for(unsigned int i
=5; i
< args
.size(); ++i
)
252 this->ClearMatches(this->Makefile
);
253 // Compile the regular expression.
254 cmsys::RegularExpression re
;
255 if(!re
.compile(regex
.c_str()))
258 "sub-command REGEX, mode MATCH failed to compile regex \""+regex
+"\".";
259 this->SetError(e
.c_str());
263 // Scan through the input for all matches.
265 if(re
.find(input
.c_str()))
267 this->StoreMatches(this->Makefile
, re
);
268 std::string::size_type l
= re
.start();
269 std::string::size_type r
= re
.end();
273 "sub-command REGEX, mode MATCH regex \""+regex
+
274 "\" matched an empty string.";
275 this->SetError(e
.c_str());
278 output
= input
.substr(l
, r
-l
);
281 // Store the output in the provided variable.
282 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
286 //----------------------------------------------------------------------------
287 bool cmStringCommand::RegexMatchAll(std::vector
<std::string
> const& args
)
289 //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
291 std::string regex
= args
[2];
292 std::string outvar
= args
[3];
294 // Concatenate all the last arguments together.
295 std::string input
= args
[4];
296 for(unsigned int i
=5; i
< args
.size(); ++i
)
301 this->ClearMatches(this->Makefile
);
302 // Compile the regular expression.
303 cmsys::RegularExpression re
;
304 if(!re
.compile(regex
.c_str()))
307 "sub-command REGEX, mode MATCHALL failed to compile regex \""+
309 this->SetError(e
.c_str());
313 // Scan through the input for all matches.
315 const char* p
= input
.c_str();
318 this->StoreMatches(this->Makefile
, re
);
319 std::string::size_type l
= re
.start();
320 std::string::size_type r
= re
.end();
323 std::string e
= "sub-command REGEX, mode MATCHALL regex \""+
324 regex
+"\" matched an empty string.";
325 this->SetError(e
.c_str());
328 if(output
.length() > 0)
332 output
+= std::string(p
+l
, r
-l
);
336 // Store the output in the provided variable.
337 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
341 //----------------------------------------------------------------------------
342 bool cmStringCommand::RegexReplace(std::vector
<std::string
> const& args
)
344 //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
345 // <output variable> <input> [<input>...])\n"
346 std::string regex
= args
[2];
347 std::string replace
= args
[3];
348 std::string outvar
= args
[4];
350 // Pull apart the replace expression to find the escaped [0-9] values.
351 std::vector
<RegexReplacement
> replacement
;
352 std::string::size_type l
= 0;
353 while(l
< replace
.length())
355 std::string::size_type r
= replace
.find("\\", l
);
356 if(r
== std::string::npos
)
358 r
= replace
.length();
359 replacement
.push_back(replace
.substr(l
, r
-l
));
365 replacement
.push_back(replace
.substr(l
, r
-l
));
367 if(r
== (replace
.length()-1))
369 this->SetError("sub-command REGEX, mode REPLACE: "
370 "replace-expression ends in a backslash.");
373 if((replace
[r
+1] >= '0') && (replace
[r
+1] <= '9'))
375 replacement
.push_back(replace
[r
+1]-'0');
377 else if(replace
[r
+1] == 'n')
379 replacement
.push_back("\n");
381 else if(replace
[r
+1] == '\\')
383 replacement
.push_back("\\");
387 std::string e
= "sub-command REGEX, mode REPLACE: Unknown escape \"";
388 e
+= replace
.substr(r
, 2);
389 e
+= "\"in replace-expression.";
390 this->SetError(e
.c_str());
398 // Concatenate all the last arguments together.
399 std::string input
= args
[5];
400 for(unsigned int i
=6; i
< args
.size(); ++i
)
405 this->ClearMatches(this->Makefile
);
406 // Compile the regular expression.
407 cmsys::RegularExpression re
;
408 if(!re
.compile(regex
.c_str()))
411 "sub-command REGEX, mode REPLACE failed to compile regex \""+
413 this->SetError(e
.c_str());
417 // Scan through the input for all matches.
419 std::string::size_type base
= 0;
420 while(re
.find(input
.c_str()+base
))
422 this->StoreMatches(this->Makefile
, re
);
423 std::string::size_type l2
= re
.start();
424 std::string::size_type r
= re
.end();
426 // Concatenate the part of the input that was not matched.
427 output
+= input
.substr(base
, l2
);
429 // Make sure the match had some text.
432 std::string e
= "sub-command REGEX, mode REPLACE regex \""+
433 regex
+"\" matched an empty string.";
434 this->SetError(e
.c_str());
438 // Concatenate the replacement for the match.
439 for(unsigned int i
=0; i
< replacement
.size(); ++i
)
441 if(replacement
[i
].number
< 0)
443 // This is just a plain-text part of the replacement.
444 output
+= replacement
[i
].value
;
448 // Replace with part of the match.
449 int n
= replacement
[i
].number
;
450 std::string::size_type start
= re
.start(n
);
451 std::string::size_type end
= re
.end(n
);
452 std::string::size_type len
= input
.length()-base
;
453 if((start
!= std::string::npos
) && (end
!= std::string::npos
) &&
454 (start
<= len
) && (end
<= len
))
456 output
+= input
.substr(base
+start
, end
-start
);
461 "sub-command REGEX, mode REPLACE: replace expression \""+
462 replace
+"\" contains an out-of-range escape for regex \""+
464 this->SetError(e
.c_str());
470 // Move past the match.
474 // Concatenate the text after the last match.
475 output
+= input
.substr(base
, input
.length()-base
);
477 // Store the output in the provided variable.
478 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
482 //----------------------------------------------------------------------------
483 void cmStringCommand::ClearMatches(cmMakefile
* mf
)
485 for (unsigned int i
=0; i
<10; i
++)
488 sprintf(name
, "CMAKE_MATCH_%d", i
);
489 mf
->AddDefinition(name
, "");
493 //----------------------------------------------------------------------------
494 void cmStringCommand::StoreMatches(cmMakefile
* mf
,cmsys::RegularExpression
& re
)
496 for (unsigned int i
=0; i
<10; i
++)
499 sprintf(name
, "CMAKE_MATCH_%d", i
);
500 mf
->AddDefinition(name
, re
.match(i
).c_str());
504 //----------------------------------------------------------------------------
505 bool cmStringCommand::HandleCompareCommand(std::vector
<std::string
> const&
510 this->SetError("sub-command COMPARE requires a mode to be specified.");
513 std::string mode
= args
[1];
514 if((mode
== "EQUAL") || (mode
== "NOTEQUAL") ||
515 (mode
== "LESS") || (mode
== "GREATER"))
519 std::string e
= "sub-command COMPARE, mode ";
521 e
+= " needs at least 5 arguments total to command.";
522 this->SetError(e
.c_str());
526 const std::string
& left
= args
[2];
527 const std::string
& right
= args
[3];
528 const std::string
& outvar
= args
[4];
532 result
= (left
< right
);
534 else if(mode
== "GREATER")
536 result
= (left
> right
);
538 else if(mode
== "EQUAL")
540 result
= (left
== right
);
542 else // if(mode == "NOTEQUAL")
544 result
= !(left
== right
);
548 this->Makefile
->AddDefinition(outvar
.c_str(), "1");
552 this->Makefile
->AddDefinition(outvar
.c_str(), "0");
556 std::string e
= "sub-command COMPARE does not recognize mode "+mode
;
557 this->SetError(e
.c_str());
561 //----------------------------------------------------------------------------
562 bool cmStringCommand::HandleReplaceCommand(std::vector
<std::string
> const&
567 this->SetError("sub-command REPLACE requires four arguments.");
571 const std::string
& matchExpression
= args
[1];
572 const std::string
& replaceExpression
= args
[2];
573 const std::string
& variableName
= args
[3];
575 std::string input
= args
[4];
576 for(unsigned int i
=5; i
< args
.size(); ++i
)
581 cmsys::SystemTools::ReplaceString(input
, matchExpression
.c_str(),
582 replaceExpression
.c_str());
584 this->Makefile
->AddDefinition(variableName
.c_str(), input
.c_str());
588 //----------------------------------------------------------------------------
589 bool cmStringCommand::HandleSubstringCommand(std::vector
<std::string
> const&
594 this->SetError("sub-command REPLACE requires four arguments.");
598 const std::string
& stringValue
= args
[1];
599 int begin
= atoi(args
[2].c_str());
600 int end
= atoi(args
[3].c_str());
601 const std::string
& variableName
= args
[4];
603 size_t stringLength
= stringValue
.size();
604 int intStringLength
= static_cast<int>(stringLength
);
605 if ( begin
< 0 || begin
> intStringLength
)
607 cmOStringStream ostr
;
608 ostr
<< "begin index: " << begin
<< " is out of range 0 - "
610 this->SetError(ostr
.str().c_str());
613 int leftOverLength
= intStringLength
- begin
;
614 if ( end
< 0 || end
> leftOverLength
)
616 cmOStringStream ostr
;
617 ostr
<< "end index: " << end
<< " is out of range " << 0 << " - "
619 this->SetError(ostr
.str().c_str());
623 this->Makefile
->AddDefinition(variableName
.c_str(),
624 stringValue
.substr(begin
, end
).c_str());
628 //----------------------------------------------------------------------------
630 ::HandleLengthCommand(std::vector
<std::string
> const& args
)
634 this->SetError("sub-command LENGTH requires two arguments.");
638 const std::string
& stringValue
= args
[1];
639 const std::string
& variableName
= args
[2];
641 size_t length
= stringValue
.size();
643 sprintf(buffer
, "%d", static_cast<int>(length
));
645 this->Makefile
->AddDefinition(variableName
.c_str(), buffer
);
649 //----------------------------------------------------------------------------
650 bool cmStringCommand::HandleStripCommand(
651 std::vector
<std::string
> const& args
)
655 this->SetError("sub-command LENGTH requires two arguments.");
659 const std::string
& stringValue
= args
[1];
660 const std::string
& variableName
= args
[2];
661 size_t inStringLength
= stringValue
.size();
662 size_t startPos
= inStringLength
+ 1;
664 const char* ptr
= stringValue
.c_str();
666 for ( cc
= 0; cc
< inStringLength
; ++ cc
)
668 if ( !isspace(*ptr
) )
670 if ( startPos
> inStringLength
)
679 size_t outLength
= 0;
681 // if the input string didn't contain any non-space characters, return
683 if (startPos
> inStringLength
)
690 outLength
=endPos
- startPos
+ 1;
693 this->Makefile
->AddDefinition(variableName
.c_str(),
694 stringValue
.substr(startPos
, outLength
).c_str());
698 //----------------------------------------------------------------------------
700 ::HandleRandomCommand(std::vector
<std::string
> const& args
)
702 if(args
.size() < 2 || args
.size() == 3 || args
.size() == 5)
704 this->SetError("sub-command RANDOM requires at least one argument.");
709 const char cmStringCommandDefaultAlphabet
[] = "qwertyuiopasdfghjklzxcvbnm"
710 "QWERTYUIOPASDFGHJKLZXCVBNM"
712 std::string alphabet
;
714 if ( args
.size() > 3 )
717 size_t stopAt
= args
.size() - 2;
719 for ( ; i
< stopAt
; ++i
)
721 if ( args
[i
] == "LENGTH" )
724 length
= atoi(args
[i
].c_str());
726 else if ( args
[i
] == "ALPHABET" )
733 if ( !alphabet
.size() )
735 alphabet
= cmStringCommandDefaultAlphabet
;
738 double sizeofAlphabet
= alphabet
.size();
739 if ( sizeofAlphabet
< 1 )
741 this->SetError("sub-command RANDOM invoked with bad alphabet.");
746 this->SetError("sub-command RANDOM invoked with bad length.");
749 const std::string
& variableName
= args
[args
.size()-1];
751 std::vector
<char> result
;
752 srand((int)time(NULL
));
753 const char* alphaPtr
= alphabet
.c_str();
755 for ( cc
= 0; cc
< length
; cc
++ )
757 int idx
=(int) (sizeofAlphabet
* rand()/(RAND_MAX
+1.0));
758 result
.push_back(*(alphaPtr
+ idx
));
762 this->Makefile
->AddDefinition(variableName
.c_str(), &*result
.begin());