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