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 "platform/text/LineEnding.h"
34
35#include "wtf/text/CString.h"
36#include "wtf/text/WTFString.h"
37
38namespace {
39
40class OutputBuffer {
41public:
42    virtual char* allocate(size_t) = 0;
43    virtual void copy(const CString&) = 0;
44    virtual ~OutputBuffer() { }
45};
46
47class CStringBuffer FINAL : public OutputBuffer {
48public:
49    CStringBuffer(CString& buffer)
50        : m_buffer(buffer)
51    {
52    }
53    virtual ~CStringBuffer() { }
54
55    virtual char* allocate(size_t size) OVERRIDE
56    {
57        char* ptr;
58        m_buffer = CString::newUninitialized(size, ptr);
59        return ptr;
60    }
61
62    virtual void copy(const CString& source) OVERRIDE
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 FINAL : 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) OVERRIDE
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) OVERRIDE
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 (p < from.data() + from.length()) {
103        char c = *p++;
104        if (c == '\r') {
105            // Safe to look ahead because of trailing '\0'.
106            if (*p != '\n') {
107                // Turn CR into CRLF.
108                newLen += 2;
109            }
110        } else if (c == '\n') {
111            // Turn LF into CRLF.
112            newLen += 2;
113        } else {
114            // Leave other characters alone.
115            newLen += 1;
116        }
117    }
118    if (newLen < from.length())
119        return;
120
121    if (newLen == from.length()) {
122        buffer.copy(from);
123        return;
124    }
125
126    p = from.data();
127    char* q = buffer.allocate(newLen);
128
129    // Make a copy of the string.
130    while (p < from.data() + from.length()) {
131        char c = *p++;
132        if (c == '\r') {
133            // Safe to look ahead because of trailing '\0'.
134            if (*p != '\n') {
135                // Turn CR into CRLF.
136                *q++ = '\r';
137                *q++ = '\n';
138            }
139        } else if (c == '\n') {
140            // Turn LF into CRLF.
141            *q++ = '\r';
142            *q++ = '\n';
143        } else {
144            // Leave other characters alone.
145            *q++ = c;
146        }
147    }
148}
149
150};
151
152namespace blink {
153
154void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR);
155
156// Normalize all line-endings to CR or LF.
157void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR)
158{
159    // Compute the new length.
160    size_t newLen = 0;
161    bool needFix = false;
162    const char* p = from.data();
163    char fromEndingChar = toCR ? '\n' : '\r';
164    char toEndingChar = toCR ? '\r' : '\n';
165    while (p < from.data() + from.length()) {
166        char c = *p++;
167        if (c == '\r' && *p == '\n') {
168            // Turn CRLF into CR or LF.
169            p++;
170            needFix = true;
171        } else if (c == fromEndingChar) {
172            // Turn CR/LF into LF/CR.
173            needFix = true;
174        }
175        newLen += 1;
176    }
177
178    // Grow the result buffer.
179    p = from.data();
180    size_t oldResultSize = result.size();
181    result.grow(oldResultSize + newLen);
182    char* q = result.data() + oldResultSize;
183
184    // If no need to fix the string, just copy the string over.
185    if (!needFix) {
186        memcpy(q, p, from.length());
187        return;
188    }
189
190    // Make a copy of the string.
191    while (p < from.data() + from.length()) {
192        char c = *p++;
193        if (c == '\r' && *p == '\n') {
194            // Turn CRLF or CR into CR or LF.
195            p++;
196            *q++ = toEndingChar;
197        } else if (c == fromEndingChar) {
198            // Turn CR/LF into LF/CR.
199            *q++ = toEndingChar;
200        } else {
201            // Leave other characters alone.
202            *q++ = c;
203        }
204    }
205}
206
207CString normalizeLineEndingsToCRLF(const CString& from)
208{
209    if (!from.length())
210        return from;
211    CString result;
212    CStringBuffer buffer(result);
213    internalNormalizeLineEndingsToCRLF(from, buffer);
214    return buffer.buffer();
215}
216
217void normalizeLineEndingsToCR(const CString& from, Vector<char>& result)
218{
219    normalizeToCROrLF(from, result, true);
220}
221
222void normalizeLineEndingsToLF(const CString& from, Vector<char>& result)
223{
224    normalizeToCROrLF(from, result, false);
225}
226
227void normalizeLineEndingsToNative(const CString& from, Vector<char>& result)
228{
229#if OS(WIN)
230    VectorCharAppendBuffer buffer(result);
231    internalNormalizeLineEndingsToCRLF(from, buffer);
232#else
233    normalizeLineEndingsToLF(from, result);
234#endif
235}
236
237} // namespace blink
238