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 <wtf/text/CString.h>
36#include <wtf/text/WTFString.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    if (!from.length())
100        return;
101    // Compute the new length.
102    size_t newLen = 0;
103    const char* p = from.data();
104    while (p < from.data() + from.length()) {
105        char c = *p++;
106        if (c == '\r') {
107            // Safe to look ahead because of trailing '\0'.
108            if (*p != '\n') {
109                // Turn CR into CRLF.
110                newLen += 2;
111            }
112        } else if (c == '\n') {
113            // Turn LF into CRLF.
114            newLen += 2;
115        } else {
116            // Leave other characters alone.
117            newLen += 1;
118        }
119    }
120    if (newLen < from.length())
121        return;
122
123    if (newLen == from.length()) {
124        buffer.copy(from);
125        return;
126    }
127
128    p = from.data();
129    char* q = buffer.allocate(newLen);
130
131    // Make a copy of the string.
132    while (p < from.data() + from.length()) {
133        char c = *p++;
134        if (c == '\r') {
135            // Safe to look ahead because of trailing '\0'.
136            if (*p != '\n') {
137                // Turn CR into CRLF.
138                *q++ = '\r';
139                *q++ = '\n';
140            }
141        } else if (c == '\n') {
142            // Turn LF into CRLF.
143            *q++ = '\r';
144            *q++ = '\n';
145        } else {
146            // Leave other characters alone.
147            *q++ = c;
148        }
149    }
150}
151
152};
153
154namespace WebCore {
155
156void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR);
157
158// Normalize all line-endings to CR or LF.
159void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR)
160{
161    // Compute the new length.
162    size_t newLen = 0;
163    bool needFix = false;
164    const char* p = from.data();
165    char fromEndingChar = toCR ? '\n' : '\r';
166    char toEndingChar = toCR ? '\r' : '\n';
167    while (p < from.data() + from.length()) {
168        char c = *p++;
169        if (c == '\r' && *p == '\n') {
170            // Turn CRLF into CR or LF.
171            p++;
172            needFix = true;
173        } else if (c == fromEndingChar) {
174            // Turn CR/LF into LF/CR.
175            needFix = true;
176        }
177        newLen += 1;
178    }
179
180    // Grow the result buffer.
181    p = from.data();
182    size_t oldResultSize = result.size();
183    result.grow(oldResultSize + newLen);
184    char* q = result.data() + oldResultSize;
185
186    // If no need to fix the string, just copy the string over.
187    if (!needFix) {
188        memcpy(q, p, from.length());
189        return;
190    }
191
192    // Make a copy of the string.
193    while (p < from.data() + from.length()) {
194        char c = *p++;
195        if (c == '\r' && *p == '\n') {
196            // Turn CRLF or CR into CR or LF.
197            p++;
198            *q++ = toEndingChar;
199        } else if (c == fromEndingChar) {
200            // Turn CR/LF into LF/CR.
201            *q++ = toEndingChar;
202        } else {
203            // Leave other characters alone.
204            *q++ = c;
205        }
206    }
207}
208
209CString normalizeLineEndingsToCRLF(const CString& from)
210{
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(WINDOWS)
230    VectorCharAppendBuffer buffer(result);
231    internalNormalizeLineEndingsToCRLF(from, buffer);
232#else
233    normalizeLineEndingsToLF(from, result);
234#endif
235}
236
237} // namespace WebCore
238