From ff3e8823840d33ebfd5704a7478b5ba90bbaf013 Mon Sep 17 00:00:00 2001
From: Peter Kuemmel <syntheticpp@gmx.net>
Date: Sat, 26 Mar 2011 16:03:45 +0100
Subject: [PATCH] Add NEWLINE_STYLE option to configure_file

---
 Modules/BundleUtilities.cmake         |   17 +++-
 Source/CMakeLists.txt                 |    2 +
 Source/cmConfigureFileCommand.cxx     |    4 +-
 Source/cmConfigureFileCommand.h       |   12 ++-
 Source/cmFileCommand.cxx              |    1 +
 Source/cmMakefile.cxx                 |    7 +-
 Source/cmMakefile.h                   |    4 +-
 Source/cmNewLine.cxx                  |  203 +++++++++++++++++++++++++++++++++
 Source/cmNewLine.h                    |   46 ++++++++
 Tests/NewLineStyleTest/CMakeLists.txt |   69 +++++++++++
 10 files changed, 356 insertions(+), 9 deletions(-)
 create mode 100644 Source/cmNewLine.cxx
 create mode 100644 Source/cmNewLine.h
 create mode 100644 Tests/NewLineStyleTest/CMakeLists.txt

diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake
index 44f2c20..6811fb6 100644
--- a/Modules/BundleUtilities.cmake
+++ b/Modules/BundleUtilities.cmake
@@ -23,14 +23,25 @@
 # PARENT_SCOPE. Also depends on GetPrerequisites.cmake.
 #
 #  FIXUP_BUNDLE(<app> <libs> <dirs>)
+#    <app>  one executables or library
+#    <libs> executables and libraries which <app> not directly links to
+#    <dirs> additional search pathes for the dependencies of <app> and <libs>
+#
 # Fix up a bundle in-place and make it standalone, such that it can be
 # drag-n-drop copied to another machine and run on that machine as long as all
 # of the system libraries are compatible.
 #
+# The whole concept of fixup_bundle is based on the assumption that there is a 
+# direcrory(bundle) containing some set of executable files (*.exes and *.dll/dylib/so)
+# to begin with. All other libraries these executable files depends on will be copied
+# by fixup_bundle into the bundle. fixup_bundle could only detect dependencies which
+# are hardcoded into the executables and which could be found by GetPrerequisites.
+# Therefore you always have to pass plugins as <libs> parameter to fixup_bundle,
+# because by definition applications do know nothing about the existance of a
+# concrete plugin.
+#
 # If you pass plugins to fixup_bundle as the libs parameter, you should install
-# them or copy them into the bundle before calling fixup_bundle. The "libs"
-# parameter is a list of libraries that must be fixed up, but that cannot be
-# determined by otool output analysis. (i.e., plugins)
+# them or copy them into the bundle before calling fixup_bundle.
 #
 # Gather all the keys for all the executables and libraries in a bundle, and
 # then, for each key, copy each prerequisite into the bundle. Then fix each one
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 278d4df..c3d6c69 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -214,6 +214,8 @@ SET(SRCS
   cmMakefileExecutableTargetGenerator.cxx
   cmMakefileLibraryTargetGenerator.cxx
   cmMakefileUtilityTargetGenerator.cxx
+  cmNewLine.h
+  cmNewLine.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.h
   cmPolicies.h
diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx
index c1327fb..c71c77a 100644
--- a/Source/cmConfigureFileCommand.cxx
+++ b/Source/cmConfigureFileCommand.cxx
@@ -65,6 +65,7 @@ bool cmConfigureFileCommand
     cmSystemTools::SetFatalErrorOccured();
     return false;
     }
+  this->NewLineStyle = cmNewLine().parse(args);
   this->CopyOnly = false;
   this->EscapeQuotes = false;
 
@@ -122,7 +123,8 @@ int cmConfigureFileCommand::ConfigureFile()
     this->OutputFile.c_str(),
     this->CopyOnly,
     this->AtOnly,
-    this->EscapeQuotes);
+    this->EscapeQuotes,
+    this->NewLineStyle);
 }
 
 
diff --git a/Source/cmConfigureFileCommand.h b/Source/cmConfigureFileCommand.h
index 844a23c..7119647 100644
--- a/Source/cmConfigureFileCommand.h
+++ b/Source/cmConfigureFileCommand.h
@@ -14,6 +14,7 @@
 
 #include "cmCommand.h"
 
+
 class cmConfigureFileCommand : public cmCommand
 {
 public:
@@ -56,7 +57,8 @@ public:
     {
       return
         "  configure_file(<input> <output>\n"
-        "                 [COPYONLY] [ESCAPE_QUOTES] [@ONLY])\n"
+        "                 [COPYONLY] [ESCAPE_QUOTES] [@ONLY] \n"
+        "                 [NEWLINE_STYLE [UNIX|APPLE|DOS|WIN32|LF|CR|CRLF|PRESERVE]])\n"
         "Copies a file <input> to file <output> and substitutes variable "
         "values referenced in the file content.  "
         "If <input> is a relative path it is evaluated with respect to "
@@ -81,14 +83,20 @@ public:
         "either #define VAR or /* #undef VAR */ depending on "
         "the setting of VAR in CMake. Any occurrences of #cmakedefine01 VAR "
         "will be replaced with either #define VAR 1 or #define VAR 0 "
-        "depending on whether VAR evaluates to TRUE or FALSE in CMake";
+        "depending on whether VAR evaluates to TRUE or FALSE in CMake.\n"
+        "With NEWLINE_STYLE the line endning coud be adjusted: \n"
+        "    'UNIX' or 'LF' for \\n, 'DOS', 'WIN32' or 'CRLF' for \\r\\n,\n"
+        "     and 'APPLE' or 'CR' for \\r \n";
     }
 
   virtual void FinalPass();
   virtual bool HasFinalPass() const { return !this->Immediate; }
+
 private:
   int ConfigureFile();
   
+  cmNewLine::Style NewLineStyle;
+
   std::string InputFile;
   std::string OutputFile;
   bool CopyOnly;
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index d28de08..e06960e 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -3081,3 +3081,4 @@ cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
   return false;
 #endif
 }
+
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 63bf03b..690ddb7 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3199,7 +3199,8 @@ void cmMakefile::ConfigureString(const std::string& input,
 }
 
 int cmMakefile::ConfigureFile(const char* infile, const char* outfile,
-                              bool copyonly, bool atOnly, bool escapeQuotes)
+                              bool copyonly, bool atOnly, bool escapeQuotes,
+                              cmNewLine::Style eol)
 {
   int res = 1;
   if ( !this->CanIWriteThisFile(outfile) )
@@ -3225,7 +3226,7 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile,
     std::string path = soutfile.substr(0, pos);
     cmSystemTools::MakeDirectory(path.c_str());
     }
-
+  
   if(copyonly)
     {
     if ( !cmSystemTools::CopyFileIfDifferent(sinfile.c_str(),
@@ -3279,6 +3280,8 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile,
       }
     cmSystemTools::RemoveFile(tempOutputFile.c_str());
     }
+
+  cmNewLine().setLineEndingOnFile(eol, soutfile);
   return res;
 }
 
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 1c1aef3..70c9cc3 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -19,6 +19,7 @@
 #include "cmPropertyMap.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmNewLine.h"
 #include "cmake.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
@@ -703,7 +704,8 @@ public:
    * Copy file but change lines acording to ConfigureString
    */
   int ConfigureFile(const char* infile, const char* outfile, 
-                    bool copyonly, bool atOnly, bool escapeQuotes);
+                    bool copyonly, bool atOnly, bool escapeQuotes,
+                    cmNewLine::Style eol =cmNewLine::Preserve);
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
   /**
diff --git a/Source/cmNewLine.cxx b/Source/cmNewLine.cxx
new file mode 100644
index 0000000..169443b
--- /dev/null
+++ b/Source/cmNewLine.cxx
@@ -0,0 +1,203 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2011 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#include "cmNewLine.h"
+
+#include <iostream>
+#include <fstream>
+
+cmNewLine::Style cmNewLine::parse(std::vector<std::string> const& args)
+{
+    for (size_t i = 0; i< args.size(); i++)
+    {
+        if (args[i] == "NEWLINE_STYLE")
+        {
+            if (args.size() > i)
+            {
+                const std::string eol= args[i + 1];
+                if (eol== "LF" || eol== "UNIX")
+                {
+                    return LF;
+                } 
+                else if (eol== "CR" || eol== "APPLE")
+                {
+                    return CR;
+                }
+                else if (eol== "CRLF" || eol== "WIN32" || eol== "DOS")
+                {
+                    return CRLF;
+                }
+                else if (eol== "PRESERVE")
+                {
+                    return Preserve;
+                }
+            }
+            else
+            {
+                return Unknown;
+            }
+        }
+    }
+    return Default;
+}
+
+
+std::vector<char> cmNewLine::characters(Style eol)
+{
+    std::vector<char> chars;
+    switch (eol)
+    {
+    case LF:
+        chars.push_back('\n');
+        break;
+    case CR:
+        chars.push_back('\r');
+        break;
+    case CRLF:
+        chars.push_back('\r');
+        chars.push_back('\n');
+        break;
+    default:
+        ;
+    };
+    chars.push_back('\0');
+    return chars;
+}
+
+
+std::vector<const std::vector<char> > cmNewLine::splitIntoLines(const std::vector<char>& str)
+{
+    std::vector<const std::vector<char> > lines;
+    int lstart = 0;
+    int lend = -1;
+    int lnext = -1;
+    int length = str.size();
+    for (int i = 0; i < length; i++)
+    {
+        if (str[i] == '\n')
+        {
+            lend = i;
+            lnext = i + 1;
+        }
+        else if (str[i] == '\r')
+        {
+            lend = i;
+            if (length > i + 1 && str[i + 1] == '\n')
+            {
+                lnext = i + 2;
+            }
+            else
+            {
+                lnext = i + 1;
+            }
+        }
+
+        if (lend != -1)
+        {
+            std::vector<char> line;
+            if (lend > lstart) {
+                line.reserve(lend - lstart + 1);
+                for (int j = lstart; j < lend; j++) {
+                    line.push_back(str[j]);
+                }
+            }
+            line.push_back('\0');
+            lines.push_back(line);
+            lstart = lnext;
+            i = lnext - 1;
+            lend = -1;
+        }
+    }
+    return lines;
+}
+
+
+void cmNewLine::append(std::vector<char>& vec, const std::vector<char>& rhs)
+{
+    size_t to = rhs.size() - 1; // ignore '\0'
+    for(size_t i = 0; i < to; i++) {
+        vec.push_back(rhs[i]);
+    }
+}
+
+
+std::vector<char> cmNewLine::setStyle(Style eol, std::vector<char> const& str)
+{
+    if (eol== Preserve || eol == Unknown) {
+        return str;
+    }
+    
+    std::vector<char> styled;
+    std::vector<const std::vector<char> > lines = splitIntoLines(str);
+    const std::vector<char> endchars = characters(eol);
+    for(size_t i = 0; i < lines.size(); i++)
+    {
+        append(styled, lines[i]);
+        append(styled, endchars);
+    }
+    return styled;
+}
+
+
+
+bool cmNewLine::setLineEndingOnFile(Style eol, std::string const& filename)
+{
+    if (eol == Unknown) {
+        return false;
+    }
+    if (eol == Preserve || eol == Default) {
+        return true;
+    }
+    
+    std::ifstream file (filename, std::ios::binary);
+    if (!file.is_open()) {
+        return false;
+    }
+
+    file.seekg (0, std::ios::end);
+    int size = (int) file.tellg();
+    if (size <= 0) {
+        return true;
+    }
+
+    char* buffer = new char[size];
+    file.seekg (0, std::ios::beg);
+    file.read (buffer, size);
+    file.close();
+
+    std::vector<char> data;
+    data.reserve(size);
+    for (int i = 0; i < size; i++) {
+        data.push_back(buffer[i]);
+    }
+    delete [] buffer;
+
+    const std::vector<char> styled = setStyle(eol, data);
+
+    std::ofstream styledfile (filename, std::ios::binary);
+    if (!styledfile.is_open()) {
+        return false;
+    }
+
+    size = styled.size();
+    buffer = new char[size];
+    for (int i = 0; i < size; i++) {
+        buffer[i] = styled[i];
+    }
+
+    styledfile.write(buffer, size);
+    styledfile.close();
+
+    delete [] buffer;
+
+    return true;
+}
+
diff --git a/Source/cmNewLine.h b/Source/cmNewLine.h
new file mode 100644
index 0000000..e44db84
--- /dev/null
+++ b/Source/cmNewLine.h
@@ -0,0 +1,46 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
+
+  Distributed under the OSI-approved BSD License (the "License");
+  see accompanying file Copyright.txt for details.
+
+  This software is distributed WITHOUT ANY WARRANTY; without even the
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the License for more information.
+============================================================================*/
+#ifndef cmNewLineStyle_h
+#define cmNewLineStyle_h
+
+
+#include <string>
+#include <vector>
+
+class cmNewLine
+{
+public:
+
+    enum Style
+    {
+        LF,        // unix
+        CR,        // mac
+        CRLF,      // dos
+        Preserve,
+        Default,
+        Unknown    // returned on parse error
+    };
+
+    Style parse(const std::vector<std::string> & args);
+
+    std::vector<char> setStyle(Style sty, const std::vector<char>&);
+
+    bool setLineEndingOnFile(Style eol, const std::string& filename);
+
+private:
+    std::vector<char> characters(Style sty);
+    std::vector<const std::vector<char> > splitIntoLines(const std::vector<char>& str);
+
+    void append(std::vector<char>&, const std::vector<char>&);
+};
+
+#endif
diff --git a/Tests/NewLineStyleTest/CMakeLists.txt b/Tests/NewLineStyleTest/CMakeLists.txt
new file mode 100644
index 0000000..a73a67a
--- /dev/null
+++ b/Tests/NewLineStyleTest/CMakeLists.txt
@@ -0,0 +1,69 @@
+cmake_minimum_required (VERSION 2.6)
+PROJECT(NewLineStyleTest)
+
+
+
+
+macro(create _input _test_name _eol)
+	set(_name ${CMAKE_BINARY_DIR}/${_test_name}_${_eol}.txt)
+	file(REMOVE ${_name})
+	message(STATUS ${_name})
+	CONFIGURE_FILE(${_input} ${_name} COPYONLY NEWLINE_STYLE ${_eol})
+endmacro()
+
+
+
+macro(create_all _input _test_name)
+	create(${_input} ${_test_name} UNIX)
+	create(${_input} ${_test_name} APPLE)
+	create(${_input} ${_test_name} DOS)
+	create(${_input} ${_test_name} LF)
+	create(${_input} ${_test_name} CR)
+	create(${_input} ${_test_name} CRLF)
+	create(${_input} ${_test_name} WIN32)
+endmacro()
+
+
+
+set(test_name eol_1_test)
+set(input ${CMAKE_BINARY_DIR}/${test_name}.txt)
+file(WRITE ${input} "1\n1\n1")
+create_all(${input} ${test_name})
+
+
+
+set(test_name eol_2_only)
+set(input ${CMAKE_BINARY_DIR}/${test_name}.txt)
+file(WRITE ${input} "\n\n\n\n\n\n\n")
+create_all(${input} ${test_name})
+
+
+
+set(test_name eol_3_mixed)
+set(input ${CMAKE_BINARY_DIR}/${test_name}.txt)
+file(WRITE ${input} "123\n123\n123\r\n123\n123\r\n123\r123\r123\n123\r\n\n")
+create_all(${input} ${test_name})
+
+
+
+set(test_name eol_4_only_mixed)
+set(input ${CMAKE_BINARY_DIR}/${test_name}.txt)
+file(WRITE ${input} "\n\n\n\n\n \r\r\r\r\r \r\n\r\n\r\n\r\n \r\n\r\n\n\r\r\r\n\r ")
+create_all(${input} ${test_name})
+
+
+
+set(test_name eol_5_from_UNIX)
+set(input ${CMAKE_BINARY_DIR}/eol_1_test_UNIX.txt)
+create_all(${input} ${test_name})
+
+
+
+set(test_name eol_6_from_APPLE)
+set(input ${CMAKE_BINARY_DIR}/eol_1_test_APPLE.txt)
+create_all(${input} ${test_name})
+
+
+set(test_name eol_7_from_DOS)
+set(input ${CMAKE_BINARY_DIR}/eol_1_test_DOS.txt)
+create_all(${input} ${test_name})
-- 
1.7.3.1.msysgit.0

