Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmFileCommand.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 "cmFileCommand.h"
13 #include "cmake.h"
14 #include "cmHexFileConverter.h"
15 #include "cmInstallType.h"
16 #include "cmFileTimeComparison.h"
17 #include "cmCryptoHash.h"
18
19 #if defined(CMAKE_BUILD_WITH_CMAKE)
20 #include "cm_curl.h"
21 #endif
22
23 #undef GetCurrentDirectory
24 #include <sys/types.h>
25 #include <sys/stat.h>
26
27 #include <cmsys/auto_ptr.hxx>
28 #include <cmsys/Directory.hxx>
29 #include <cmsys/Glob.hxx>
30 #include <cmsys/RegularExpression.hxx>
31
32 // Table of permissions flags.
33 #if defined(_WIN32) && !defined(__CYGWIN__)
34 static mode_t mode_owner_read = S_IREAD;
35 static mode_t mode_owner_write = S_IWRITE;
36 static mode_t mode_owner_execute = S_IEXEC;
37 static mode_t mode_group_read = 0;
38 static mode_t mode_group_write = 0;
39 static mode_t mode_group_execute = 0;
40 static mode_t mode_world_read = 0;
41 static mode_t mode_world_write = 0;
42 static mode_t mode_world_execute = 0;
43 static mode_t mode_setuid = 0;
44 static mode_t mode_setgid = 0;
45 #else
46 static mode_t mode_owner_read = S_IRUSR;
47 static mode_t mode_owner_write = S_IWUSR;
48 static mode_t mode_owner_execute = S_IXUSR;
49 static mode_t mode_group_read = S_IRGRP;
50 static mode_t mode_group_write = S_IWGRP;
51 static mode_t mode_group_execute = S_IXGRP;
52 static mode_t mode_world_read = S_IROTH;
53 static mode_t mode_world_write = S_IWOTH;
54 static mode_t mode_world_execute = S_IXOTH;
55 static mode_t mode_setuid = S_ISUID;
56 static mode_t mode_setgid = S_ISGID;
57 #endif
58
59 // cmLibraryCommand
60 bool cmFileCommand
61 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
62 {
63   if(args.size() < 2 )
64     {
65     this->SetError("must be called with at least two arguments.");
66     return false;
67     }
68   std::string subCommand = args[0];
69   if ( subCommand == "WRITE" )
70     {
71     return this->HandleWriteCommand(args, false);
72     }
73   else if ( subCommand == "APPEND" )
74     {
75     return this->HandleWriteCommand(args, true);
76     }
77   else if ( subCommand == "DOWNLOAD" )
78     {
79     return this->HandleDownloadCommand(args);
80     }
81   else if ( subCommand == "UPLOAD" )
82     {
83     return this->HandleUploadCommand(args);
84     }
85   else if ( subCommand == "READ" )
86     {
87     return this->HandleReadCommand(args);
88     }
89   else if ( subCommand == "MD5" ||
90             subCommand == "SHA1" ||
91             subCommand == "SHA224" ||
92             subCommand == "SHA256" ||
93             subCommand == "SHA384" ||
94             subCommand == "SHA512" )
95     {
96     return this->HandleHashCommand(args);
97     }
98   else if ( subCommand == "STRINGS" )
99     {
100     return this->HandleStringsCommand(args);
101     }
102   else if ( subCommand == "GLOB" )
103     {
104     return this->HandleGlobCommand(args, false);
105     }
106   else if ( subCommand == "GLOB_RECURSE" )
107     {
108     return this->HandleGlobCommand(args, true);
109     }
110   else if ( subCommand == "MAKE_DIRECTORY" )
111     {
112     return this->HandleMakeDirectoryCommand(args);
113     }
114   else if ( subCommand == "RENAME" )
115     {
116     return this->HandleRename(args);
117     }
118   else if ( subCommand == "REMOVE" )
119     {
120     return this->HandleRemove(args, false);
121     }
122   else if ( subCommand == "REMOVE_RECURSE" )
123     {
124     return this->HandleRemove(args, true);
125     }
126   else if ( subCommand == "COPY" )
127     {
128     return this->HandleCopyCommand(args);
129     }
130   else if ( subCommand == "INSTALL" )
131     {
132     return this->HandleInstallCommand(args);
133     }
134   else if ( subCommand == "DIFFERENT" )
135     {
136     return this->HandleDifferentCommand(args);
137     }
138   else if ( subCommand == "RPATH_CHANGE" || subCommand == "CHRPATH" )
139     {
140     return this->HandleRPathChangeCommand(args);
141     }
142   else if ( subCommand == "RPATH_CHECK" )
143     {
144     return this->HandleRPathCheckCommand(args);
145     }
146   else if ( subCommand == "RPATH_REMOVE" )
147     {
148     return this->HandleRPathRemoveCommand(args);
149     }
150   else if ( subCommand == "RELATIVE_PATH" )
151     {
152     return this->HandleRelativePathCommand(args);
153     }
154   else if ( subCommand == "TO_CMAKE_PATH" )
155     {
156     return this->HandleCMakePathCommand(args, false);
157     }
158   else if ( subCommand == "TO_NATIVE_PATH" )
159     {
160     return this->HandleCMakePathCommand(args, true);
161     }
162
163   std::string e = "does not recognize sub-command "+subCommand;
164   this->SetError(e.c_str());
165   return false;
166 }
167
168 //----------------------------------------------------------------------------
169 bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args,
170   bool append)
171 {
172   std::string message;
173   std::vector<std::string>::const_iterator i = args.begin();
174
175   i++; // Get rid of subcommand
176
177   std::string fileName = *i;
178   if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
179     {
180     fileName = this->Makefile->GetCurrentDirectory();
181     fileName += "/" + *i;
182     }
183
184   i++;
185
186   for(;i != args.end(); ++i)
187     {
188     message += *i;
189     }
190   if ( !this->Makefile->CanIWriteThisFile(fileName.c_str()) )
191     {
192     std::string e
193       = "attempted to write a file: " + fileName +
194       " into a source directory.";
195     this->SetError(e.c_str());
196     cmSystemTools::SetFatalErrorOccured();
197     return false;
198     }
199   std::string dir = cmSystemTools::GetFilenamePath(fileName);
200   cmSystemTools::MakeDirectory(dir.c_str());
201
202   mode_t mode = 0;
203
204   // Set permissions to writable
205   if ( cmSystemTools::GetPermissions(fileName.c_str(), mode) )
206     {
207     cmSystemTools::SetPermissions(fileName.c_str(),
208 #if defined( _MSC_VER ) || defined( __MINGW32__ )
209       mode | S_IWRITE
210 #elif defined( __BORLANDC__ )
211       mode | S_IWUSR
212 #else
213       mode | S_IWUSR | S_IWGRP
214 #endif
215     );
216     }
217   // If GetPermissions fails, pretend like it is ok. File open will fail if
218   // the file is not writable
219   std::ofstream file(fileName.c_str(), append?std::ios::app: std::ios::out);
220   if ( !file )
221     {
222     std::string error = "Internal CMake error when trying to open file: ";
223     error += fileName.c_str();
224     error += " for writing.";
225     this->SetError(error.c_str());
226     return false;
227     }
228   file << message;
229   file.close();
230   if(mode)
231     {
232     cmSystemTools::SetPermissions(fileName.c_str(), mode);
233     }
234   return true;
235 }
236
237 //----------------------------------------------------------------------------
238 bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
239 {
240   if ( args.size() < 3 )
241     {
242     this->SetError("READ must be called with at least two additional "
243                    "arguments");
244     return false;
245     }
246
247   cmCommandArgumentsHelper argHelper;
248   cmCommandArgumentGroup group;
249
250   cmCAString readArg    (&argHelper, "READ");
251   cmCAString fileNameArg    (&argHelper, 0);
252   cmCAString resultArg      (&argHelper, 0);
253
254   cmCAString offsetArg      (&argHelper, "OFFSET", &group);
255   cmCAString limitArg       (&argHelper, "LIMIT", &group);
256   cmCAEnabler hexOutputArg  (&argHelper, "HEX", &group);
257   readArg.Follows(0);
258   fileNameArg.Follows(&readArg);
259   resultArg.Follows(&fileNameArg);
260   group.Follows(&resultArg);
261   argHelper.Parse(&args, 0);
262
263   std::string fileName = fileNameArg.GetString();
264   if ( !cmsys::SystemTools::FileIsFullPath(fileName.c_str()) )
265     {
266     fileName = this->Makefile->GetCurrentDirectory();
267     fileName += "/" + fileNameArg.GetString();
268     }
269
270   std::string variable = resultArg.GetString();
271
272   // Open the specified file.
273 #if defined(_WIN32) || defined(__CYGWIN__)
274   std::ifstream file(fileName.c_str(), std::ios::in | 
275                (hexOutputArg.IsEnabled() ? std::ios::binary : std::ios::in));
276 #else
277   std::ifstream file(fileName.c_str(), std::ios::in);
278 #endif
279
280   if ( !file )
281     {
282     std::string error = "Internal CMake error when trying to open file: ";
283     error += fileName.c_str();
284     error += " for reading.";
285     this->SetError(error.c_str());
286     return false;
287     }
288
289   // is there a limit?
290   long sizeLimit = -1;
291   if (limitArg.GetString().size() > 0)
292     {
293     sizeLimit = atoi(limitArg.GetCString());
294     }
295
296   // is there an offset?
297   long offset = 0;
298   if (offsetArg.GetString().size() > 0)
299     {
300     offset = atoi(offsetArg.GetCString());
301     }
302
303   file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6
304
305   std::string output;
306
307   if (hexOutputArg.IsEnabled())
308     {
309     // Convert part of the file into hex code
310     char c;
311     while((sizeLimit != 0) && (file.get(c)))
312       {
313       char hex[4];
314       sprintf(hex, "%.2x", c&0xff);
315       output += hex;
316       if (sizeLimit > 0)
317         {
318         sizeLimit--;
319         }
320       }
321     }
322   else
323     {
324     std::string line;
325     bool has_newline = false;
326     while (sizeLimit != 0 &&
327           cmSystemTools::GetLineFromStream(file, line, &has_newline,
328                                             sizeLimit) )
329       {
330       if (sizeLimit > 0)
331         {
332         sizeLimit = sizeLimit - static_cast<long>(line.size());
333         if (has_newline)
334           {
335           sizeLimit--;
336           }
337         if (sizeLimit < 0)
338           {
339           sizeLimit = 0;
340           }
341         }
342       output += line;
343       if ( has_newline )
344         {
345         output += "\n";
346         }
347       }
348     }
349   this->Makefile->AddDefinition(variable.c_str(), output.c_str());
350   return true;
351 }
352
353 //----------------------------------------------------------------------------
354 bool cmFileCommand::HandleHashCommand(std::vector<std::string> const& args)
355 {
356 #if defined(CMAKE_BUILD_WITH_CMAKE)
357   if(args.size() != 3)
358     {
359     cmOStringStream e;
360     e << args[0] << " requires a file name and output variable";
361     this->SetError(e.str().c_str());
362     return false;
363     }
364
365   cmsys::auto_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0].c_str()));
366   if(hash.get())
367     {
368     std::string out = hash->HashFile(args[1].c_str());
369     if(!out.empty())
370       {
371       this->Makefile->AddDefinition(args[2].c_str(), out.c_str());
372       return true;
373       }
374     cmOStringStream e;
375     e << args[0] << " failed to read file \"" << args[1] << "\": "
376       << cmSystemTools::GetLastSystemError();
377     this->SetError(e.str().c_str());
378     }
379   return false;
380 #else
381   cmOStringStream e;
382   e << args[0] << " not available during bootstrap";
383   this->SetError(e.str().c_str());
384   return false;
385 #endif
386 }
387
388 //----------------------------------------------------------------------------
389 bool cmFileCommand::HandleStringsCommand(std::vector<std::string> const& args)
390 {
391   if(args.size() < 3)
392     {
393     this->SetError("STRINGS requires a file name and output variable");
394     return false;
395     }
396
397   // Get the file to read.
398   std::string fileName = args[1];
399   if(!cmsys::SystemTools::FileIsFullPath(fileName.c_str()))
400     {
401     fileName = this->Makefile->GetCurrentDirectory();
402     fileName += "/" + args[1];
403     }
404
405   // Get the variable in which to store the results.
406   std::string outVar = args[2];
407
408   // Parse the options.
409   enum { arg_none,
410          arg_limit_input,
411          arg_limit_output,
412          arg_limit_count,
413          arg_length_minimum,
414          arg_length_maximum,
415          arg__maximum,
416          arg_regex };
417   unsigned int minlen = 0;
418   unsigned int maxlen = 0;
419   int limit_input = -1;
420   int limit_output = -1;
421   unsigned int limit_count = 0;
422   cmsys::RegularExpression regex;
423   bool have_regex = false;
424   bool newline_consume = false;
425   bool hex_conversion_enabled = true;
426   int arg_mode = arg_none;
427   for(unsigned int i=3; i < args.size(); ++i)
428     {
429     if(args[i] == "LIMIT_INPUT")
430       {
431       arg_mode = arg_limit_input;
432       }
433     else if(args[i] == "LIMIT_OUTPUT")
434       {
435       arg_mode = arg_limit_output;
436       }
437     else if(args[i] == "LIMIT_COUNT")
438       {
439       arg_mode = arg_limit_count;
440       }
441     else if(args[i] == "LENGTH_MINIMUM")
442       {
443       arg_mode = arg_length_minimum;
444       }
445     else if(args[i] == "LENGTH_MAXIMUM")
446       {
447       arg_mode = arg_length_maximum;
448       }
449     else if(args[i] == "REGEX")
450       {
451       arg_mode = arg_regex;
452       }
453     else if(args[i] == "NEWLINE_CONSUME")
454       {
455       newline_consume = true;
456       arg_mode = arg_none;
457       }
458     else if(args[i] == "NO_HEX_CONVERSION")
459       {
460       hex_conversion_enabled = false;
461       arg_mode = arg_none;
462       }
463     else if(arg_mode == arg_limit_input)
464       {
465       if(sscanf(args[i].c_str(), "%d", &limit_input) != 1 ||
466          limit_input < 0)
467         {
468         cmOStringStream e;
469         e << "STRINGS option LIMIT_INPUT value \""
470           << args[i] << "\" is not an unsigned integer.";
471         this->SetError(e.str().c_str());
472         return false;
473         }
474       arg_mode = arg_none;
475       }
476     else if(arg_mode == arg_limit_output)
477       {
478       if(sscanf(args[i].c_str(), "%d", &limit_output) != 1 ||
479          limit_output < 0)
480         {
481         cmOStringStream e;
482         e << "STRINGS option LIMIT_OUTPUT value \""
483           << args[i] << "\" is not an unsigned integer.";
484         this->SetError(e.str().c_str());
485         return false;
486         }
487       arg_mode = arg_none;
488       }
489     else if(arg_mode == arg_limit_count)
490       {
491       int count;
492       if(sscanf(args[i].c_str(), "%d", &count) != 1 || count < 0)
493         {
494         cmOStringStream e;
495         e << "STRINGS option LIMIT_COUNT value \""
496           << args[i] << "\" is not an unsigned integer.";
497         this->SetError(e.str().c_str());
498         return false;
499         }
500       limit_count = count;
501       arg_mode = arg_none;
502       }
503     else if(arg_mode == arg_length_minimum)
504       {
505       int len;
506       if(sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0)
507         {
508         cmOStringStream e;
509         e << "STRINGS option LENGTH_MINIMUM value \""
510           << args[i] << "\" is not an unsigned integer.";
511         this->SetError(e.str().c_str());
512         return false;
513         }
514       minlen = len;
515       arg_mode = arg_none;
516       }
517     else if(arg_mode == arg_length_maximum)
518       {
519       int len;
520       if(sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0)
521         {
522         cmOStringStream e;
523         e << "STRINGS option LENGTH_MAXIMUM value \""
524           << args[i] << "\" is not an unsigned integer.";
525         this->SetError(e.str().c_str());
526         return false;
527         }
528       maxlen = len;
529       arg_mode = arg_none;
530       }
531     else if(arg_mode == arg_regex)
532       {
533       if(!regex.compile(args[i].c_str()))
534         {
535         cmOStringStream e;
536         e << "STRINGS option REGEX value \""
537           << args[i] << "\" could not be compiled.";
538         this->SetError(e.str().c_str());
539         return false;
540         }
541       have_regex = true;
542       arg_mode = arg_none;
543       }
544     else
545       {
546       cmOStringStream e;
547       e << "STRINGS given unknown argument \""
548         << args[i] << "\"";
549       this->SetError(e.str().c_str());
550       return false;
551       }
552     }
553
554   if (hex_conversion_enabled)
555     {
556     // TODO: should work without temp file, but just on a memory buffer
557     std::string binaryFileName = this->Makefile->GetCurrentOutputDirectory();
558     binaryFileName += cmake::GetCMakeFilesDirectory();
559     binaryFileName += "/FileCommandStringsBinaryFile";
560     if(cmHexFileConverter::TryConvert(fileName.c_str(),binaryFileName.c_str()))
561       {
562       fileName = binaryFileName;
563       }
564     }
565
566   // Open the specified file.
567 #if defined(_WIN32) || defined(__CYGWIN__)
568   std::ifstream fin(fileName.c_str(), std::ios::in | std::ios::binary);
569 #else
570   std::ifstream fin(fileName.c_str(), std::ios::in);
571 #endif
572   if(!fin)
573     {
574     cmOStringStream e;
575     e << "STRINGS file \"" << fileName << "\" cannot be read.";
576     this->SetError(e.str().c_str());
577     return false;
578     }
579
580   // Parse strings out of the file.
581   int output_size = 0;
582   std::vector<std::string> strings;
583   std::string s;
584   int c;
585   while((!limit_count || strings.size() < limit_count) &&
586         (limit_input < 0 || static_cast<int>(fin.tellg()) < limit_input) &&
587         (c = fin.get(), fin))
588     {
589     if(c == '\n' && !newline_consume)
590       {
591       // The current line has been terminated.  Check if the current
592       // string matches the requirements.  The length may now be as
593       // low as zero since blank lines are allowed.
594       if(s.length() >= minlen &&
595          (!have_regex || regex.find(s.c_str())))
596         {
597         output_size += static_cast<int>(s.size()) + 1;
598         if(limit_output >= 0 && output_size >= limit_output)
599           {
600           s = "";
601           break;
602           }
603         strings.push_back(s);
604         }
605
606       // Reset the string to empty.
607       s = "";
608       }
609     else if(c == '\r')
610       {
611       // Ignore CR character to make output always have UNIX newlines.
612       }
613     else if((c >= 0x20 && c < 0x7F) || c == '\t' ||
614             (c == '\n' && newline_consume))
615       {
616       // This is an ASCII character that may be part of a string.
617       // Cast added to avoid compiler warning. Cast is ok because
618       // c is guaranteed to fit in char by the above if...
619       s += static_cast<char>(c);
620       }
621     else
622       {
623       // TODO: Support ENCODING option.  See issue #10519.
624       // A non-string character has been found.  Check if the current
625       // string matches the requirements.  We require that the length
626       // be at least one no matter what the user specified.
627       if(s.length() >= minlen && s.length() >= 1 &&
628          (!have_regex || regex.find(s.c_str())))
629         {
630         output_size += static_cast<int>(s.size()) + 1;
631         if(limit_output >= 0 && output_size >= limit_output)
632           {
633           s = "";
634           break;
635           }
636         strings.push_back(s);
637         }
638
639       // Reset the string to empty.
640       s = "";
641       }
642
643     // Terminate a string if the maximum length is reached.
644     if(maxlen > 0 && s.size() == maxlen)
645       {
646       if(s.length() >= minlen &&
647          (!have_regex || regex.find(s.c_str())))
648         {
649         output_size += static_cast<int>(s.size()) + 1;
650         if(limit_output >= 0 && output_size >= limit_output)
651           {
652           s = "";
653           break;
654           }
655         strings.push_back(s);
656         }
657       s = "";
658       }
659     }
660
661   // If there is a non-empty current string we have hit the end of the
662   // input file or the input size limit.  Check if the current string
663   // matches the requirements.
664   if((!limit_count || strings.size() < limit_count) &&
665      !s.empty() && s.length() >= minlen &&
666      (!have_regex || regex.find(s.c_str())))
667     {
668     output_size += static_cast<int>(s.size()) + 1;
669     if(limit_output < 0 || output_size < limit_output)
670       {
671       strings.push_back(s);
672       }
673     }
674
675   // Encode the result in a CMake list.
676   const char* sep = "";
677   std::string output;
678   for(std::vector<std::string>::const_iterator si = strings.begin();
679       si != strings.end(); ++si)
680     {
681     // Separate the strings in the output to make it a list.
682     output += sep;
683     sep = ";";
684
685     // Store the string in the output, but escape semicolons to
686     // make sure it is a list.
687     std::string const& sr = *si;
688     for(unsigned int i=0; i < sr.size(); ++i)
689       {
690       if(sr[i] == ';')
691         {
692         output += '\\';
693         }
694       output += sr[i];
695       }
696     }
697
698   // Save the output in a makefile variable.
699   this->Makefile->AddDefinition(outVar.c_str(), output.c_str());
700   return true;
701 }
702
703 //----------------------------------------------------------------------------
704 bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args,
705   bool recurse)
706 {
707   if ( args.size() < 2 )
708     {
709     this->SetError("GLOB requires at least a variable name");
710     return false;
711     }
712
713   std::vector<std::string>::const_iterator i = args.begin();
714
715   i++; // Get rid of subcommand
716
717   std::string variable = *i;
718   i++;
719   cmsys::Glob g;
720   g.SetRecurse(recurse);
721
722   bool explicitFollowSymlinks = false;
723   cmPolicies::PolicyStatus status =
724     this->Makefile->GetPolicyStatus(cmPolicies::CMP0009);
725   if(recurse)
726     {
727     switch(status)
728       {
729       case cmPolicies::NEW:
730         g.RecurseThroughSymlinksOff();
731         break;
732       case cmPolicies::OLD:
733       case cmPolicies::WARN:
734       case cmPolicies::REQUIRED_IF_USED:
735       case cmPolicies::REQUIRED_ALWAYS:
736         g.RecurseThroughSymlinksOn();
737         break;
738       }
739     }
740
741   std::string output = "";
742   bool first = true;
743   for ( ; i != args.end(); ++i )
744     {
745     if ( recurse && (*i == "FOLLOW_SYMLINKS") )
746       {
747       explicitFollowSymlinks = true;
748       g.RecurseThroughSymlinksOn();
749       ++i;
750       if ( i == args.end() )
751         {
752         this->SetError(
753           "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS");
754         return false;
755         }
756       }
757
758     if ( *i == "RELATIVE" )
759       {
760       ++i; // skip RELATIVE
761       if ( i == args.end() )
762         {
763         this->SetError("GLOB requires a directory after the RELATIVE tag");
764         return false;
765         }
766       g.SetRelative(i->c_str());
767       ++i;
768       if(i == args.end())
769         {
770         this->SetError("GLOB requires a glob expression after the directory");
771         return false;
772         }
773       }
774
775     if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
776       {
777       std::string expr = this->Makefile->GetCurrentDirectory();
778       // Handle script mode
779       if ( expr.size() > 0 )
780         {
781         expr += "/" + *i;
782         g.FindFiles(expr);
783         }
784       else
785         {
786         g.FindFiles(*i);
787         }
788       }
789     else
790       {
791       g.FindFiles(*i);
792       }
793
794     std::vector<std::string>::size_type cc;
795     std::vector<std::string>& files = g.GetFiles();
796     for ( cc = 0; cc < files.size(); cc ++ )
797       {
798       if ( !first )
799         {
800         output += ";";
801         }
802       output += files[cc];
803       first = false;
804       }
805     }
806
807   if(recurse && !explicitFollowSymlinks)
808     {
809     switch (status)
810       {
811       case cmPolicies::NEW:
812         // Correct behavior, yay!
813         break;
814       case cmPolicies::OLD:
815         // Probably not really the expected behavior, but the author explicitly
816         // asked for the old behavior... no warning.
817       case cmPolicies::WARN:
818         // Possibly unexpected old behavior *and* we actually traversed
819         // symlinks without being explicitly asked to: warn the author.
820         if(g.GetFollowedSymlinkCount() != 0)
821           {
822           this->Makefile->IssueMessage(cmake::AUTHOR_WARNING,
823             this->Makefile->GetPolicies()->
824               GetPolicyWarning(cmPolicies::CMP0009));
825           }
826         break;
827       case cmPolicies::REQUIRED_IF_USED:
828       case cmPolicies::REQUIRED_ALWAYS:
829         this->SetError("policy CMP0009 error");
830         this->Makefile->IssueMessage(cmake::FATAL_ERROR,
831           this->Makefile->GetPolicies()->
832             GetRequiredPolicyError(cmPolicies::CMP0009));
833         return false;
834       }
835     }
836
837   this->Makefile->AddDefinition(variable.c_str(), output.c_str());
838   return true;
839 }
840
841 //----------------------------------------------------------------------------
842 bool cmFileCommand::HandleMakeDirectoryCommand(
843   std::vector<std::string> const& args)
844 {
845   if(args.size() < 2 )
846     {
847     this->SetError("called with incorrect number of arguments");
848     return false;
849     }
850
851   std::vector<std::string>::const_iterator i = args.begin();
852
853   i++; // Get rid of subcommand
854
855   std::string expr;
856   for ( ; i != args.end(); ++i )
857     {
858     const std::string* cdir = &(*i);
859     if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
860       {
861       expr = this->Makefile->GetCurrentDirectory();
862       expr += "/" + *i;
863       cdir = &expr;
864       }
865     if ( !this->Makefile->CanIWriteThisFile(cdir->c_str()) )
866       {
867       std::string e = "attempted to create a directory: " + *cdir
868         + " into a source directory.";
869       this->SetError(e.c_str());
870       cmSystemTools::SetFatalErrorOccured();
871       return false;
872       }
873     if ( !cmSystemTools::MakeDirectory(cdir->c_str()) )
874       {
875       std::string error = "problem creating directory: " + *cdir;
876       this->SetError(error.c_str());
877       return false;
878       }
879     }
880   return true;
881 }
882
883 //----------------------------------------------------------------------------
884 bool
885 cmFileCommand::HandleDifferentCommand(std::vector<std::string> const& args)
886 {
887   /*
888     FILE(DIFFERENT <variable> FILES <lhs> <rhs>)
889    */
890
891   // Evaluate arguments.
892   const char* file_lhs = 0;
893   const char* file_rhs = 0;
894   const char* var = 0;
895   enum Doing { DoingNone, DoingVar, DoingFileLHS, DoingFileRHS };
896   Doing doing = DoingVar;
897   for(unsigned int i=1; i < args.size(); ++i)
898     {
899     if(args[i] == "FILES")
900       {
901       doing = DoingFileLHS;
902       }
903     else if(doing == DoingVar)
904       {
905       var = args[i].c_str();
906       doing = DoingNone;
907       }
908     else if(doing == DoingFileLHS)
909       {
910       file_lhs = args[i].c_str();
911       doing = DoingFileRHS;
912       }
913     else if(doing == DoingFileRHS)
914       {
915       file_rhs = args[i].c_str();
916       doing = DoingNone;
917       }
918     else
919       {
920       cmOStringStream e;
921       e << "DIFFERENT given unknown argument " << args[i];
922       this->SetError(e.str().c_str());
923       return false;
924       }
925     }
926   if(!var)
927     {
928     this->SetError("DIFFERENT not given result variable name.");
929     return false;
930     }
931   if(!file_lhs || !file_rhs)
932     {
933     this->SetError("DIFFERENT not given FILES option with two file names.");
934     return false;
935     }
936
937   // Compare the files.
938   const char* result =
939     cmSystemTools::FilesDiffer(file_lhs, file_rhs)? "1" : "0";
940   this->Makefile->AddDefinition(var, result);
941   return true;
942 }
943
944 //----------------------------------------------------------------------------
945 // File installation helper class.
946 struct cmFileCopier
947 {
948   cmFileCopier(cmFileCommand* command, const char* name = "COPY"):
949     FileCommand(command),
950     Makefile(command->GetMakefile()),
951     Name(name),
952     Always(false),
953     MatchlessFiles(true),
954     FilePermissions(0),
955     DirPermissions(0),
956     CurrentMatchRule(0),
957     UseGivenPermissionsFile(false),
958     UseGivenPermissionsDir(false),
959     UseSourcePermissions(true),
960     Doing(DoingNone)
961     {
962     }
963   virtual ~cmFileCopier() {}
964
965   bool Run(std::vector<std::string> const& args);
966 protected:
967
968   cmFileCommand* FileCommand;
969   cmMakefile* Makefile;
970   const char* Name;
971   bool Always;
972   cmFileTimeComparison FileTimes;
973
974   // Whether to install a file not matching any expression.
975   bool MatchlessFiles;
976
977   // Permissions for files and directories installed by this object.
978   mode_t FilePermissions;
979   mode_t DirPermissions;
980
981   // Properties set by pattern and regex match rules.
982   struct MatchProperties
983   {
984     bool Exclude;
985     mode_t Permissions;
986     MatchProperties(): Exclude(false), Permissions(0) {}
987   };
988   struct MatchRule;
989   friend struct MatchRule;
990   struct MatchRule
991   {
992     cmsys::RegularExpression Regex;
993     MatchProperties Properties;
994     std::string RegexString;
995     MatchRule(std::string const& regex):
996       Regex(regex.c_str()), RegexString(regex) {}
997   };
998   std::vector<MatchRule> MatchRules;
999
1000   // Get the properties from rules matching this input file.
1001   MatchProperties CollectMatchProperties(const char* file)
1002     {
1003     // Match rules are case-insensitive on some platforms.
1004 #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
1005     std::string lower = cmSystemTools::LowerCase(file);
1006     const char* file_to_match = lower.c_str();
1007 #else
1008     const char* file_to_match = file;
1009 #endif
1010
1011     // Collect properties from all matching rules.
1012     bool matched = false;
1013     MatchProperties result;
1014     for(std::vector<MatchRule>::iterator mr = this->MatchRules.begin();
1015         mr != this->MatchRules.end(); ++mr)
1016       {
1017       if(mr->Regex.find(file_to_match))
1018         {
1019         matched = true;
1020         result.Exclude |= mr->Properties.Exclude;
1021         result.Permissions |= mr->Properties.Permissions;
1022         }
1023       }
1024     if(!matched && !this->MatchlessFiles)
1025       {
1026       result.Exclude = !cmSystemTools::FileIsDirectory(file);
1027       }
1028     return result;
1029     }
1030
1031   bool SetPermissions(const char* toFile, mode_t permissions)
1032     {
1033     if(permissions && !cmSystemTools::SetPermissions(toFile, permissions))
1034       {
1035       cmOStringStream e;
1036       e << this->Name << " cannot set permissions on \"" << toFile << "\"";
1037       this->FileCommand->SetError(e.str().c_str());
1038       return false;
1039       }
1040     return true;
1041     }
1042
1043   // Translate an argument to a permissions bit.
1044   bool CheckPermissions(std::string const& arg, mode_t& permissions)
1045     {
1046     if(arg == "OWNER_READ")         { permissions |= mode_owner_read; }
1047     else if(arg == "OWNER_WRITE")   { permissions |= mode_owner_write; }
1048     else if(arg == "OWNER_EXECUTE") { permissions |= mode_owner_execute; }
1049     else if(arg == "GROUP_READ")    { permissions |= mode_group_read; }
1050     else if(arg == "GROUP_WRITE")   { permissions |= mode_group_write; }
1051     else if(arg == "GROUP_EXECUTE") { permissions |= mode_group_execute; }
1052     else if(arg == "WORLD_READ")    { permissions |= mode_world_read; }
1053     else if(arg == "WORLD_WRITE")   { permissions |= mode_world_write; }
1054     else if(arg == "WORLD_EXECUTE") { permissions |= mode_world_execute; }
1055     else if(arg == "SETUID")        { permissions |= mode_setuid; }
1056     else if(arg == "SETGID")        { permissions |= mode_setgid; }
1057     else
1058       {
1059       cmOStringStream e;
1060       e << this->Name << " given invalid permission \"" << arg << "\".";
1061       this->FileCommand->SetError(e.str().c_str());
1062       return false;
1063       }
1064     return true;
1065     }
1066
1067   bool InstallSymlink(const char* fromFile, const char* toFile);
1068   bool InstallFile(const char* fromFile, const char* toFile,
1069                    MatchProperties const& match_properties);
1070   bool InstallDirectory(const char* source, const char* destination,
1071                         MatchProperties const& match_properties);
1072   virtual bool Install(const char* fromFile, const char* toFile);
1073   virtual std::string const& ToName(std::string const& fromName)
1074     { return fromName; }
1075
1076   enum Type
1077   {
1078     TypeFile,
1079     TypeDir,
1080     TypeLink
1081   };
1082   virtual void ReportCopy(const char*, Type, bool) {}
1083   virtual bool ReportMissing(const char* fromFile)
1084     {
1085     // The input file does not exist and installation is not optional.
1086     cmOStringStream e;
1087     e << this->Name << " cannot find \"" << fromFile << "\".";
1088     this->FileCommand->SetError(e.str().c_str());
1089     return false;
1090     }
1091
1092   MatchRule* CurrentMatchRule;
1093   bool UseGivenPermissionsFile;
1094   bool UseGivenPermissionsDir;
1095   bool UseSourcePermissions;
1096   std::string Destination;
1097   std::vector<std::string> Files;
1098   int Doing;
1099
1100   virtual bool Parse(std::vector<std::string> const& args);
1101   enum
1102   {
1103     DoingNone,
1104     DoingError,
1105     DoingDestination,
1106     DoingFiles,
1107     DoingPattern,
1108     DoingRegex,
1109     DoingPermissionsFile,
1110     DoingPermissionsDir,
1111     DoingPermissionsMatch,
1112     DoingLast1
1113   };
1114   virtual bool CheckKeyword(std::string const& arg);
1115   virtual bool CheckValue(std::string const& arg);
1116
1117   void NotBeforeMatch(std::string const& arg)
1118     {
1119     cmOStringStream e;
1120     e << "option " << arg << " may not appear before PATTERN or REGEX.";
1121     this->FileCommand->SetError(e.str().c_str());
1122     this->Doing = DoingError;
1123     }
1124   void NotAfterMatch(std::string const& arg)
1125     {
1126     cmOStringStream e;
1127     e << "option " << arg << " may not appear after PATTERN or REGEX.";
1128     this->FileCommand->SetError(e.str().c_str());
1129     this->Doing = DoingError;
1130     }
1131   virtual void DefaultFilePermissions()
1132     {
1133     // Use read/write permissions.
1134     this->FilePermissions = 0;
1135     this->FilePermissions |= mode_owner_read;
1136     this->FilePermissions |= mode_owner_write;
1137     this->FilePermissions |= mode_group_read;
1138     this->FilePermissions |= mode_world_read;
1139     }
1140   virtual void DefaultDirectoryPermissions()
1141     {
1142     // Use read/write/executable permissions.
1143     this->DirPermissions = 0;
1144     this->DirPermissions |= mode_owner_read;
1145     this->DirPermissions |= mode_owner_write;
1146     this->DirPermissions |= mode_owner_execute;
1147     this->DirPermissions |= mode_group_read;
1148     this->DirPermissions |= mode_group_execute;
1149     this->DirPermissions |= mode_world_read;
1150     this->DirPermissions |= mode_world_execute;
1151     }
1152 };
1153
1154 //----------------------------------------------------------------------------
1155 bool cmFileCopier::Parse(std::vector<std::string> const& args)
1156 {
1157   this->Doing = DoingFiles;
1158   for(unsigned int i=1; i < args.size(); ++i)
1159     {
1160     // Check this argument.
1161     if(!this->CheckKeyword(args[i]) &&
1162        !this->CheckValue(args[i]))
1163       {
1164       cmOStringStream e;
1165       e << "called with unknown argument \"" << args[i] << "\".";
1166       this->FileCommand->SetError(e.str().c_str());
1167       return false;
1168       }
1169
1170     // Quit if an argument is invalid.
1171     if(this->Doing == DoingError)
1172       {
1173       return false;
1174       }
1175     }
1176
1177   // Require a destination.
1178   if(this->Destination.empty())
1179     {
1180     cmOStringStream e;
1181     e << this->Name << " given no DESTINATION";
1182     this->FileCommand->SetError(e.str().c_str());
1183     return false;
1184     }
1185
1186   // If file permissions were not specified set default permissions.
1187   if(!this->UseGivenPermissionsFile && !this->UseSourcePermissions)
1188     {
1189     this->DefaultFilePermissions();
1190     }
1191
1192   // If directory permissions were not specified set default permissions.
1193   if(!this->UseGivenPermissionsDir && !this->UseSourcePermissions)
1194     {
1195     this->DefaultDirectoryPermissions();
1196     }
1197
1198   return true;
1199 }
1200
1201 //----------------------------------------------------------------------------
1202 bool cmFileCopier::CheckKeyword(std::string const& arg)
1203 {
1204   if(arg == "DESTINATION")
1205     {
1206     if(this->CurrentMatchRule)
1207       {
1208       this->NotAfterMatch(arg);
1209       }
1210     else
1211       {
1212       this->Doing = DoingDestination;
1213       }
1214     }
1215   else if(arg == "PATTERN")
1216     {
1217     this->Doing = DoingPattern;
1218     }
1219   else if(arg == "REGEX")
1220     {
1221     this->Doing = DoingRegex;
1222     }
1223   else if(arg == "EXCLUDE")
1224     {
1225     // Add this property to the current match rule.
1226     if(this->CurrentMatchRule)
1227       {
1228       this->CurrentMatchRule->Properties.Exclude = true;
1229       this->Doing = DoingNone;
1230       }
1231     else
1232       {
1233       this->NotBeforeMatch(arg);
1234       }
1235     }
1236   else if(arg == "PERMISSIONS")
1237     {
1238     if(this->CurrentMatchRule)
1239       {
1240       this->Doing = DoingPermissionsMatch;
1241       }
1242     else
1243       {
1244       this->NotBeforeMatch(arg);
1245       }
1246     }
1247   else if(arg == "FILE_PERMISSIONS")
1248     {
1249     if(this->CurrentMatchRule)
1250       {
1251       this->NotAfterMatch(arg);
1252       }
1253     else
1254       {
1255       this->Doing = DoingPermissionsFile;
1256       this->UseGivenPermissionsFile = true;
1257       }
1258     }
1259   else if(arg == "DIRECTORY_PERMISSIONS")
1260     {
1261     if(this->CurrentMatchRule)
1262       {
1263       this->NotAfterMatch(arg);
1264       }
1265     else
1266       {
1267       this->Doing = DoingPermissionsDir;
1268       this->UseGivenPermissionsDir = true;
1269       }
1270     }
1271   else if(arg == "USE_SOURCE_PERMISSIONS")
1272     {
1273     if(this->CurrentMatchRule)
1274       {
1275       this->NotAfterMatch(arg);
1276       }
1277     else
1278       {
1279       this->Doing = DoingNone;
1280       this->UseSourcePermissions = true;
1281       }
1282     }
1283   else if(arg == "NO_SOURCE_PERMISSIONS")
1284     {
1285     if(this->CurrentMatchRule)
1286       {
1287       this->NotAfterMatch(arg);
1288       }
1289     else
1290       {
1291       this->Doing = DoingNone;
1292       this->UseSourcePermissions = false;
1293       }
1294     }
1295   else if(arg == "FILES_MATCHING")
1296     {
1297     if(this->CurrentMatchRule)
1298       {
1299       this->NotAfterMatch(arg);
1300       }
1301     else
1302       {
1303       this->Doing = DoingNone;
1304       this->MatchlessFiles = false;
1305       }
1306     }
1307   else
1308     {
1309     return false;
1310     }
1311   return true;
1312 }
1313
1314 //----------------------------------------------------------------------------
1315 bool cmFileCopier::CheckValue(std::string const& arg)
1316 {
1317   switch(this->Doing)
1318     {
1319     case DoingFiles:
1320       if(arg.empty() || cmSystemTools::FileIsFullPath(arg.c_str()))
1321         {
1322         this->Files.push_back(arg);
1323         }
1324       else
1325         {
1326         std::string file = this->Makefile->GetCurrentDirectory();
1327         file += "/" + arg;
1328         this->Files.push_back(file);
1329         }
1330       break;
1331     case DoingDestination:
1332       if(arg.empty() || cmSystemTools::FileIsFullPath(arg.c_str()))
1333         {
1334         this->Destination = arg;
1335         }
1336       else
1337         {
1338         this->Destination = this->Makefile->GetCurrentOutputDirectory();
1339         this->Destination += "/" + arg;
1340         }
1341       this->Doing = DoingNone;
1342       break;
1343     case DoingPattern:
1344       {
1345       // Convert the pattern to a regular expression.  Require a
1346       // leading slash and trailing end-of-string in the matched
1347       // string to make sure the pattern matches only whole file
1348       // names.
1349       std::string regex = "/";
1350       regex += cmsys::Glob::PatternToRegex(arg, false);
1351       regex += "$";
1352       this->MatchRules.push_back(MatchRule(regex));
1353       this->CurrentMatchRule = &*(this->MatchRules.end()-1);
1354       if(this->CurrentMatchRule->Regex.is_valid())
1355         {
1356         this->Doing = DoingNone;
1357         }
1358       else
1359         {
1360         cmOStringStream e;
1361         e << "could not compile PATTERN \"" << arg << "\".";
1362         this->FileCommand->SetError(e.str().c_str());
1363         this->Doing = DoingError;
1364         }
1365       }
1366       break;
1367     case DoingRegex:
1368       this->MatchRules.push_back(MatchRule(arg));
1369       this->CurrentMatchRule = &*(this->MatchRules.end()-1);
1370       if(this->CurrentMatchRule->Regex.is_valid())
1371         {
1372         this->Doing = DoingNone;
1373         }
1374       else
1375         {
1376         cmOStringStream e;
1377         e << "could not compile REGEX \"" << arg << "\".";
1378         this->FileCommand->SetError(e.str().c_str());
1379         this->Doing = DoingError;
1380         }
1381       break;
1382     case DoingPermissionsFile:
1383       if(!this->CheckPermissions(arg, this->FilePermissions))
1384         {
1385         this->Doing = DoingError;
1386         }
1387       break;
1388     case DoingPermissionsDir:
1389       if(!this->CheckPermissions(arg, this->DirPermissions))
1390         {
1391         this->Doing = DoingError;
1392         }
1393       break;
1394     case DoingPermissionsMatch:
1395       if(!this->CheckPermissions(
1396            arg, this->CurrentMatchRule->Properties.Permissions))
1397         {
1398         this->Doing = DoingError;
1399         }
1400       break;
1401     default:
1402       return false;
1403     }
1404   return true;
1405 }
1406
1407 //----------------------------------------------------------------------------
1408 bool cmFileCopier::Run(std::vector<std::string> const& args)
1409 {
1410   if(!this->Parse(args))
1411     {
1412     return false;
1413     }
1414
1415   std::vector<std::string> const& files = this->Files;
1416   for(std::vector<std::string>::size_type i = 0; i < files.size(); ++i)
1417     {
1418     // Split the input file into its directory and name components.
1419     std::vector<std::string> fromPathComponents;
1420     cmSystemTools::SplitPath(files[i].c_str(), fromPathComponents);
1421     std::string fromName = *(fromPathComponents.end()-1);
1422     std::string fromDir = cmSystemTools::JoinPath(fromPathComponents.begin(),
1423                                                   fromPathComponents.end()-1);
1424
1425     // Compute the full path to the destination file.
1426     std::string toFile = this->Destination;
1427     std::string const& toName = this->ToName(fromName);
1428     if(!toName.empty())
1429       {
1430       toFile += "/";
1431       toFile += toName;
1432       }
1433
1434     // Construct the full path to the source file.  The file name may
1435     // have been changed above.
1436     std::string fromFile = fromDir;
1437     if(!fromName.empty())
1438       {
1439       fromFile += "/";
1440       fromFile += fromName;
1441       }
1442
1443     if(!this->Install(fromFile.c_str(), toFile.c_str()))
1444       {
1445       return false;
1446       }
1447     }
1448   return true;
1449 }
1450
1451 //----------------------------------------------------------------------------
1452 bool cmFileCopier::Install(const char* fromFile, const char* toFile)
1453 {
1454   if(!*fromFile)
1455     {
1456     cmOStringStream e;
1457     e << "INSTALL encountered an empty string input file name.";
1458     this->FileCommand->SetError(e.str().c_str());
1459     return false;
1460     }
1461
1462   // Collect any properties matching this file name.
1463   MatchProperties match_properties = this->CollectMatchProperties(fromFile);
1464
1465   // Skip the file if it is excluded.
1466   if(match_properties.Exclude)
1467     {
1468     return true;
1469     }
1470
1471   if(cmSystemTools::SameFile(fromFile, toFile))
1472     {
1473     return true;
1474     }
1475   else if(cmSystemTools::FileIsSymlink(fromFile))
1476     {
1477     return this->InstallSymlink(fromFile, toFile);
1478     }
1479   else if(cmSystemTools::FileIsDirectory(fromFile))
1480     {
1481     return this->InstallDirectory(fromFile, toFile, match_properties);
1482     }
1483   else if(cmSystemTools::FileExists(fromFile))
1484     {
1485     return this->InstallFile(fromFile, toFile, match_properties);
1486     }
1487   return this->ReportMissing(fromFile);
1488 }
1489
1490 //----------------------------------------------------------------------------
1491 bool cmFileCopier::InstallSymlink(const char* fromFile, const char* toFile)
1492 {
1493   // Read the original symlink.
1494   std::string symlinkTarget;
1495   if(!cmSystemTools::ReadSymlink(fromFile, symlinkTarget))
1496     {
1497     cmOStringStream e;
1498     e << this->Name << " cannot read symlink \"" << fromFile
1499       << "\" to duplicate at \"" << toFile << "\".";
1500     this->FileCommand->SetError(e.str().c_str());
1501     return false;
1502     }
1503
1504   // Compare the symlink value to that at the destination if not
1505   // always installing.
1506   bool copy = true;
1507   if(!this->Always)
1508     {
1509     std::string oldSymlinkTarget;
1510     if(cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget))
1511       {
1512       if(symlinkTarget == oldSymlinkTarget)
1513         {
1514         copy = false;
1515         }
1516       }
1517     }
1518
1519   // Inform the user about this file installation.
1520   this->ReportCopy(toFile, TypeLink, copy);
1521
1522   if(copy)
1523     {
1524     // Remove the destination file so we can always create the symlink.
1525     cmSystemTools::RemoveFile(toFile);
1526
1527     // Create the symlink.
1528     if(!cmSystemTools::CreateSymlink(symlinkTarget.c_str(), toFile))
1529       {
1530       cmOStringStream e;
1531       e << this->Name <<  " cannot duplicate symlink \"" << fromFile
1532         << "\" at \"" << toFile << "\".";
1533       this->FileCommand->SetError(e.str().c_str());
1534       return false;
1535       }
1536     }
1537
1538   return true;
1539 }
1540
1541 //----------------------------------------------------------------------------
1542 bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile,
1543                                MatchProperties const& match_properties)
1544 {
1545   // Determine whether we will copy the file.
1546   bool copy = true;
1547   if(!this->Always)
1548     {
1549     // If both files exist with the same time do not copy.
1550     if(!this->FileTimes.FileTimesDiffer(fromFile, toFile))
1551       {
1552       copy = false;
1553       }
1554     }
1555
1556   // Inform the user about this file installation.
1557   this->ReportCopy(toFile, TypeFile, copy);
1558
1559   // Copy the file.
1560   if(copy && !cmSystemTools::CopyAFile(fromFile, toFile, true))
1561     {
1562     cmOStringStream e;
1563     e << this->Name << " cannot copy file \"" << fromFile
1564       << "\" to \"" << toFile << "\".";
1565     this->FileCommand->SetError(e.str().c_str());
1566     return false;
1567     }
1568
1569   // Set the file modification time of the destination file.
1570   if(copy && !this->Always)
1571     {
1572     // Add write permission so we can set the file time.
1573     // Permissions are set unconditionally below anyway.
1574     mode_t perm = 0;
1575     if(cmSystemTools::GetPermissions(toFile, perm))
1576       {
1577       cmSystemTools::SetPermissions(toFile, perm | mode_owner_write);
1578       }
1579     if (!cmSystemTools::CopyFileTime(fromFile, toFile))
1580       {
1581       cmOStringStream e;
1582       e << this->Name << " cannot set modification time on \""
1583         << toFile << "\"";
1584       this->FileCommand->SetError(e.str().c_str());
1585       return false;
1586       }
1587     }
1588
1589   // Set permissions of the destination file.
1590   mode_t permissions = (match_properties.Permissions?
1591                         match_properties.Permissions : this->FilePermissions);
1592   if(!permissions)
1593     {
1594     // No permissions were explicitly provided but the user requested
1595     // that the source file permissions be used.
1596     cmSystemTools::GetPermissions(fromFile, permissions);
1597     }
1598   return this->SetPermissions(toFile, permissions);
1599 }
1600
1601 //----------------------------------------------------------------------------
1602 bool cmFileCopier::InstallDirectory(const char* source,
1603                                     const char* destination,
1604                                     MatchProperties const& match_properties)
1605 {
1606   // Inform the user about this directory installation.
1607   this->ReportCopy(destination, TypeDir, true);
1608
1609   // Make sure the destination directory exists.
1610   if(!cmSystemTools::MakeDirectory(destination))
1611     {
1612     cmOStringStream e;
1613     e << this->Name << " cannot make directory \"" << destination << "\": "
1614       << cmSystemTools::GetLastSystemError();
1615     this->FileCommand->SetError(e.str().c_str());
1616     return false;
1617     }
1618
1619   // Compute the requested permissions for the destination directory.
1620   mode_t permissions = (match_properties.Permissions?
1621                         match_properties.Permissions : this->DirPermissions);
1622   if(!permissions)
1623     {
1624     // No permissions were explicitly provided but the user requested
1625     // that the source directory permissions be used.
1626     cmSystemTools::GetPermissions(source, permissions);
1627     }
1628
1629   // Compute the set of permissions required on this directory to
1630   // recursively install files and subdirectories safely.
1631   mode_t required_permissions =
1632     mode_owner_read | mode_owner_write | mode_owner_execute;
1633
1634   // If the required permissions are specified it is safe to set the
1635   // final permissions now.  Otherwise we must add the required
1636   // permissions temporarily during file installation.
1637   mode_t permissions_before = 0;
1638   mode_t permissions_after = 0;
1639   if((permissions & required_permissions) == required_permissions)
1640     {
1641     permissions_before = permissions;
1642     }
1643   else
1644     {
1645     permissions_before = permissions | required_permissions;
1646     permissions_after = permissions;
1647     }
1648
1649   // Set the required permissions of the destination directory.
1650   if(!this->SetPermissions(destination, permissions_before))
1651     {
1652     return false;
1653     }
1654
1655   // Load the directory contents to traverse it recursively.
1656   cmsys::Directory dir;
1657   if(source && *source)
1658     {
1659     dir.Load(source);
1660     }
1661   unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles());
1662   for(unsigned long fileNum = 0; fileNum < numFiles; ++fileNum)
1663     {
1664     if(!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
1665          strcmp(dir.GetFile(fileNum), "..") == 0))
1666       {
1667       cmsys_stl::string fromPath = source;
1668       fromPath += "/";
1669       fromPath += dir.GetFile(fileNum);
1670       std::string toPath = destination;
1671       toPath += "/";
1672       toPath += dir.GetFile(fileNum);
1673       if(!this->Install(fromPath.c_str(), toPath.c_str()))
1674         {
1675         return false;
1676         }
1677       }
1678     }
1679
1680   // Set the requested permissions of the destination directory.
1681   return this->SetPermissions(destination, permissions_after);
1682 }
1683
1684 //----------------------------------------------------------------------------
1685 bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args)
1686 {
1687   cmFileCopier copier(this);
1688   return copier.Run(args);
1689 }
1690
1691 //----------------------------------------------------------------------------
1692 struct cmFileInstaller: public cmFileCopier
1693 {
1694   cmFileInstaller(cmFileCommand* command):
1695     cmFileCopier(command, "INSTALL"),
1696     InstallType(cmInstallType_FILES),
1697     Optional(false),
1698     DestDirLength(0)
1699     {
1700     // Installation does not use source permissions by default.
1701     this->UseSourcePermissions = false;
1702     // Check whether to copy files always or only if they have changed.
1703     this->Always =
1704       cmSystemTools::IsOn(cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS"));
1705     // Get the current manifest.
1706     this->Manifest =
1707       this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
1708     }
1709   ~cmFileInstaller()
1710     {
1711     // Save the updated install manifest.
1712     this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
1713                                   this->Manifest.c_str());
1714     }
1715
1716 protected:
1717   cmInstallType InstallType;
1718   bool Optional;
1719   int DestDirLength;
1720   std::string Rename;
1721
1722   std::string Manifest;
1723   void ManifestAppend(std::string const& file)
1724     {
1725     this->Manifest += ";";
1726     this->Manifest += file.substr(this->DestDirLength);
1727     }
1728
1729   virtual std::string const& ToName(std::string const& fromName)
1730     { return this->Rename.empty()? fromName : this->Rename; }
1731
1732   virtual void ReportCopy(const char* toFile, Type type, bool copy)
1733     {
1734     std::string message = (copy? "Installing: " : "Up-to-date: ");
1735     message += toFile;
1736     this->Makefile->DisplayStatus(message.c_str(), -1);
1737     if(type != TypeDir)
1738       {
1739       // Add the file to the manifest.
1740       this->ManifestAppend(toFile);
1741       }
1742     }
1743   virtual bool ReportMissing(const char* fromFile)
1744     {
1745     return (this->Optional ||
1746             this->cmFileCopier::ReportMissing(fromFile));
1747     }
1748   virtual bool Install(const char* fromFile, const char* toFile)
1749     {
1750     // Support installing from empty source to make a directory.
1751     if(this->InstallType == cmInstallType_DIRECTORY && !*fromFile)
1752       {
1753       return this->InstallDirectory(fromFile, toFile, MatchProperties());
1754       }
1755     return this->cmFileCopier::Install(fromFile, toFile);
1756     }
1757
1758   virtual bool Parse(std::vector<std::string> const& args);
1759   enum
1760   {
1761     DoingType = DoingLast1,
1762     DoingRename,
1763     DoingLast2
1764   };
1765   virtual bool CheckKeyword(std::string const& arg);
1766   virtual bool CheckValue(std::string const& arg);
1767   virtual void DefaultFilePermissions()
1768     {
1769     this->cmFileCopier::DefaultFilePermissions();
1770     // Add execute permissions based on the target type.
1771     switch(this->InstallType)
1772       {
1773       case cmInstallType_SHARED_LIBRARY:
1774       case cmInstallType_MODULE_LIBRARY:
1775         if(this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE"))
1776           {
1777           break;
1778           }
1779       case cmInstallType_EXECUTABLE:
1780       case cmInstallType_PROGRAMS:
1781         this->FilePermissions |= mode_owner_execute;
1782         this->FilePermissions |= mode_group_execute;
1783         this->FilePermissions |= mode_world_execute;
1784         break;
1785       default: break;
1786       }
1787     }
1788   bool GetTargetTypeFromString(const std::string& stype);
1789   bool HandleInstallDestination();
1790 };
1791
1792 //----------------------------------------------------------------------------
1793 bool cmFileInstaller::Parse(std::vector<std::string> const& args)
1794 {
1795   if(!this->cmFileCopier::Parse(args))
1796     {
1797     return false;
1798     }
1799
1800   if(!this->Rename.empty())
1801     {
1802     if(this->InstallType != cmInstallType_FILES &&
1803        this->InstallType != cmInstallType_PROGRAMS)
1804       {
1805       this->FileCommand->SetError("INSTALL option RENAME may be used "
1806                                   "only with FILES or PROGRAMS.");
1807       return false;
1808       }
1809     if(this->Files.size() > 1)
1810       {
1811       this->FileCommand->SetError("INSTALL option RENAME may be used "
1812                                   "only with one file.");
1813       return false;
1814       }
1815     }
1816
1817   if(!this->HandleInstallDestination())
1818     {
1819     return false;
1820     }
1821
1822   return true;
1823 }
1824
1825 //----------------------------------------------------------------------------
1826 bool cmFileInstaller::CheckKeyword(std::string const& arg)
1827 {
1828   if(arg == "TYPE")
1829     {
1830     if(this->CurrentMatchRule)
1831       {
1832       this->NotAfterMatch(arg);
1833       }
1834     else
1835       {
1836       this->Doing = DoingType;
1837       }
1838     }
1839   else if(arg == "FILES")
1840     {
1841     if(this->CurrentMatchRule)
1842       {
1843       this->NotAfterMatch(arg);
1844       }
1845     else
1846       {
1847       this->Doing = DoingFiles;
1848       }
1849     }
1850   else if(arg == "RENAME")
1851     {
1852     if(this->CurrentMatchRule)
1853       {
1854       this->NotAfterMatch(arg);
1855       }
1856     else
1857       {
1858       this->Doing = DoingRename;
1859       }
1860     }
1861   else if(arg == "OPTIONAL")
1862     {
1863     if(this->CurrentMatchRule)
1864       {
1865       this->NotAfterMatch(arg);
1866       }
1867     else
1868       {
1869       this->Doing = DoingNone;
1870       this->Optional = true;
1871       }
1872     }
1873   else if(arg == "PERMISSIONS")
1874     {
1875     if(this->CurrentMatchRule)
1876       {
1877       this->Doing = DoingPermissionsMatch;
1878       }
1879     else
1880       {
1881       // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
1882       this->Doing = DoingPermissionsFile;
1883       this->UseGivenPermissionsFile = true;
1884       }
1885     }
1886   else if(arg == "DIR_PERMISSIONS")
1887     {
1888     if(this->CurrentMatchRule)
1889       {
1890       this->NotAfterMatch(arg);
1891       }
1892     else
1893       {
1894       // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
1895       this->Doing = DoingPermissionsDir;
1896       this->UseGivenPermissionsDir = true;
1897       }
1898     }
1899   else if(arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
1900           arg == "PROPERTIES")
1901     {
1902     cmOStringStream e;
1903     e << "INSTALL called with old-style " << arg << " argument.  "
1904       << "This script was generated with an older version of CMake.  "
1905       << "Re-run this cmake version on your build tree.";
1906     this->FileCommand->SetError(e.str().c_str());
1907     this->Doing = DoingError;
1908     }
1909   else
1910     {
1911     return this->cmFileCopier::CheckKeyword(arg);
1912     }
1913   return true;
1914 }
1915
1916 //----------------------------------------------------------------------------
1917 bool cmFileInstaller::CheckValue(std::string const& arg)
1918 {
1919   switch(this->Doing)
1920     {
1921     case DoingType:
1922       if(!this->GetTargetTypeFromString(arg))
1923         {
1924         this->Doing = DoingError;
1925         }
1926       break;
1927     case DoingRename:
1928       this->Rename = arg;
1929       break;
1930     default:
1931       return this->cmFileCopier::CheckValue(arg);
1932     }
1933   return true;
1934 }
1935
1936 //----------------------------------------------------------------------------
1937 bool cmFileInstaller
1938 ::GetTargetTypeFromString(const std::string& stype)
1939 {
1940   if ( stype == "EXECUTABLE" )
1941     {
1942     this->InstallType = cmInstallType_EXECUTABLE;
1943     }
1944   else if ( stype == "FILE" )
1945     {
1946     this->InstallType = cmInstallType_FILES;
1947     }
1948   else if ( stype == "PROGRAM" )
1949     {
1950     this->InstallType = cmInstallType_PROGRAMS;
1951     }
1952   else if ( stype == "STATIC_LIBRARY" )
1953     {
1954     this->InstallType = cmInstallType_STATIC_LIBRARY;
1955     }
1956   else if ( stype == "SHARED_LIBRARY" )
1957     {
1958     this->InstallType = cmInstallType_SHARED_LIBRARY;
1959     }
1960   else if ( stype == "MODULE" )
1961     {
1962     this->InstallType = cmInstallType_MODULE_LIBRARY;
1963     }
1964   else if ( stype == "DIRECTORY" )
1965     {
1966     this->InstallType = cmInstallType_DIRECTORY;
1967     }
1968   else
1969     {
1970     cmOStringStream e;
1971     e << "Option TYPE given uknown value \"" << stype << "\".";
1972     this->FileCommand->SetError(e.str().c_str());
1973     return false;
1974     }
1975   return true;
1976 }
1977
1978 //----------------------------------------------------------------------------
1979 bool cmFileInstaller::HandleInstallDestination()
1980 {
1981   std::string& destination = this->Destination;
1982
1983   // allow for / to be a valid destination
1984   if ( destination.size() < 2 && destination != "/" )
1985     {
1986     this->FileCommand->SetError("called with inapropriate arguments. "
1987         "No DESTINATION provided or .");
1988     return false;
1989     }
1990
1991   const char* destdir = cmSystemTools::GetEnv("DESTDIR");
1992   if ( destdir && *destdir )
1993     {
1994     std::string sdestdir = destdir;
1995     cmSystemTools::ConvertToUnixSlashes(sdestdir);
1996     char ch1 = destination[0];
1997     char ch2 = destination[1];
1998     char ch3 = 0;
1999     if ( destination.size() > 2 )
2000       {
2001       ch3 = destination[2];
2002       }
2003     int skip = 0;
2004     if ( ch1 != '/' )
2005       {
2006       int relative = 0;
2007       if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
2008              ch2 == ':' )
2009         {
2010         // Assume windows
2011         // let's do some destdir magic:
2012         skip = 2;
2013         if ( ch3 != '/' )
2014           {
2015           relative = 1;
2016           }
2017         }
2018       else
2019         {
2020         relative = 1;
2021         }
2022       if ( relative )
2023         {
2024         // This is relative path on unix or windows. Since we are doing
2025         // destdir, this case does not make sense.
2026         this->FileCommand->SetError(
2027           "called with relative DESTINATION. This "
2028           "does not make sense when using DESTDIR. Specify "
2029           "absolute path or remove DESTDIR environment variable.");
2030         return false;
2031         }
2032       }
2033     else
2034       {
2035       if ( ch2 == '/' )
2036         {
2037         // looks like a network path.
2038         std::string message = "called with network path DESTINATION. This "
2039           "does not make sense when using DESTDIR. Specify local "
2040           "absolute path or remove DESTDIR environment variable."
2041           "\nDESTINATION=\n";
2042         message += destination;
2043         this->FileCommand->SetError(message.c_str());
2044         return false;
2045         }
2046       }
2047     destination = sdestdir + (destination.c_str() + skip);
2048     this->DestDirLength = int(sdestdir.size());
2049     }
2050
2051   if ( !cmSystemTools::FileExists(destination.c_str()) )
2052     {
2053     if ( !cmSystemTools::MakeDirectory(destination.c_str()) )
2054       {
2055       std::string errstring = "cannot create directory: " + destination +
2056           ". Maybe need administrative privileges.";
2057       this->FileCommand->SetError(errstring.c_str());
2058       return false;
2059       }
2060     }
2061   if ( !cmSystemTools::FileIsDirectory(destination.c_str()) )
2062     {
2063     std::string errstring = "INSTALL destination: " + destination +
2064         " is not a directory.";
2065     this->FileCommand->SetError(errstring.c_str());
2066     return false;
2067     }
2068   return true;
2069 }
2070
2071 //----------------------------------------------------------------------------
2072 bool
2073 cmFileCommand::HandleRPathChangeCommand(std::vector<std::string> const& args)
2074 {
2075   // Evaluate arguments.
2076   const char* file = 0;
2077   const char* oldRPath = 0;
2078   const char* newRPath = 0;
2079   enum Doing { DoingNone, DoingFile, DoingOld, DoingNew };
2080   Doing doing = DoingNone;
2081   for(unsigned int i=1; i < args.size(); ++i)
2082     {
2083     if(args[i] == "OLD_RPATH")
2084       {
2085       doing = DoingOld;
2086       }
2087     else if(args[i] == "NEW_RPATH")
2088       {
2089       doing = DoingNew;
2090       }
2091     else if(args[i] == "FILE")
2092       {
2093       doing = DoingFile;
2094       }
2095     else if(doing == DoingFile)
2096       {
2097       file = args[i].c_str();
2098       doing = DoingNone;
2099       }
2100     else if(doing == DoingOld)
2101       {
2102       oldRPath = args[i].c_str();
2103       doing = DoingNone;
2104       }
2105     else if(doing == DoingNew)
2106       {
2107       newRPath = args[i].c_str();
2108       doing = DoingNone;
2109       }
2110     else
2111       {
2112       cmOStringStream e;
2113       e << "RPATH_CHANGE given unknown argument " << args[i];
2114       this->SetError(e.str().c_str());
2115       return false;
2116       }
2117     }
2118   if(!file)
2119     {
2120     this->SetError("RPATH_CHANGE not given FILE option.");
2121     return false;
2122     }
2123   if(!oldRPath)
2124     {
2125     this->SetError("RPATH_CHANGE not given OLD_RPATH option.");
2126     return false;
2127     }
2128   if(!newRPath)
2129     {
2130     this->SetError("RPATH_CHANGE not given NEW_RPATH option.");
2131     return false;
2132     }
2133   if(!cmSystemTools::FileExists(file, true))
2134     {
2135     cmOStringStream e;
2136     e << "RPATH_CHANGE given FILE \"" << file << "\" that does not exist.";
2137     this->SetError(e.str().c_str());
2138     return false;
2139     }
2140   bool success = true;
2141   cmSystemToolsFileTime* ft = cmSystemTools::FileTimeNew();
2142   bool have_ft = cmSystemTools::FileTimeGet(file, ft);
2143   std::string emsg;
2144   bool changed;
2145   if(!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, &emsg, &changed))
2146     {
2147     cmOStringStream e;
2148     e << "RPATH_CHANGE could not write new RPATH:\n"
2149       << "  " << newRPath << "\n"
2150       << "to the file:\n"
2151       << "  " << file << "\n"
2152       << emsg;
2153     this->SetError(e.str().c_str());
2154     success = false;
2155     }
2156   if(success)
2157     {
2158     if(changed)
2159       {
2160       std::string message = "Set runtime path of \"";
2161       message += file;
2162       message += "\" to \"";
2163       message += newRPath;
2164       message += "\"";
2165       this->Makefile->DisplayStatus(message.c_str(), -1);
2166       }
2167     if(have_ft)
2168       {
2169       cmSystemTools::FileTimeSet(file, ft);
2170       }
2171     }
2172   cmSystemTools::FileTimeDelete(ft);
2173   return success;
2174 }
2175
2176 //----------------------------------------------------------------------------
2177 bool
2178 cmFileCommand::HandleRPathRemoveCommand(std::vector<std::string> const& args)
2179 {
2180   // Evaluate arguments.
2181   const char* file = 0;
2182   enum Doing { DoingNone, DoingFile };
2183   Doing doing = DoingNone;
2184   for(unsigned int i=1; i < args.size(); ++i)
2185     {
2186     if(args[i] == "FILE")
2187       {
2188       doing = DoingFile;
2189       }
2190     else if(doing == DoingFile)
2191       {
2192       file = args[i].c_str();
2193       doing = DoingNone;
2194       }
2195     else
2196       {
2197       cmOStringStream e;
2198       e << "RPATH_REMOVE given unknown argument " << args[i];
2199       this->SetError(e.str().c_str());
2200       return false;
2201       }
2202     }
2203   if(!file)
2204     {
2205     this->SetError("RPATH_REMOVE not given FILE option.");
2206     return false;
2207     }
2208   if(!cmSystemTools::FileExists(file, true))
2209     {
2210     cmOStringStream e;
2211     e << "RPATH_REMOVE given FILE \"" << file << "\" that does not exist.";
2212     this->SetError(e.str().c_str());
2213     return false;
2214     }
2215   bool success = true;
2216   cmSystemToolsFileTime* ft = cmSystemTools::FileTimeNew();
2217   bool have_ft = cmSystemTools::FileTimeGet(file, ft);
2218   std::string emsg;
2219   bool removed;
2220   if(!cmSystemTools::RemoveRPath(file, &emsg, &removed))
2221     {
2222     cmOStringStream e;
2223     e << "RPATH_REMOVE could not remove RPATH from file:\n"
2224       << "  " << file << "\n"
2225       << emsg;
2226     this->SetError(e.str().c_str());
2227     success = false;
2228     }
2229   if(success)
2230     {
2231     if(removed)
2232       {
2233       std::string message = "Removed runtime path from \"";
2234       message += file;
2235       message += "\"";
2236       this->Makefile->DisplayStatus(message.c_str(), -1);
2237       }
2238     if(have_ft)
2239       {
2240       cmSystemTools::FileTimeSet(file, ft);
2241       }
2242     }
2243   cmSystemTools::FileTimeDelete(ft);
2244   return success;
2245 }
2246
2247 //----------------------------------------------------------------------------
2248 bool
2249 cmFileCommand::HandleRPathCheckCommand(std::vector<std::string> const& args)
2250 {
2251   // Evaluate arguments.
2252   const char* file = 0;
2253   const char* rpath = 0;
2254   enum Doing { DoingNone, DoingFile, DoingRPath };
2255   Doing doing = DoingNone;
2256   for(unsigned int i=1; i < args.size(); ++i)
2257     {
2258     if(args[i] == "RPATH")
2259       {
2260       doing = DoingRPath;
2261       }
2262     else if(args[i] == "FILE")
2263       {
2264       doing = DoingFile;
2265       }
2266     else if(doing == DoingFile)
2267       {
2268       file = args[i].c_str();
2269       doing = DoingNone;
2270       }
2271     else if(doing == DoingRPath)
2272       {
2273       rpath = args[i].c_str();
2274       doing = DoingNone;
2275       }
2276     else
2277       {
2278       cmOStringStream e;
2279       e << "RPATH_CHECK given unknown argument " << args[i];
2280       this->SetError(e.str().c_str());
2281       return false;
2282       }
2283     }
2284   if(!file)
2285     {
2286     this->SetError("RPATH_CHECK not given FILE option.");
2287     return false;
2288     }
2289   if(!rpath)
2290     {
2291     this->SetError("RPATH_CHECK not given RPATH option.");
2292     return false;
2293     }
2294
2295   // If the file exists but does not have the desired RPath then
2296   // delete it.  This is used during installation to re-install a file
2297   // if its RPath will change.
2298   if(cmSystemTools::FileExists(file, true) &&
2299      !cmSystemTools::CheckRPath(file, rpath))
2300     {
2301     cmSystemTools::RemoveFile(file);
2302     }
2303
2304   return true;
2305 }
2306
2307 //----------------------------------------------------------------------------
2308 bool cmFileCommand::HandleInstallCommand(std::vector<std::string> const& args)
2309 {
2310   cmFileInstaller installer(this);
2311   return installer.Run(args);
2312 }
2313
2314 //----------------------------------------------------------------------------
2315 bool cmFileCommand::HandleRelativePathCommand(
2316   std::vector<std::string> const& args)
2317 {
2318   if(args.size() != 4 )
2319     {
2320     this->SetError("RELATIVE_PATH called with incorrect number of arguments");
2321     return false;
2322     }
2323
2324   const std::string& outVar = args[1];
2325   const std::string& directoryName = args[2];
2326   const std::string& fileName = args[3];
2327
2328   if(!cmSystemTools::FileIsFullPath(directoryName.c_str()))
2329     {
2330     std::string errstring =
2331       "RELATIVE_PATH must be passed a full path to the directory: "
2332       + directoryName;
2333     this->SetError(errstring.c_str());
2334     return false;
2335     }
2336   if(!cmSystemTools::FileIsFullPath(fileName.c_str()))
2337     {
2338     std::string errstring =
2339       "RELATIVE_PATH must be passed a full path to the file: "
2340       + fileName;
2341     this->SetError(errstring.c_str());
2342     return false;
2343     }
2344
2345   std::string res = cmSystemTools::RelativePath(directoryName.c_str(),
2346                                                 fileName.c_str());
2347   this->Makefile->AddDefinition(outVar.c_str(),
2348     res.c_str());
2349   return true;
2350 }
2351
2352
2353 //----------------------------------------------------------------------------
2354 bool cmFileCommand::HandleRename(std::vector<std::string> const& args)
2355 {
2356   if(args.size() != 3)
2357     {
2358     this->SetError("RENAME given incorrect number of arguments.");
2359     return false;
2360     }
2361
2362   // Compute full path for old and new names.
2363   std::string oldname = args[1];
2364   if(!cmsys::SystemTools::FileIsFullPath(oldname.c_str()))
2365     {
2366     oldname = this->Makefile->GetCurrentDirectory();
2367     oldname += "/" + args[1];
2368     }
2369   std::string newname = args[2];
2370   if(!cmsys::SystemTools::FileIsFullPath(newname.c_str()))
2371     {
2372     newname = this->Makefile->GetCurrentDirectory();
2373     newname += "/" + args[2];
2374     }
2375
2376   if(!cmSystemTools::RenameFile(oldname.c_str(), newname.c_str()))
2377     {
2378     std::string err = cmSystemTools::GetLastSystemError();
2379     cmOStringStream e;
2380     e << "RENAME failed to rename\n"
2381       << "  " << oldname << "\n"
2382       << "to\n"
2383       << "  " << newname << "\n"
2384       << "because: " << err << "\n";
2385     this->SetError(e.str().c_str());
2386     return false;
2387     }
2388   return true;
2389 }
2390
2391
2392 //----------------------------------------------------------------------------
2393 bool cmFileCommand::HandleRemove(std::vector<std::string> const& args,
2394                                  bool recurse)
2395 {
2396
2397   std::string message;
2398   std::vector<std::string>::const_iterator i = args.begin();
2399
2400   i++; // Get rid of subcommand
2401   for(;i != args.end(); ++i)
2402     {
2403     std::string fileName = *i;
2404     if(!cmsys::SystemTools::FileIsFullPath(fileName.c_str()))
2405       {
2406       fileName = this->Makefile->GetCurrentDirectory();
2407       fileName += "/" + *i;
2408       }
2409
2410     if(cmSystemTools::FileIsDirectory(fileName.c_str()) && recurse)
2411       {
2412       cmSystemTools::RemoveADirectory(fileName.c_str());
2413       }
2414     else
2415       {
2416       cmSystemTools::RemoveFile(fileName.c_str());
2417       }
2418     }
2419   return true;
2420 }
2421
2422 //----------------------------------------------------------------------------
2423 bool cmFileCommand::HandleCMakePathCommand(std::vector<std::string>
2424                                            const& args,
2425                                            bool nativePath)
2426 {
2427   std::vector<std::string>::const_iterator i = args.begin();
2428   if(args.size() != 3)
2429     {
2430     this->SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
2431       "called with exactly three arguments.");
2432     return false;
2433     }
2434   i++; // Get rid of subcommand
2435 #if defined(_WIN32) && !defined(__CYGWIN__)
2436   char pathSep = ';';
2437 #else
2438   char pathSep = ':';
2439 #endif
2440   std::vector<cmsys::String> path = cmSystemTools::SplitString(i->c_str(),
2441                                                              pathSep);
2442   i++;
2443   const char* var =  i->c_str();
2444   std::string value;
2445   for(std::vector<cmsys::String>::iterator j = path.begin();
2446       j != path.end(); ++j)
2447     {
2448     if(j != path.begin())
2449       {
2450       value += ";";
2451       }
2452     if(!nativePath)
2453       {
2454       cmSystemTools::ConvertToUnixSlashes(*j);
2455       }
2456     else
2457       {
2458       *j = cmSystemTools::ConvertToOutputPath(j->c_str());
2459       // remove double quotes in the path
2460       cmsys::String& s = *j;
2461
2462       if(s.size() > 1 && s[0] == '\"' && s[s.size()-1] == '\"')
2463         {
2464         s = s.substr(1,s.size()-2);
2465         }
2466       }
2467     value += *j;
2468     }
2469   this->Makefile->AddDefinition(var, value.c_str());
2470   return true;
2471 }
2472
2473
2474 #if defined(CMAKE_BUILD_WITH_CMAKE)
2475
2476 // Stuff for curl download/upload
2477 typedef std::vector<char> cmFileCommandVectorOfChar;
2478
2479 namespace {
2480
2481   size_t
2482   cmWriteToFileCallback(void *ptr, size_t size, size_t nmemb,
2483                         void *data)
2484     {
2485     register int realsize = (int)(size * nmemb);
2486     std::ofstream* fout = static_cast<std::ofstream*>(data);
2487     const char* chPtr = static_cast<char*>(ptr);
2488     fout->write(chPtr, realsize);
2489     return realsize;
2490     }
2491
2492
2493   size_t
2494   cmWriteToMemoryCallback(void *ptr, size_t size, size_t nmemb,
2495                           void *data)
2496     {
2497     register int realsize = (int)(size * nmemb);
2498     cmFileCommandVectorOfChar *vec
2499       = static_cast<cmFileCommandVectorOfChar*>(data);
2500     const char* chPtr = static_cast<char*>(ptr);
2501     vec->insert(vec->end(), chPtr, chPtr + realsize);
2502     return realsize;
2503     }
2504
2505
2506   static size_t
2507   cmFileCommandCurlDebugCallback(CURL *, curl_infotype, char *chPtr,
2508                                  size_t size, void *data)
2509     {
2510     cmFileCommandVectorOfChar *vec
2511       = static_cast<cmFileCommandVectorOfChar*>(data);
2512     vec->insert(vec->end(), chPtr, chPtr + size);
2513     return size;
2514     }
2515
2516
2517   class cURLProgressHelper
2518   {
2519   public:
2520     cURLProgressHelper(cmFileCommand *fc, const char *text)
2521       {
2522       this->CurrentPercentage = -1;
2523       this->FileCommand = fc;
2524       this->Text = text;
2525       }
2526
2527     bool UpdatePercentage(double value, double total, std::string &status)
2528       {
2529       int OldPercentage = this->CurrentPercentage;
2530
2531       if (total > 0.0)
2532         {
2533         this->CurrentPercentage = static_cast<int>(value/total*100.0 + 0.5);
2534         }
2535
2536       bool updated = (OldPercentage != this->CurrentPercentage);
2537
2538       if (updated)
2539         {
2540         cmOStringStream oss;
2541         oss << "[" << this->Text << " " << this->CurrentPercentage
2542             << "% complete]";
2543         status = oss.str();
2544         }
2545
2546       return updated;
2547       }
2548
2549     cmFileCommand *GetFileCommand()
2550       {
2551       return this->FileCommand;
2552       }
2553
2554   private:
2555     int CurrentPercentage;
2556     cmFileCommand *FileCommand;
2557     std::string Text;
2558   };
2559
2560
2561   static int
2562   cmFileDownloadProgressCallback(void *clientp,
2563                                  double dltotal, double dlnow,
2564                                  double ultotal, double ulnow)
2565     {
2566     cURLProgressHelper *helper =
2567       reinterpret_cast<cURLProgressHelper *>(clientp);
2568
2569     static_cast<void>(ultotal);
2570     static_cast<void>(ulnow);
2571
2572     std::string status;
2573     if (helper->UpdatePercentage(dlnow, dltotal, status))
2574       {
2575       cmFileCommand *fc = helper->GetFileCommand();
2576       cmMakefile *mf = fc->GetMakefile();
2577       mf->DisplayStatus(status.c_str(), -1);
2578       }
2579
2580     return 0;
2581     }
2582
2583
2584   static int
2585   cmFileUploadProgressCallback(void *clientp,
2586                                double dltotal, double dlnow,
2587                                double ultotal, double ulnow)
2588     {
2589     cURLProgressHelper *helper =
2590     reinterpret_cast<cURLProgressHelper *>(clientp);
2591
2592     static_cast<void>(dltotal);
2593     static_cast<void>(dlnow);
2594
2595     std::string status;
2596     if (helper->UpdatePercentage(ulnow, ultotal, status))
2597       {
2598       cmFileCommand *fc = helper->GetFileCommand();
2599       cmMakefile *mf = fc->GetMakefile();
2600       mf->DisplayStatus(status.c_str(), -1);
2601       }
2602
2603     return 0;
2604     }
2605 }
2606
2607
2608 namespace {
2609
2610   class cURLEasyGuard
2611   {
2612   public:
2613     cURLEasyGuard(CURL * easy)
2614       : Easy(easy)
2615       {}
2616
2617     ~cURLEasyGuard(void)
2618       {
2619         if (this->Easy)
2620           {
2621           ::curl_easy_cleanup(this->Easy);
2622           }
2623       }
2624     
2625     inline void release(void) 
2626       {
2627         this->Easy = 0;
2628         return;
2629       }
2630     
2631   private:
2632     ::CURL * Easy;
2633   };
2634   
2635 }
2636 #endif
2637
2638
2639 #define check_curl_result(result, errstr) \
2640   if (result != CURLE_OK)                 \
2641     {                                     \
2642     std::string e(errstr);                \
2643     e += ::curl_easy_strerror(result);    \
2644     this->SetError(e.c_str());            \
2645     return false;                         \
2646     }
2647
2648
2649 bool
2650 cmFileCommand::HandleDownloadCommand(std::vector<std::string> const& args)
2651 {
2652 #if defined(CMAKE_BUILD_WITH_CMAKE)
2653   std::vector<std::string>::const_iterator i = args.begin();
2654   if(args.size() < 3)
2655     {
2656     this->SetError("DOWNLOAD must be called with at least three arguments.");
2657     return false;
2658     }
2659   ++i; // Get rid of subcommand
2660   std::string url = *i;
2661   ++i;
2662   std::string file = *i;
2663   ++i;
2664
2665   long timeout = 0;
2666   long inactivity_timeout = 0;
2667   std::string verboseLog;
2668   std::string statusVar;
2669   std::string expectedMD5sum;
2670   bool showProgress = false;
2671
2672   while(i != args.end())
2673     {
2674     if(*i == "TIMEOUT")
2675       {
2676       ++i;
2677       if(i != args.end())
2678         {
2679         timeout = atol(i->c_str());
2680         }
2681       else
2682         {
2683         this->SetError("DOWNLOAD missing time for TIMEOUT.");
2684         return false;
2685         }
2686       }
2687     else if(*i == "INACTIVITY_TIMEOUT")
2688       {
2689       ++i;
2690       if(i != args.end())
2691         {
2692         inactivity_timeout = atol(i->c_str());
2693         }
2694       else
2695         {
2696         this->SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
2697         return false;
2698         }
2699       }
2700     else if(*i == "LOG")
2701       {
2702       ++i;
2703       if( i == args.end())
2704         {
2705         this->SetError("DOWNLOAD missing VAR for LOG.");
2706         return false;
2707         }
2708       verboseLog = *i;
2709       }
2710     else if(*i == "STATUS")
2711       {
2712       ++i;
2713       if( i == args.end())
2714         {
2715         this->SetError("DOWNLOAD missing VAR for STATUS.");
2716         return false;
2717         }
2718       statusVar = *i;
2719       }
2720     else if(*i == "EXPECTED_MD5")
2721       {
2722       ++i;
2723       if( i == args.end())
2724         {
2725         this->SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
2726         return false;
2727         }
2728       expectedMD5sum = cmSystemTools::LowerCase(*i);
2729       }
2730     else if(*i == "SHOW_PROGRESS")
2731       {
2732       showProgress = true;
2733       }
2734     ++i;
2735     }
2736
2737   // If file exists already, and caller specified an expected md5 sum,
2738   // and the existing file already has the expected md5 sum, then simply
2739   // return.
2740   //
2741   if(cmSystemTools::FileExists(file.c_str()) &&
2742     !expectedMD5sum.empty())
2743     {
2744     char computedMD5[32];
2745
2746     if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5))
2747       {
2748       this->SetError("DOWNLOAD cannot compute MD5 sum on pre-existing file");
2749       return false;
2750       }
2751
2752     std::string actualMD5sum = cmSystemTools::LowerCase(
2753       std::string(computedMD5, 32));
2754
2755     if (expectedMD5sum == actualMD5sum)
2756       {
2757       if(statusVar.size())
2758         {
2759         cmOStringStream result;
2760         result << (int)0 << ";\""
2761           "returning early: file already exists with expected MD5 sum\"";
2762         this->Makefile->AddDefinition(statusVar.c_str(),
2763                                       result.str().c_str());
2764         }
2765
2766       return true;
2767       }
2768     }
2769
2770   // Make sure parent directory exists so we can write to the file
2771   // as we receive downloaded bits from curl...
2772   //
2773   std::string dir = cmSystemTools::GetFilenamePath(file.c_str());
2774   if(!cmSystemTools::FileExists(dir.c_str()) &&
2775      !cmSystemTools::MakeDirectory(dir.c_str()))
2776     {
2777     std::string errstring = "DOWNLOAD error: cannot create directory '"
2778       + dir + "' - Specify file by full path name and verify that you "
2779       "have directory creation and file write privileges.";
2780     this->SetError(errstring.c_str());
2781     return false;
2782     }
2783
2784   std::ofstream fout(file.c_str(), std::ios::binary);
2785   if(!fout)
2786     {
2787     this->SetError("DOWNLOAD cannot open file for write.");
2788     return false;
2789     }
2790
2791   ::CURL *curl;
2792   ::curl_global_init(CURL_GLOBAL_DEFAULT);
2793   curl = ::curl_easy_init();
2794   if(!curl)
2795     {
2796     this->SetError("DOWNLOAD error initializing curl.");
2797     return false;
2798     }
2799
2800   cURLEasyGuard g_curl(curl);
2801
2802   ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2803   check_curl_result(res, "DOWNLOAD cannot set url: ");
2804
2805   // enable HTTP ERROR parsing
2806   res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2807   check_curl_result(res, "DOWNLOAD cannot set http failure option: ");
2808
2809   res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
2810                            cmWriteToFileCallback);
2811   check_curl_result(res, "DOWNLOAD cannot set write function: ");
2812
2813   res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
2814                            cmFileCommandCurlDebugCallback);
2815   check_curl_result(res, "DOWNLOAD cannot set debug function: ");
2816
2817   cmFileCommandVectorOfChar chunkDebug;
2818
2819   res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&fout);
2820   check_curl_result(res, "DOWNLOAD cannot set write data: ");
2821
2822   res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
2823   check_curl_result(res, "DOWNLOAD cannot set debug data: ");
2824
2825   res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
2826   check_curl_result(res, "DOWNLOAD cannot set follow-redirect option: ");
2827
2828   if(verboseLog.size())
2829     {
2830     res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
2831     check_curl_result(res, "DOWNLOAD cannot set verbose: ");
2832     }
2833
2834   if(timeout > 0)
2835     {
2836     res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout );
2837     check_curl_result(res, "DOWNLOAD cannot set timeout: ");
2838     }
2839
2840   if(inactivity_timeout > 0)
2841     {
2842     // Give up if there is no progress for a long time.
2843     ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
2844     ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
2845     }
2846
2847   // Need the progress helper's scope to last through the duration of
2848   // the curl_easy_perform call... so this object is declared at function
2849   // scope intentionally, rather than inside the "if(showProgress)"
2850   // block...
2851   //
2852   cURLProgressHelper helper(this, "download");
2853
2854   if(showProgress)
2855     {
2856     res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2857     check_curl_result(res, "DOWNLOAD cannot set noprogress value: ");
2858
2859     res = ::curl_easy_setopt(curl,
2860       CURLOPT_PROGRESSFUNCTION, cmFileDownloadProgressCallback);
2861     check_curl_result(res, "DOWNLOAD cannot set progress function: ");
2862
2863     res = ::curl_easy_setopt(curl,
2864       CURLOPT_PROGRESSDATA, reinterpret_cast<void*>(&helper));
2865     check_curl_result(res, "DOWNLOAD cannot set progress data: ");
2866     }
2867
2868   res = ::curl_easy_perform(curl);
2869
2870   /* always cleanup */
2871   g_curl.release();
2872   ::curl_easy_cleanup(curl);
2873
2874   if(statusVar.size())
2875     {
2876     cmOStringStream result;
2877     result << (int)res << ";\"" << ::curl_easy_strerror(res) << "\"";
2878     this->Makefile->AddDefinition(statusVar.c_str(),
2879                                   result.str().c_str());
2880     }
2881
2882   ::curl_global_cleanup();
2883
2884   // Explicitly flush/close so we can measure the md5 accurately.
2885   //
2886   fout.flush();
2887   fout.close();
2888
2889   // Verify MD5 sum if requested:
2890   //
2891   if (!expectedMD5sum.empty())
2892     {
2893     char computedMD5[32];
2894
2895     if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5))
2896       {
2897       this->SetError("DOWNLOAD cannot compute MD5 sum on downloaded file");
2898       return false;
2899       }
2900
2901     std::string actualMD5sum = cmSystemTools::LowerCase(
2902       std::string(computedMD5, 32));
2903
2904     if (expectedMD5sum != actualMD5sum)
2905       {
2906       cmOStringStream oss;
2907       oss << "DOWNLOAD MD5 mismatch" << std::endl
2908         << "  for file: [" << file << "]" << std::endl
2909         << "    expected MD5 sum: [" << expectedMD5sum << "]" << std::endl
2910         << "      actual MD5 sum: [" << actualMD5sum << "]" << std::endl
2911         ;
2912       this->SetError(oss.str().c_str());
2913       return false;
2914       }
2915     }
2916
2917   if(chunkDebug.size())
2918     {
2919     chunkDebug.push_back(0);
2920     if(CURLE_OPERATION_TIMEOUTED == res)
2921       {
2922       std::string output = &*chunkDebug.begin();
2923
2924       if(verboseLog.size())
2925         {
2926         this->Makefile->AddDefinition(verboseLog.c_str(),
2927                                       &*chunkDebug.begin());
2928         }
2929       }
2930
2931     this->Makefile->AddDefinition(verboseLog.c_str(),
2932                                   &*chunkDebug.begin());
2933     }
2934
2935   return true;
2936 #else
2937   this->SetError("DOWNLOAD not supported by bootstrap cmake.");
2938   return false;
2939 #endif
2940 }
2941
2942
2943 bool
2944 cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
2945 {
2946 #if defined(CMAKE_BUILD_WITH_CMAKE)
2947   if(args.size() < 3)
2948     {
2949     this->SetError("UPLOAD must be called with at least three arguments.");
2950     return false;
2951     }
2952   std::vector<std::string>::const_iterator i = args.begin();
2953   ++i;
2954   std::string filename = *i;
2955   ++i;
2956   std::string url = *i;
2957   ++i;
2958
2959   long timeout = 0;
2960   long inactivity_timeout = 0;
2961   std::string logVar;
2962   std::string statusVar;
2963   bool showProgress = false;
2964
2965   while(i != args.end())
2966     {
2967     if(*i == "TIMEOUT")
2968       {
2969       ++i;
2970       if(i != args.end())
2971         {
2972         timeout = atol(i->c_str());
2973         }
2974       else
2975         {
2976         this->SetError("UPLOAD missing time for TIMEOUT.");
2977         return false;
2978         }
2979       }
2980     else if(*i == "INACTIVITY_TIMEOUT")
2981       {
2982       ++i;
2983       if(i != args.end())
2984         {
2985         inactivity_timeout = atol(i->c_str());
2986         }
2987       else
2988         {
2989         this->SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
2990         return false;
2991         }
2992       }
2993     else if(*i == "LOG")
2994       {
2995       ++i;
2996       if( i == args.end())
2997         {
2998         this->SetError("UPLOAD missing VAR for LOG.");
2999         return false;
3000         }
3001       logVar = *i;
3002       }
3003     else if(*i == "STATUS")
3004       {
3005       ++i;
3006       if( i == args.end())
3007         {
3008         this->SetError("UPLOAD missing VAR for STATUS.");
3009         return false;
3010         }
3011       statusVar = *i;
3012       }
3013     else if(*i == "SHOW_PROGRESS")
3014       {
3015       showProgress = true;
3016       }
3017
3018     ++i;
3019     }
3020
3021   // Open file for reading:
3022   //
3023   FILE *fin = fopen(filename.c_str(), "rb");
3024   if(!fin)
3025     {
3026     std::string errStr = "UPLOAD cannot open file '";
3027     errStr += filename + "' for reading.";
3028     this->SetError(errStr.c_str());
3029     return false;
3030     }
3031
3032   struct stat st;
3033   if(::stat(filename.c_str(), &st))
3034     {
3035     std::string errStr = "UPLOAD cannot stat file '";
3036     errStr += filename + "'.";
3037     this->SetError(errStr.c_str());
3038     fclose(fin);
3039     return false;
3040     }
3041
3042   ::CURL *curl;
3043   ::curl_global_init(CURL_GLOBAL_DEFAULT);
3044   curl = ::curl_easy_init();
3045   if(!curl)
3046     {
3047     this->SetError("UPLOAD error initializing curl.");
3048     fclose(fin);
3049     return false;
3050     }
3051
3052   cURLEasyGuard g_curl(curl);
3053
3054   // enable HTTP ERROR parsing
3055   ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
3056
3057   // enable uploading
3058   res = ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
3059   check_curl_result(res, "UPLOAD cannot set upload flag: ");
3060
3061   res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
3062   check_curl_result(res, "UPLOAD cannot set url: ");
3063
3064   res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
3065                            cmWriteToMemoryCallback);
3066   check_curl_result(res, "UPLOAD cannot set write function: ");
3067
3068   res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
3069                            cmFileCommandCurlDebugCallback);
3070   check_curl_result(res, "UPLOAD cannot set debug function: ");
3071
3072   cmFileCommandVectorOfChar chunkResponse;
3073   cmFileCommandVectorOfChar chunkDebug;
3074
3075   res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunkResponse);
3076   check_curl_result(res, "UPLOAD cannot set write data: ");
3077
3078   res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
3079   check_curl_result(res, "UPLOAD cannot set debug data: ");
3080
3081   res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
3082   check_curl_result(res, "UPLOAD cannot set follow-redirect option: ");
3083
3084   if(logVar.size())
3085     {
3086     res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
3087     check_curl_result(res, "UPLOAD cannot set verbose: ");
3088     }
3089
3090   if(timeout > 0)
3091     {
3092     res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout );
3093     check_curl_result(res, "UPLOAD cannot set timeout: ");
3094     }
3095
3096   if(inactivity_timeout > 0)
3097     {
3098     // Give up if there is no progress for a long time.
3099     ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
3100     ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
3101     }
3102
3103   // Need the progress helper's scope to last through the duration of
3104   // the curl_easy_perform call... so this object is declared at function
3105   // scope intentionally, rather than inside the "if(showProgress)"
3106   // block...
3107   //
3108   cURLProgressHelper helper(this, "upload");
3109
3110   if(showProgress)
3111     {
3112     res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
3113     check_curl_result(res, "UPLOAD cannot set noprogress value: ");
3114
3115     res = ::curl_easy_setopt(curl,
3116       CURLOPT_PROGRESSFUNCTION, cmFileUploadProgressCallback);
3117     check_curl_result(res, "UPLOAD cannot set progress function: ");
3118
3119     res = ::curl_easy_setopt(curl,
3120       CURLOPT_PROGRESSDATA, reinterpret_cast<void*>(&helper));
3121     check_curl_result(res, "UPLOAD cannot set progress data: ");
3122     }
3123
3124   // now specify which file to upload
3125   res = ::curl_easy_setopt(curl, CURLOPT_INFILE, fin);
3126   check_curl_result(res, "UPLOAD cannot set input file: ");
3127
3128   // and give the size of the upload (optional)
3129   res = ::curl_easy_setopt(curl,
3130     CURLOPT_INFILESIZE, static_cast<long>(st.st_size));
3131   check_curl_result(res, "UPLOAD cannot set input file size: ");
3132
3133   res = ::curl_easy_perform(curl);
3134
3135   /* always cleanup */
3136   g_curl.release();
3137   ::curl_easy_cleanup(curl);
3138
3139   if(statusVar.size())
3140     {
3141     cmOStringStream result;
3142     result << (int)res << ";\"" << ::curl_easy_strerror(res) << "\"";
3143     this->Makefile->AddDefinition(statusVar.c_str(),
3144                                   result.str().c_str());
3145     }
3146
3147   ::curl_global_cleanup();
3148
3149   fclose(fin);
3150   fin = NULL;
3151
3152   if(logVar.size())
3153     {
3154     std::string log;
3155
3156     if(chunkResponse.size())
3157       {
3158       chunkResponse.push_back(0);
3159       log += "Response:\n";
3160       log += &*chunkResponse.begin();
3161       log += "\n";
3162       }
3163
3164     if(chunkDebug.size())
3165       {
3166       chunkDebug.push_back(0);
3167       log += "Debug:\n";
3168       log += &*chunkDebug.begin();
3169       log += "\n";
3170       }
3171
3172     this->Makefile->AddDefinition(logVar.c_str(), log.c_str());
3173     }
3174
3175   return true;
3176 #else
3177   this->SetError("UPLOAD not supported by bootstrap cmake.");
3178   return false;
3179 #endif
3180 }