1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmStringCommand.cxx,v $
6 <<<<<<< cmStringCommand.cxx
7 Date: $Date: 2008/01/23 15:27:59 $
8 Version: $Revision: 1.27 $
10 Date: $Date: 2008-08-26 16:54:06 $
11 Version: $Revision: 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
30 //----------------------------------------------------------------------------
32 ::InitialPass(std::vector
<std::string
> const& args
, cmExecutionStatus
&)
36 this->SetError("must be called with at least one argument.");
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());
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");
101 std::string outvar
= args
[2];
106 output
= cmSystemTools::UpperCase(args
[1]);
110 output
= cmSystemTools::LowerCase(args
[1]);
113 // Store the output in the provided variable.
114 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
118 //----------------------------------------------------------------------------
119 bool cmStringCommand::HandleAsciiCommand(std::vector
<std::string
> const& args
)
121 if ( args
.size() < 3 )
123 this->SetError("No output variable specified");
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
);
138 std::string error
= "Character with code ";
140 error
+= " does not exist.";
141 this->SetError(error
.c_str());
145 // Store the output in the provided variable.
146 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
150 //----------------------------------------------------------------------------
151 bool cmStringCommand::HandleConfigureCommand(
152 std::vector
<std::string
> const& args
)
154 if ( args
.size() < 2 )
156 this->SetError("No input string specified.");
159 else if ( args
.size() < 3 )
161 this->SetError("No output variable specified.");
166 bool escapeQuotes
= false;
168 for(unsigned int i
= 3; i
< args
.size(); ++i
)
170 if(args
[i
] == "@ONLY")
174 else if(args
[i
] == "ESCAPE_QUOTES")
181 err
<< "Unrecognized argument \"" << args
[i
] << "\"";
182 this->SetError(err
.str().c_str());
187 // Configure the string.
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());
197 //----------------------------------------------------------------------------
198 bool cmStringCommand::HandleRegexCommand(std::vector
<std::string
> const& args
)
202 this->SetError("sub-command REGEX requires a mode to be specified.");
205 std::string mode
= args
[1];
210 this->SetError("sub-command REGEX, mode MATCH needs "
211 "at least 5 arguments total to command.");
214 return this->RegexMatch(args
);
216 else if(mode
== "MATCHALL")
220 this->SetError("sub-command REGEX, mode MATCHALL needs "
221 "at least 5 arguments total to command.");
224 return this->RegexMatchAll(args
);
226 else if(mode
== "REPLACE")
230 this->SetError("sub-command REGEX, mode REPLACE needs "
231 "at least 6 arguments total to command.");
234 return this->RegexReplace(args
);
237 std::string e
= "sub-command REGEX does not recognize mode "+mode
;
238 this->SetError(e
.c_str());
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
)
257 this->ClearMatches(this->Makefile
);
258 // Compile the regular expression.
259 cmsys::RegularExpression re
;
260 if(!re
.compile(regex
.c_str()))
263 "sub-command REGEX, mode MATCH failed to compile regex \""+regex
+"\".";
264 this->SetError(e
.c_str());
268 // Scan through the input for all matches.
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();
278 "sub-command REGEX, mode MATCH regex \""+regex
+
279 "\" matched an empty string.";
280 this->SetError(e
.c_str());
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());
291 //----------------------------------------------------------------------------
292 bool cmStringCommand::RegexMatchAll(std::vector
<std::string
> const& args
)
294 //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
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
)
306 this->ClearMatches(this->Makefile
);
307 // Compile the regular expression.
308 cmsys::RegularExpression re
;
309 if(!re
.compile(regex
.c_str()))
312 "sub-command REGEX, mode MATCHALL failed to compile regex \""+
314 this->SetError(e
.c_str());
318 // Scan through the input for all matches.
320 const char* p
= input
.c_str();
323 this->StoreMatches(this->Makefile
, re
);
324 std::string::size_type l
= re
.start();
325 std::string::size_type r
= re
.end();
328 std::string e
= "sub-command REGEX, mode MATCHALL regex \""+
329 regex
+"\" matched an empty string.";
330 this->SetError(e
.c_str());
333 if(output
.length() > 0)
337 output
+= std::string(p
+l
, r
-l
);
341 // Store the output in the provided variable.
342 this->Makefile
->AddDefinition(outvar
.c_str(), output
.c_str());
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
));
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.");
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("\\");
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());
403 // Concatenate all the last arguments together.
404 std::string input
= args
[5];
405 for(unsigned int i
=6; i
< args
.size(); ++i
)
410 this->ClearMatches(this->Makefile
);
411 // Compile the regular expression.
412 cmsys::RegularExpression re
;
413 if(!re
.compile(regex
.c_str()))
416 "sub-command REGEX, mode REPLACE failed to compile regex \""+
418 this->SetError(e
.c_str());
422 // Scan through the input for all matches.
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.
437 std::string e
= "sub-command REGEX, mode REPLACE regex \""+
438 regex
+"\" matched an empty string.";
439 this->SetError(e
.c_str());
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
;
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
);
466 "sub-command REGEX, mode REPLACE: replace expression \""+
467 replace
+"\" contains an out-of-range escape for regex \""+
469 this->SetError(e
.c_str());
475 // Move past the match.
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());
487 //----------------------------------------------------------------------------
488 void cmStringCommand::ClearMatches(cmMakefile
* mf
)
490 for (unsigned int i
=0; i
<10; i
++)
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
++)
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&
515 this->SetError("sub-command COMPARE requires a mode to be specified.");
518 std::string mode
= args
[1];
519 if((mode
== "EQUAL") || (mode
== "NOTEQUAL") ||
520 (mode
== "LESS") || (mode
== "GREATER"))
524 std::string e
= "sub-command COMPARE, mode ";
526 e
+= " needs at least 5 arguments total to command.";
527 this->SetError(e
.c_str());
531 const std::string
& left
= args
[2];
532 const std::string
& right
= args
[3];
533 const std::string
& outvar
= args
[4];
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
);
553 this->Makefile
->AddDefinition(outvar
.c_str(), "1");
557 this->Makefile
->AddDefinition(outvar
.c_str(), "0");
561 std::string e
= "sub-command COMPARE does not recognize mode "+mode
;
562 this->SetError(e
.c_str());
566 //----------------------------------------------------------------------------
567 bool cmStringCommand::HandleReplaceCommand(std::vector
<std::string
> const&
572 this->SetError("sub-command REPLACE requires four arguments.");
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
)
586 cmsys::SystemTools::ReplaceString(input
, matchExpression
.c_str(),
587 replaceExpression
.c_str());
589 this->Makefile
->AddDefinition(variableName
.c_str(), input
.c_str());
593 //----------------------------------------------------------------------------
594 bool cmStringCommand::HandleSubstringCommand(std::vector
<std::string
> const&
599 this->SetError("sub-command REPLACE requires four arguments.");
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 - "
615 this->SetError(ostr
.str().c_str());
618 int leftOverLength
= intStringLength
- begin
;
619 if ( end
< 0 || end
> leftOverLength
)
621 cmOStringStream ostr
;
622 ostr
<< "end index: " << end
<< " is out of range " << 0 << " - "
624 this->SetError(ostr
.str().c_str());
628 this->Makefile
->AddDefinition(variableName
.c_str(),
629 stringValue
.substr(begin
, end
).c_str());
633 //----------------------------------------------------------------------------
635 ::HandleLengthCommand(std::vector
<std::string
> const& args
)
639 this->SetError("sub-command LENGTH requires two arguments.");
643 const std::string
& stringValue
= args
[1];
644 const std::string
& variableName
= args
[2];
646 size_t length
= stringValue
.size();
648 sprintf(buffer
, "%d", static_cast<int>(length
));
650 this->Makefile
->AddDefinition(variableName
.c_str(), buffer
);
654 //----------------------------------------------------------------------------
655 bool cmStringCommand::HandleStripCommand(
656 std::vector
<std::string
> const& args
)
660 this->SetError("sub-command LENGTH requires two arguments.");
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;
669 const char* ptr
= stringValue
.c_str();
671 for ( cc
= 0; cc
< inStringLength
; ++ cc
)
673 if ( !isspace(*ptr
) )
675 if ( startPos
> inStringLength
)
684 size_t outLength
= 0;
686 // if the input string didn't contain any non-space characters, return
688 if (startPos
> inStringLength
)
695 outLength
=endPos
- startPos
+ 1;
698 this->Makefile
->AddDefinition(variableName
.c_str(),
699 stringValue
.substr(startPos
, outLength
).c_str());
703 //----------------------------------------------------------------------------
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.");
714 const char cmStringCommandDefaultAlphabet
[] = "qwertyuiopasdfghjklzxcvbnm"
715 "QWERTYUIOPASDFGHJKLZXCVBNM"
717 std::string alphabet
;
719 if ( args
.size() > 3 )
722 size_t stopAt
= args
.size() - 2;
724 for ( ; i
< stopAt
; ++i
)
726 if ( args
[i
] == "LENGTH" )
729 length
= atoi(args
[i
].c_str());
731 else if ( args
[i
] == "ALPHABET" )
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.");
751 this->SetError("sub-command RANDOM invoked with bad length.");
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();
760 for ( cc
= 0; cc
< length
; cc
++ )
762 int idx
=(int) (sizeofAlphabet
* rand()/(RAND_MAX
+1.0));
763 result
.push_back(*(alphaPtr
+ idx
));
767 this->Makefile
->AddDefinition(variableName
.c_str(), &*result
.begin());