Imported Upstream version 2.8.12.2
[platform/upstream/cmake.git] / Source / cmStringCommand.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 "cmStringCommand.h"
13 #include "cmCryptoHash.h"
14
15 #include <cmsys/RegularExpression.hxx>
16 #include <cmsys/SystemTools.hxx>
17
18 #include <stdlib.h> // required for atoi
19 #include <ctype.h>
20 #include <time.h>
21
22 #include <cmTimestamp.h>
23
24 //----------------------------------------------------------------------------
25 bool cmStringCommand
26 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
27 {
28   if(args.size() < 1)
29     {
30     this->SetError("must be called with at least one argument.");
31     return false;
32     }
33
34   const std::string &subCommand = args[0];
35   if(subCommand == "REGEX")
36     {
37     return this->HandleRegexCommand(args);
38     }
39   else if(subCommand == "REPLACE")
40     {
41     return this->HandleReplaceCommand(args);
42     }
43   else if ( subCommand == "MD5" ||
44             subCommand == "SHA1" ||
45             subCommand == "SHA224" ||
46             subCommand == "SHA256" ||
47             subCommand == "SHA384" ||
48             subCommand == "SHA512" )
49     {
50     return this->HandleHashCommand(args);
51     }
52   else if(subCommand == "TOLOWER")
53     {
54     return this->HandleToUpperLowerCommand(args, false);
55     }
56   else if(subCommand == "TOUPPER")
57     {
58     return this->HandleToUpperLowerCommand(args, true);
59     }
60   else if(subCommand == "COMPARE")
61     {
62     return this->HandleCompareCommand(args);
63     }
64   else if(subCommand == "ASCII")
65     {
66     return this->HandleAsciiCommand(args);
67     }
68   else if(subCommand == "CONFIGURE")
69     {
70     return this->HandleConfigureCommand(args);
71     }
72   else if(subCommand == "LENGTH")
73     {
74     return this->HandleLengthCommand(args);
75     }
76   else if(subCommand == "SUBSTRING")
77     {
78     return this->HandleSubstringCommand(args);
79     }
80   else if(subCommand == "STRIP")
81     {
82     return this->HandleStripCommand(args);
83     }
84   else if(subCommand == "RANDOM")
85     {
86     return this->HandleRandomCommand(args);
87     }
88   else if(subCommand == "FIND")
89     {
90     return this->HandleFindCommand(args);
91     }
92   else if(subCommand == "TIMESTAMP")
93     {
94     return this->HandleTimestampCommand(args);
95     }
96   else if(subCommand == "MAKE_C_IDENTIFIER")
97     {
98     return this->HandleMakeCIdentifierCommand(args);
99     }
100
101   std::string e = "does not recognize sub-command "+subCommand;
102   this->SetError(e.c_str());
103   return false;
104 }
105
106 //----------------------------------------------------------------------------
107 bool cmStringCommand::HandleHashCommand(std::vector<std::string> const& args)
108 {
109 #if defined(CMAKE_BUILD_WITH_CMAKE)
110   if(args.size() != 3)
111     {
112     cmOStringStream e;
113     e << args[0] << " requires an output variable and an input string";
114     this->SetError(e.str().c_str());
115     return false;
116     }
117
118   cmsys::auto_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0].c_str()));
119   if(hash.get())
120     {
121     std::string out = hash->HashString(args[2].c_str());
122     this->Makefile->AddDefinition(args[1].c_str(), out.c_str());
123     return true;
124     }
125   return false;
126 #else
127   cmOStringStream e;
128   e << args[0] << " not available during bootstrap";
129   this->SetError(e.str().c_str());
130   return false;
131 #endif
132 }
133
134 //----------------------------------------------------------------------------
135 bool cmStringCommand::HandleToUpperLowerCommand(
136   std::vector<std::string> const& args, bool toUpper)
137 {
138   if ( args.size() < 3 )
139     {
140     this->SetError("no output variable specified");
141     return false;
142     }
143
144   std::string outvar = args[2];
145   std::string output;
146
147   if (toUpper)
148     {
149     output = cmSystemTools::UpperCase(args[1]);
150     }
151   else
152     {
153     output = cmSystemTools::LowerCase(args[1]);
154     }
155
156   // Store the output in the provided variable.
157   this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
158   return true;
159 }
160
161 //----------------------------------------------------------------------------
162 bool cmStringCommand::HandleAsciiCommand(std::vector<std::string> const& args)
163 {
164   if ( args.size() < 3 )
165     {
166     this->SetError("No output variable specified");
167     return false;
168     }
169   std::string::size_type cc;
170   std::string outvar = args[args.size()-1];
171   std::string output = "";
172   for ( cc = 1; cc < args.size()-1; cc ++ )
173     {
174     int ch = atoi(args[cc].c_str());
175     if ( ch > 0 && ch < 256 )
176       {
177       output += static_cast<char>(ch);
178       }
179     else
180       {
181       std::string error = "Character with code ";
182       error += args[cc];
183       error += " does not exist.";
184       this->SetError(error.c_str());
185       return false;
186       }
187     }
188   // Store the output in the provided variable.
189   this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
190   return true;
191 }
192
193 //----------------------------------------------------------------------------
194 bool cmStringCommand::HandleConfigureCommand(
195   std::vector<std::string> const& args)
196 {
197   if ( args.size() < 2 )
198     {
199     this->SetError("No input string specified.");
200     return false;
201     }
202   else if ( args.size() < 3 )
203     {
204     this->SetError("No output variable specified.");
205     return false;
206     }
207
208   // Parse options.
209   bool escapeQuotes = false;
210   bool atOnly = false;
211   for(unsigned int i = 3; i < args.size(); ++i)
212     {
213     if(args[i] == "@ONLY")
214       {
215       atOnly = true;
216       }
217     else if(args[i] == "ESCAPE_QUOTES")
218       {
219       escapeQuotes = true;
220       }
221     else
222       {
223       cmOStringStream err;
224       err << "Unrecognized argument \"" << args[i] << "\"";
225       this->SetError(err.str().c_str());
226       return false;
227       }
228     }
229
230   // Configure the string.
231   std::string output;
232   this->Makefile->ConfigureString(args[1], output, atOnly, escapeQuotes);
233
234   // Store the output in the provided variable.
235   this->Makefile->AddDefinition(args[2].c_str(), output.c_str());
236
237   return true;
238 }
239
240 //----------------------------------------------------------------------------
241 bool cmStringCommand::HandleRegexCommand(std::vector<std::string> const& args)
242 {
243   if(args.size() < 2)
244     {
245     this->SetError("sub-command REGEX requires a mode to be specified.");
246     return false;
247     }
248   std::string mode = args[1];
249   if(mode == "MATCH")
250     {
251     if(args.size() < 5)
252       {
253       this->SetError("sub-command REGEX, mode MATCH needs "
254                      "at least 5 arguments total to command.");
255       return false;
256       }
257     return this->RegexMatch(args);
258     }
259   else if(mode == "MATCHALL")
260     {
261     if(args.size() < 5)
262       {
263       this->SetError("sub-command REGEX, mode MATCHALL needs "
264                      "at least 5 arguments total to command.");
265       return false;
266       }
267     return this->RegexMatchAll(args);
268     }
269   else if(mode == "REPLACE")
270     {
271     if(args.size() < 6)
272       {
273       this->SetError("sub-command REGEX, mode REPLACE needs "
274                      "at least 6 arguments total to command.");
275       return false;
276       }
277     return this->RegexReplace(args);
278     }
279
280   std::string e = "sub-command REGEX does not recognize mode "+mode;
281   this->SetError(e.c_str());
282   return false;
283 }
284
285 //----------------------------------------------------------------------------
286 bool cmStringCommand::RegexMatch(std::vector<std::string> const& args)
287 {
288   //"STRING(REGEX MATCH <regular_expression> <output variable>
289   // <input> [<input>...])\n";
290   std::string regex = args[2];
291   std::string outvar = args[3];
292
293   // Concatenate all the last arguments together.
294   std::string input = args[4];
295   for(unsigned int i=5; i < args.size(); ++i)
296     {
297     input += args[i];
298     }
299
300   this->ClearMatches(this->Makefile);
301   // Compile the regular expression.
302   cmsys::RegularExpression re;
303   if(!re.compile(regex.c_str()))
304     {
305     std::string e =
306       "sub-command REGEX, mode MATCH failed to compile regex \""+regex+"\".";
307     this->SetError(e.c_str());
308     return false;
309     }
310
311   // Scan through the input for all matches.
312   std::string output;
313   if(re.find(input.c_str()))
314     {
315     this->StoreMatches(this->Makefile, re);
316     std::string::size_type l = re.start();
317     std::string::size_type r = re.end();
318     if(r-l == 0)
319       {
320       std::string e =
321         "sub-command REGEX, mode MATCH regex \""+regex+
322         "\" matched an empty string.";
323       this->SetError(e.c_str());
324       return false;
325       }
326     output = input.substr(l, r-l);
327     }
328
329   // Store the output in the provided variable.
330   this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
331   return true;
332 }
333
334 //----------------------------------------------------------------------------
335 bool cmStringCommand::RegexMatchAll(std::vector<std::string> const& args)
336 {
337   //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
338   // [<input>...])\n";
339   std::string regex = args[2];
340   std::string outvar = args[3];
341
342   // Concatenate all the last arguments together.
343   std::string input = args[4];
344   for(unsigned int i=5; i < args.size(); ++i)
345     {
346     input += args[i];
347     }
348
349   this->ClearMatches(this->Makefile);
350   // Compile the regular expression.
351   cmsys::RegularExpression re;
352   if(!re.compile(regex.c_str()))
353     {
354     std::string e =
355       "sub-command REGEX, mode MATCHALL failed to compile regex \""+
356       regex+"\".";
357     this->SetError(e.c_str());
358     return false;
359     }
360
361   // Scan through the input for all matches.
362   std::string output;
363   const char* p = input.c_str();
364   while(re.find(p))
365     {
366     this->StoreMatches(this->Makefile, re);
367     std::string::size_type l = re.start();
368     std::string::size_type r = re.end();
369     if(r-l == 0)
370       {
371       std::string e = "sub-command REGEX, mode MATCHALL regex \""+
372         regex+"\" matched an empty string.";
373       this->SetError(e.c_str());
374       return false;
375       }
376     if(output.length() > 0)
377       {
378       output += ";";
379       }
380     output += std::string(p+l, r-l);
381     p += r;
382     }
383
384   // Store the output in the provided variable.
385   this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
386   return true;
387 }
388
389 //----------------------------------------------------------------------------
390 bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
391 {
392   //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
393   // <output variable> <input> [<input>...])\n"
394   std::string regex = args[2];
395   std::string replace = args[3];
396   std::string outvar = args[4];
397
398   // Pull apart the replace expression to find the escaped [0-9] values.
399   std::vector<RegexReplacement> replacement;
400   std::string::size_type l = 0;
401   while(l < replace.length())
402     {
403     std::string::size_type r = replace.find("\\", l);
404     if(r == std::string::npos)
405       {
406       r = replace.length();
407       replacement.push_back(replace.substr(l, r-l));
408       }
409     else
410       {
411       if(r-l > 0)
412         {
413         replacement.push_back(replace.substr(l, r-l));
414         }
415       if(r == (replace.length()-1))
416         {
417         this->SetError("sub-command REGEX, mode REPLACE: "
418                        "replace-expression ends in a backslash.");
419         return false;
420         }
421       if((replace[r+1] >= '0') && (replace[r+1] <= '9'))
422         {
423         replacement.push_back(replace[r+1]-'0');
424         }
425       else if(replace[r+1] == 'n')
426         {
427         replacement.push_back("\n");
428         }
429       else if(replace[r+1] == '\\')
430         {
431         replacement.push_back("\\");
432         }
433       else
434         {
435         std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \"";
436         e += replace.substr(r, 2);
437         e += "\" in replace-expression.";
438         this->SetError(e.c_str());
439         return false;
440         }
441       r += 2;
442       }
443     l = r;
444     }
445
446   // Concatenate all the last arguments together.
447   std::string input = args[5];
448   for(unsigned int i=6; i < args.size(); ++i)
449     {
450     input += args[i];
451     }
452
453   this->ClearMatches(this->Makefile);
454   // Compile the regular expression.
455   cmsys::RegularExpression re;
456   if(!re.compile(regex.c_str()))
457     {
458     std::string e =
459       "sub-command REGEX, mode REPLACE failed to compile regex \""+
460       regex+"\".";
461     this->SetError(e.c_str());
462     return false;
463     }
464
465   // Scan through the input for all matches.
466   std::string output;
467   std::string::size_type base = 0;
468   while(re.find(input.c_str()+base))
469     {
470     this->StoreMatches(this->Makefile, re);
471     std::string::size_type l2 = re.start();
472     std::string::size_type r = re.end();
473
474     // Concatenate the part of the input that was not matched.
475     output += input.substr(base, l2);
476
477     // Make sure the match had some text.
478     if(r-l2 == 0)
479       {
480       std::string e = "sub-command REGEX, mode REPLACE regex \""+
481         regex+"\" matched an empty string.";
482       this->SetError(e.c_str());
483       return false;
484       }
485
486     // Concatenate the replacement for the match.
487     for(unsigned int i=0; i < replacement.size(); ++i)
488       {
489       if(replacement[i].number < 0)
490         {
491         // This is just a plain-text part of the replacement.
492         output += replacement[i].value;
493         }
494       else
495         {
496         // Replace with part of the match.
497         int n = replacement[i].number;
498         std::string::size_type start = re.start(n);
499         std::string::size_type end = re.end(n);
500         std::string::size_type len = input.length()-base;
501         if((start != std::string::npos) && (end != std::string::npos) &&
502            (start <= len) && (end <= len))
503           {
504           output += input.substr(base+start, end-start);
505           }
506         else
507           {
508           std::string e =
509             "sub-command REGEX, mode REPLACE: replace expression \""+
510             replace+"\" contains an out-of-range escape for regex \""+
511             regex+"\".";
512           this->SetError(e.c_str());
513           return false;
514           }
515         }
516       }
517
518     // Move past the match.
519     base += r;
520     }
521
522   // Concatenate the text after the last match.
523   output += input.substr(base, input.length()-base);
524
525   // Store the output in the provided variable.
526   this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
527   return true;
528 }
529
530 //----------------------------------------------------------------------------
531 void cmStringCommand::ClearMatches(cmMakefile* mf)
532 {
533   for (unsigned int i=0; i<10; i++)
534     {
535     char name[128];
536     sprintf(name, "CMAKE_MATCH_%d", i);
537     const char* s = mf->GetDefinition(name);
538     if(s && *s != 0)
539       {
540       mf->AddDefinition(name, "");
541       mf->MarkVariableAsUsed(name);
542       }
543     }
544 }
545
546 //----------------------------------------------------------------------------
547 void cmStringCommand::StoreMatches(cmMakefile* mf,cmsys::RegularExpression& re)
548 {
549   for (unsigned int i=0; i<10; i++)
550     {
551     std::string m = re.match(i);
552     if(m.size() > 0)
553       {
554       char name[128];
555       sprintf(name, "CMAKE_MATCH_%d", i);
556       mf->AddDefinition(name, re.match(i).c_str());
557       mf->MarkVariableAsUsed(name);
558       }
559     }
560 }
561
562 //----------------------------------------------------------------------------
563 bool cmStringCommand::HandleFindCommand(std::vector<std::string> const&
564                                            args)
565 {
566   // check if all required parameters were passed
567   if(args.size() < 4 || args.size() > 5)
568     {
569     this->SetError("sub-command FIND requires 3 or 4 parameters.");
570     return false;
571     }
572
573   // check if the reverse flag was set or not
574   bool reverseMode = false;
575   if(args.size() == 5 && args[4] == "REVERSE")
576     {
577     reverseMode = true;
578     }
579
580   // if we have 5 arguments the last one must be REVERSE
581   if(args.size() == 5 && args[4] != "REVERSE")
582     {
583     this->SetError("sub-command FIND: unknown last parameter");
584     return false;
585     }
586
587   // local parameter names.
588   const std::string& sstring = args[1];
589   const std::string& schar = args[2];
590   const std::string& outvar = args[3];
591
592   // ensure that the user cannot accidentally specify REVERSE as a variable
593   if(outvar == "REVERSE")
594     {
595     this->SetError("sub-command FIND does not allow to select REVERSE as "
596                    "the output variable.  "
597                    "Maybe you missed the actual output variable?");
598     return false;
599     }
600
601   // try to find the character and return its position
602   size_t pos;
603   if(!reverseMode)
604     {
605     pos = sstring.find(schar);
606     }
607   else
608     {
609     pos = sstring.rfind(schar);
610     }
611   if(std::string::npos != pos)
612     {
613     cmOStringStream s;
614     s << pos;
615     this->Makefile->AddDefinition(outvar.c_str(), s.str().c_str());
616     return true;
617     }
618
619   // the character was not found, but this is not really an error
620   this->Makefile->AddDefinition(outvar.c_str(), "-1");
621   return true;
622 }
623
624 //----------------------------------------------------------------------------
625 bool cmStringCommand::HandleCompareCommand(std::vector<std::string> const&
626                                            args)
627 {
628   if(args.size() < 2)
629     {
630     this->SetError("sub-command COMPARE requires a mode to be specified.");
631     return false;
632     }
633   std::string mode = args[1];
634   if((mode == "EQUAL") || (mode == "NOTEQUAL") ||
635      (mode == "LESS") || (mode == "GREATER"))
636     {
637     if(args.size() < 5)
638       {
639       std::string e = "sub-command COMPARE, mode ";
640       e += mode;
641       e += " needs at least 5 arguments total to command.";
642       this->SetError(e.c_str());
643       return false;
644       }
645
646     const std::string& left = args[2];
647     const std::string& right = args[3];
648     const std::string& outvar = args[4];
649     bool result;
650     if(mode == "LESS")
651       {
652       result = (left < right);
653       }
654     else if(mode == "GREATER")
655       {
656       result = (left > right);
657       }
658     else if(mode == "EQUAL")
659       {
660       result = (left == right);
661       }
662     else // if(mode == "NOTEQUAL")
663       {
664       result = !(left == right);
665       }
666     if(result)
667       {
668       this->Makefile->AddDefinition(outvar.c_str(), "1");
669       }
670     else
671       {
672       this->Makefile->AddDefinition(outvar.c_str(), "0");
673       }
674     return true;
675     }
676   std::string e = "sub-command COMPARE does not recognize mode "+mode;
677   this->SetError(e.c_str());
678   return false;
679 }
680
681 //----------------------------------------------------------------------------
682 bool cmStringCommand::HandleReplaceCommand(std::vector<std::string> const&
683                                            args)
684 {
685   if(args.size() < 5)
686     {
687     this->SetError("sub-command REPLACE requires at least four arguments.");
688     return false;
689     }
690
691   const std::string& matchExpression = args[1];
692   const std::string& replaceExpression = args[2];
693   const std::string& variableName = args[3];
694
695   std::string input = args[4];
696   for(unsigned int i=5; i < args.size(); ++i)
697     {
698     input += args[i];
699     }
700
701   cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
702                                     replaceExpression.c_str());
703
704   this->Makefile->AddDefinition(variableName.c_str(), input.c_str());
705   return true;
706 }
707
708 //----------------------------------------------------------------------------
709 bool cmStringCommand::HandleSubstringCommand(std::vector<std::string> const&
710                                              args)
711 {
712   if(args.size() != 5)
713     {
714     this->SetError("sub-command SUBSTRING requires four arguments.");
715     return false;
716     }
717
718   const std::string& stringValue = args[1];
719   int begin = atoi(args[2].c_str());
720   int end = atoi(args[3].c_str());
721   const std::string& variableName = args[4];
722
723   size_t stringLength = stringValue.size();
724   int intStringLength = static_cast<int>(stringLength);
725   if ( begin < 0 || begin > intStringLength )
726     {
727     cmOStringStream ostr;
728     ostr << "begin index: " << begin << " is out of range 0 - "
729          << stringLength;
730     this->SetError(ostr.str().c_str());
731     return false;
732     }
733   int leftOverLength = intStringLength - begin;
734   if ( end < -1 || end > leftOverLength )
735     {
736     cmOStringStream ostr;
737     ostr << "end index: " << end << " is out of range -1 - "
738          << leftOverLength;
739     this->SetError(ostr.str().c_str());
740     return false;
741     }
742
743   this->Makefile->AddDefinition(variableName.c_str(),
744                                 stringValue.substr(begin, end).c_str());
745   return true;
746 }
747
748 //----------------------------------------------------------------------------
749 bool cmStringCommand
750 ::HandleLengthCommand(std::vector<std::string> const& args)
751 {
752   if(args.size() != 3)
753     {
754     this->SetError("sub-command LENGTH requires two arguments.");
755     return false;
756     }
757
758   const std::string& stringValue = args[1];
759   const std::string& variableName = args[2];
760
761   size_t length = stringValue.size();
762   char buffer[1024];
763   sprintf(buffer, "%d", static_cast<int>(length));
764
765   this->Makefile->AddDefinition(variableName.c_str(), buffer);
766   return true;
767 }
768
769 //----------------------------------------------------------------------------
770 bool cmStringCommand
771 ::HandleMakeCIdentifierCommand(std::vector<std::string> const& args)
772 {
773   if(args.size() != 3)
774     {
775     this->SetError("sub-command MAKE_C_IDENTIFIER requires two arguments.");
776     return false;
777     }
778
779   const std::string& input = args[1];
780   const std::string& variableName = args[2];
781
782   this->Makefile->AddDefinition(variableName.c_str(),
783                       cmSystemTools::MakeCidentifier(input.c_str()).c_str());
784   return true;
785 }
786
787 //----------------------------------------------------------------------------
788 bool cmStringCommand::HandleStripCommand(
789   std::vector<std::string> const& args)
790 {
791  if(args.size() != 3)
792     {
793     this->SetError("sub-command STRIP requires two arguments.");
794     return false;
795     }
796
797   const std::string& stringValue = args[1];
798   const std::string& variableName = args[2];
799   size_t inStringLength = stringValue.size();
800   size_t startPos = inStringLength + 1;
801   size_t endPos = 0;
802   const char* ptr = stringValue.c_str();
803   size_t cc;
804   for ( cc = 0; cc < inStringLength; ++ cc )
805     {
806     if ( !isspace(*ptr) )
807       {
808       if ( startPos > inStringLength )
809         {
810         startPos = cc;
811         }
812       endPos = cc;
813       }
814     ++ ptr;
815     }
816
817   size_t outLength = 0;
818
819   // if the input string didn't contain any non-space characters, return
820   // an empty string
821   if (startPos > inStringLength)
822     {
823     outLength = 0;
824     startPos = 0;
825     }
826   else
827     {
828     outLength=endPos - startPos + 1;
829     }
830
831   this->Makefile->AddDefinition(variableName.c_str(),
832     stringValue.substr(startPos, outLength).c_str());
833   return true;
834 }
835
836 //----------------------------------------------------------------------------
837 bool cmStringCommand
838 ::HandleRandomCommand(std::vector<std::string> const& args)
839 {
840   if(args.size() < 2 || args.size() == 3 || args.size() == 5)
841     {
842     this->SetError("sub-command RANDOM requires at least one argument.");
843     return false;
844     }
845
846   static bool seeded = false;
847   bool force_seed = false;
848   unsigned int seed = 0;
849   int length = 5;
850   const char cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
851     "QWERTYUIOPASDFGHJKLZXCVBNM"
852     "0123456789";
853   std::string alphabet;
854
855   if ( args.size() > 3 )
856     {
857     size_t i = 1;
858     size_t stopAt = args.size() - 2;
859
860     for ( ; i < stopAt; ++i )
861       {
862       if ( args[i] == "LENGTH" )
863         {
864         ++i;
865         length = atoi(args[i].c_str());
866         }
867       else if ( args[i] == "ALPHABET" )
868         {
869         ++i;
870         alphabet = args[i];
871         }
872       else if ( args[i] == "RANDOM_SEED" )
873         {
874         ++i;
875         seed = static_cast<unsigned int>(atoi(args[i].c_str()));
876         force_seed = true;
877         }
878       }
879     }
880   if ( !alphabet.size() )
881     {
882     alphabet = cmStringCommandDefaultAlphabet;
883     }
884
885   double sizeofAlphabet = static_cast<double>(alphabet.size());
886   if ( sizeofAlphabet < 1 )
887     {
888     this->SetError("sub-command RANDOM invoked with bad alphabet.");
889     return false;
890     }
891   if ( length < 1 )
892     {
893     this->SetError("sub-command RANDOM invoked with bad length.");
894     return false;
895     }
896   const std::string& variableName = args[args.size()-1];
897
898   std::vector<char> result;
899
900   if (!seeded || force_seed)
901     {
902     seeded = true;
903     srand(force_seed? seed : cmSystemTools::RandomSeed());
904     }
905
906   const char* alphaPtr = alphabet.c_str();
907   int cc;
908   for ( cc = 0; cc < length; cc ++ )
909     {
910     int idx=(int) (sizeofAlphabet* rand()/(RAND_MAX+1.0));
911     result.push_back(*(alphaPtr + idx));
912     }
913   result.push_back(0);
914
915   this->Makefile->AddDefinition(variableName.c_str(), &*result.begin());
916   return true;
917 }
918
919 //----------------------------------------------------------------------------
920 bool cmStringCommand
921 ::HandleTimestampCommand(std::vector<std::string> const& args)
922 {
923   if(args.size() < 2)
924     {
925     this->SetError("sub-command TIMESTAMP requires at least one argument.");
926     return false;
927     }
928   else if(args.size() > 4)
929     {
930     this->SetError("sub-command TIMESTAMP takes at most three arguments.");
931     return false;
932     }
933
934   unsigned int argsIndex = 1;
935
936   const std::string &outputVariable = args[argsIndex++];
937
938   std::string formatString;
939   if(args.size() > argsIndex && args[argsIndex] != "UTC")
940     {
941     formatString = args[argsIndex++];
942     }
943
944   bool utcFlag = false;
945   if(args.size() > argsIndex)
946     {
947     if(args[argsIndex] == "UTC")
948       {
949       utcFlag = true;
950       }
951     else
952       {
953       std::string e = " TIMESTAMP sub-command does not recognize option " +
954           args[argsIndex] + ".";
955       this->SetError(e.c_str());
956       return false;
957       }
958     }
959
960   cmTimestamp timestamp;
961   std::string result = timestamp.CurrentTime(formatString, utcFlag);
962   this->Makefile->AddDefinition(outputVariable.c_str(), result.c_str());
963
964   return true;
965 }