1/*
2 * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "LineEnding.h"
34
35#include "PlatformString.h"
36#include <wtf/text/CString.h>
37
38namespace {
39
40class OutputBuffer {
41public:
42    virtual char* allocate(size_t size) = 0;
43    virtual void copy(const CString&) = 0;
44    virtual ~OutputBuffer() { }
45};
46
47class CStringBuffer : public OutputBuffer {
48public:
49    CStringBuffer(CString& buffer)
50        : m_buffer(buffer)
51    {
52    }
53    virtual ~CStringBuffer() { }
54
55    virtual char* allocate(size_t size)
56    {
57        char* ptr;
58        m_buffer = CString::newUninitialized(size, ptr);
59        return ptr;
60    }
61
62    virtual void copy(const CString& source)
63    {
64        m_buffer = source;
65    }
66
67    const CString& buffer() const { return m_buffer; }
68
69private:
70    CString m_buffer;
71};
72
73class VectorCharAppendBuffer : public OutputBuffer {
74public:
75    VectorCharAppendBuffer(Vector<char>& buffer)
76        : m_buffer(buffer)
77    {
78    }
79    virtual ~VectorCharAppendBuffer() { }
80
81    virtual char* allocate(size_t size)
82    {
83        size_t oldSize = m_buffer.size();
84        m_buffer.grow(oldSize + size);
85        return m_buffer.data() + oldSize;
86    }
87
88    virtual void copy(const CString& source)
89    {
90        m_buffer.append(source.data(), source.length());
91    }
92
93private:
94    Vector<char>& m_buffer;
95};
96
97void internalNormalizeLineEndingsToCRLF(const CString& from, OutputBuffer& buffer)
98{
99    // Compute the new length.
100    size_t newLen = 0;
101    const char* p = from.data();
102    while (char c = *p++) {
103        if (c == '\r') {
104            // Safe to look ahead because of trailing '\0'.
105            if (*p != '\n') {
106                // Turn CR into CRLF.
107                newLen += 2;
108            }
109        } else if (c == '\n') {
110            // Turn LF into CRLF.
111            newLen += 2;
112        } else {
113            // Leave other characters alone.
114            newLen += 1;
115        }
116    }
117    if (newLen < from.length())
118        return;
119
120    if (newLen == from.length()) {
121        buffer.copy(from);
122        return;
123    }
124
125    p = from.data();
126    char* q = buffer.allocate(newLen);
127
128    // Make a copy of the string.
129    while (char c = *p++) {
130        if (c == '\r') {
131            // Safe to look ahead because of trailing '\0'.
132            if (*p != '\n') {
133                // Turn CR into CRLF.
134                *q++ = '\r';
135                *q++ = '\n';
136            }
137        } else if (c == '\n') {
138            // Turn LF into CRLF.
139            *q++ = '\r';
140            *q++ = '\n';
141        } else {
142            // Leave other characters alone.
143            *q++ = c;
144        }
145    }
146}
147
148};
149
150namespace WebCore {
151
152void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR);
153
154// Normalize all line-endings to CR or LF.
155void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR)
156{
157    // Compute the new length.
158    size_t newLen = 0;
159    bool needFix = false;
160    const char* p = from.data();
161    char fromEndingChar = toCR ? '\n' : '\r';
162    char toEndingChar = toCR ? '\r' : '\n';
163    while (char c = *p++) {
164        if (c == '\r' && *p == '\n') {
165            // Turn CRLF into CR or LF.
166            p++;
167            needFix = true;
168        } else if (c == fromEndingChar) {
169            // Turn CR/LF into LF/CR.
170            needFix = true;
171        }
172        newLen += 1;
173    }
174
175    // Grow the result buffer.
176    p = from.data();
177    size_t oldResultSize = result.size();
178    result.grow(oldResultSize + newLen);
179    char* q = result.data() + oldResultSize;
180
181    // If no need to fix the string, just copy the string over.
182    if (!needFix) {
183        memcpy(q, p, from.length());
184        return;
185    }
186
187    // Make a copy of the string.
188    while (char c = *p++) {
189        if (c == '\r' && *p == '\n') {
190            // Turn CRLF or CR into CR or LF.
191            p++;
192            *q++ = toEndingChar;
193        } else if (c == fromEndingChar) {
194            // Turn CR/LF into LF/CR.
195            *q++ = toEndingChar;
196        } else {
197            // Leave other characters alone.
198            *q++ = c;
199        }
200    }
201}
202
203CString normalizeLineEndingsToCRLF(const CString& from)
204{
205    CString result;
206    CStringBuffer buffer(result);
207    internalNormalizeLineEndingsToCRLF(from, buffer);
208    return buffer.buffer();
209}
210
211void normalizeLineEndingsToCR(const CString& from, Vector<char>& result)
212{
213    normalizeToCROrLF(from, result, true);
214}
215
216void normalizeLineEndingsToLF(const CString& from, Vector<char>& result)
217{
218    normalizeToCROrLF(from, result, false);
219}
220
221void normalizeLineEndingsToNative(const CString& from, Vector<char>& result)
222{
223#if OS(WINDOWS)
224    VectorCharAppendBuffer buffer(result);
225    internalNormalizeLineEndingsToCRLF(from, buffer);
226#else
227    normalizeLineEndingsToLF(from, result);
228#endif
229}
230
231} // namespace WebCore
232