Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsLinebreakConverter.h"
8 :
9 : #include "nsMemory.h"
10 : #include "nsCRT.h"
11 :
12 :
13 : /*----------------------------------------------------------------------------
14 : GetLinebreakString
15 :
16 : Could make this inline
17 : ----------------------------------------------------------------------------*/
18 : static const char*
19 0 : GetLinebreakString(nsLinebreakConverter::ELinebreakType aBreakType)
20 : {
21 : static const char* const sLinebreaks[] = {
22 : "", // any
23 : NS_LINEBREAK, // platform
24 : LFSTR, // content
25 : CRLF, // net
26 : CRSTR, // Mac
27 : LFSTR, // Unix
28 : CRLF, // Windows
29 : " ", // space
30 : nullptr
31 : };
32 :
33 0 : return sLinebreaks[aBreakType];
34 : }
35 :
36 :
37 : /*----------------------------------------------------------------------------
38 : AppendLinebreak
39 :
40 : Wee inline method to append a line break. Modifies ioDest.
41 : ----------------------------------------------------------------------------*/
42 : template<class T>
43 : void
44 0 : AppendLinebreak(T*& aIoDest, const char* aLineBreakStr)
45 : {
46 0 : *aIoDest++ = *aLineBreakStr;
47 :
48 0 : if (aLineBreakStr[1]) {
49 0 : *aIoDest++ = aLineBreakStr[1];
50 : }
51 0 : }
52 :
53 : /*----------------------------------------------------------------------------
54 : CountChars
55 :
56 : Counts occurrences of breakStr in aSrc
57 : ----------------------------------------------------------------------------*/
58 : template<class T>
59 : int32_t
60 0 : CountLinebreaks(const T* aSrc, int32_t aInLen, const char* aBreakStr)
61 : {
62 0 : const T* src = aSrc;
63 0 : const T* srcEnd = aSrc + aInLen;
64 0 : int32_t theCount = 0;
65 :
66 0 : while (src < srcEnd) {
67 0 : if (*src == *aBreakStr) {
68 0 : src++;
69 :
70 0 : if (aBreakStr[1]) {
71 0 : if (src < srcEnd && *src == aBreakStr[1]) {
72 0 : src++;
73 0 : theCount++;
74 : }
75 : } else {
76 0 : theCount++;
77 : }
78 : } else {
79 0 : src++;
80 : }
81 : }
82 :
83 0 : return theCount;
84 : }
85 :
86 :
87 : /*----------------------------------------------------------------------------
88 : ConvertBreaks
89 :
90 : ioLen *includes* a terminating null, if any
91 : ----------------------------------------------------------------------------*/
92 : template<class T>
93 : static T*
94 0 : ConvertBreaks(const T* aInSrc, int32_t& aIoLen, const char* aSrcBreak,
95 : const char* aDestBreak)
96 : {
97 0 : NS_ASSERTION(aInSrc && aSrcBreak && aDestBreak, "Got a null string");
98 :
99 0 : T* resultString = nullptr;
100 :
101 : // handle the no conversion case
102 0 : if (nsCRT::strcmp(aSrcBreak, aDestBreak) == 0) {
103 0 : resultString = (T*)malloc(sizeof(T) * aIoLen);
104 0 : if (!resultString) {
105 0 : return nullptr;
106 : }
107 0 : memcpy(resultString, aInSrc, sizeof(T) * aIoLen); // includes the null, if any
108 0 : return resultString;
109 : }
110 :
111 0 : int32_t srcBreakLen = strlen(aSrcBreak);
112 0 : int32_t destBreakLen = strlen(aDestBreak);
113 :
114 : // handle the easy case, where the string length does not change, and the
115 : // breaks are only 1 char long, i.e. CR <-> LF
116 0 : if (srcBreakLen == destBreakLen && srcBreakLen == 1) {
117 0 : resultString = (T*)malloc(sizeof(T) * aIoLen);
118 0 : if (!resultString) {
119 0 : return nullptr;
120 : }
121 :
122 0 : const T* src = aInSrc;
123 0 : const T* srcEnd = aInSrc + aIoLen; // includes null, if any
124 0 : T* dst = resultString;
125 :
126 0 : char srcBreakChar = *aSrcBreak; // we know it's one char long already
127 0 : char dstBreakChar = *aDestBreak;
128 :
129 0 : while (src < srcEnd) {
130 0 : if (*src == srcBreakChar) {
131 0 : *dst++ = dstBreakChar;
132 0 : src++;
133 : } else {
134 0 : *dst++ = *src++;
135 : }
136 0 : }
137 :
138 : // aIoLen does not change
139 : } else {
140 : // src and dest termination is different length. Do it a slower way.
141 :
142 : // count linebreaks in src. Assumes that chars in 2-char linebreaks are unique.
143 0 : int32_t numLinebreaks = CountLinebreaks(aInSrc, aIoLen, aSrcBreak);
144 :
145 : int32_t newBufLen =
146 0 : aIoLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
147 0 : resultString = (T*)malloc(sizeof(T) * newBufLen);
148 0 : if (!resultString) {
149 0 : return nullptr;
150 : }
151 :
152 0 : const T* src = aInSrc;
153 0 : const T* srcEnd = aInSrc + aIoLen; // includes null, if any
154 0 : T* dst = resultString;
155 :
156 0 : while (src < srcEnd) {
157 0 : if (*src == *aSrcBreak) {
158 0 : *dst++ = *aDestBreak;
159 0 : if (aDestBreak[1]) {
160 0 : *dst++ = aDestBreak[1];
161 : }
162 :
163 0 : src++;
164 0 : if (src < srcEnd && aSrcBreak[1] && *src == aSrcBreak[1]) {
165 0 : src++;
166 : }
167 : } else {
168 0 : *dst++ = *src++;
169 : }
170 : }
171 :
172 0 : aIoLen = newBufLen;
173 : }
174 :
175 0 : return resultString;
176 : }
177 :
178 :
179 : /*----------------------------------------------------------------------------
180 : ConvertBreaksInSitu
181 :
182 : Convert breaks in situ. Can only do this if the linebreak length
183 : does not change.
184 : ----------------------------------------------------------------------------*/
185 : template<class T>
186 : static void
187 0 : ConvertBreaksInSitu(T* aInSrc, int32_t aInLen, char aSrcBreak, char aDestBreak)
188 : {
189 0 : T* src = aInSrc;
190 0 : T* srcEnd = aInSrc + aInLen;
191 :
192 0 : while (src < srcEnd) {
193 0 : if (*src == aSrcBreak) {
194 0 : *src = aDestBreak;
195 : }
196 :
197 0 : src++;
198 : }
199 0 : }
200 :
201 :
202 : /*----------------------------------------------------------------------------
203 : ConvertUnknownBreaks
204 :
205 : Convert unknown line breaks to the specified break.
206 :
207 : This will convert CRLF pairs to one break, and single CR or LF to a break.
208 : ----------------------------------------------------------------------------*/
209 : template<class T>
210 : static T*
211 0 : ConvertUnknownBreaks(const T* aInSrc, int32_t& aIoLen, const char* aDestBreak)
212 : {
213 0 : const T* src = aInSrc;
214 0 : const T* srcEnd = aInSrc + aIoLen; // includes null, if any
215 :
216 0 : int32_t destBreakLen = strlen(aDestBreak);
217 0 : int32_t finalLen = 0;
218 :
219 0 : while (src < srcEnd) {
220 0 : if (*src == nsCRT::CR) {
221 0 : if (src < srcEnd && src[1] == nsCRT::LF) {
222 : // CRLF
223 0 : finalLen += destBreakLen;
224 0 : src++;
225 : } else {
226 : // Lone CR
227 0 : finalLen += destBreakLen;
228 : }
229 0 : } else if (*src == nsCRT::LF) {
230 : // Lone LF
231 0 : finalLen += destBreakLen;
232 : } else {
233 0 : finalLen++;
234 : }
235 0 : src++;
236 : }
237 :
238 0 : T* resultString = (T*)malloc(sizeof(T) * finalLen);
239 0 : if (!resultString) {
240 0 : return nullptr;
241 : }
242 :
243 0 : src = aInSrc;
244 0 : srcEnd = aInSrc + aIoLen; // includes null, if any
245 :
246 0 : T* dst = resultString;
247 :
248 0 : while (src < srcEnd) {
249 0 : if (*src == nsCRT::CR) {
250 0 : if (src < srcEnd && src[1] == nsCRT::LF) {
251 : // CRLF
252 0 : AppendLinebreak(dst, aDestBreak);
253 0 : src++;
254 : } else {
255 : // Lone CR
256 0 : AppendLinebreak(dst, aDestBreak);
257 : }
258 0 : } else if (*src == nsCRT::LF) {
259 : // Lone LF
260 0 : AppendLinebreak(dst, aDestBreak);
261 : } else {
262 0 : *dst++ = *src;
263 : }
264 0 : src++;
265 : }
266 :
267 0 : aIoLen = finalLen;
268 0 : return resultString;
269 : }
270 :
271 :
272 : /*----------------------------------------------------------------------------
273 : ConvertLineBreaks
274 :
275 : ----------------------------------------------------------------------------*/
276 : char*
277 0 : nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
278 : ELinebreakType aSrcBreaks,
279 : ELinebreakType aDestBreaks,
280 : int32_t aSrcLen, int32_t* aOutLen)
281 : {
282 0 : NS_ASSERTION(aDestBreaks != eLinebreakAny &&
283 : aSrcBreaks != eLinebreakSpace, "Invalid parameter");
284 0 : if (!aSrc) {
285 0 : return nullptr;
286 : }
287 :
288 0 : int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(aSrc) + 1 : aSrcLen;
289 :
290 : char* resultString;
291 0 : if (aSrcBreaks == eLinebreakAny) {
292 0 : resultString = ConvertUnknownBreaks(aSrc, sourceLen,
293 0 : GetLinebreakString(aDestBreaks));
294 : } else
295 0 : resultString = ConvertBreaks(aSrc, sourceLen,
296 : GetLinebreakString(aSrcBreaks),
297 0 : GetLinebreakString(aDestBreaks));
298 :
299 0 : if (aOutLen) {
300 0 : *aOutLen = sourceLen;
301 : }
302 0 : return resultString;
303 : }
304 :
305 :
306 : /*----------------------------------------------------------------------------
307 : ConvertLineBreaksInSitu
308 :
309 : ----------------------------------------------------------------------------*/
310 : nsresult
311 0 : nsLinebreakConverter::ConvertLineBreaksInSitu(char** aIoBuffer,
312 : ELinebreakType aSrcBreaks,
313 : ELinebreakType aDestBreaks,
314 : int32_t aSrcLen, int32_t* aOutLen)
315 : {
316 0 : NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
317 0 : if (!aIoBuffer || !*aIoBuffer) {
318 0 : return NS_ERROR_NULL_POINTER;
319 : }
320 :
321 0 : NS_ASSERTION(aDestBreaks != eLinebreakAny &&
322 : aSrcBreaks != eLinebreakSpace, "Invalid parameter");
323 :
324 0 : int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(*aIoBuffer) + 1 : aSrcLen;
325 :
326 : // can we convert in-place?
327 0 : const char* srcBreaks = GetLinebreakString(aSrcBreaks);
328 0 : const char* dstBreaks = GetLinebreakString(aDestBreaks);
329 :
330 0 : if (aSrcBreaks != eLinebreakAny &&
331 0 : strlen(srcBreaks) == 1 &&
332 0 : strlen(dstBreaks) == 1) {
333 0 : ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
334 0 : if (aOutLen) {
335 0 : *aOutLen = sourceLen;
336 : }
337 : } else {
338 : char* destBuffer;
339 :
340 0 : if (aSrcBreaks == eLinebreakAny) {
341 0 : destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
342 : } else {
343 0 : destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
344 : }
345 :
346 0 : if (!destBuffer) {
347 0 : return NS_ERROR_OUT_OF_MEMORY;
348 : }
349 0 : *aIoBuffer = destBuffer;
350 0 : if (aOutLen) {
351 0 : *aOutLen = sourceLen;
352 : }
353 : }
354 :
355 0 : return NS_OK;
356 : }
357 :
358 :
359 : /*----------------------------------------------------------------------------
360 : ConvertUnicharLineBreaks
361 :
362 : ----------------------------------------------------------------------------*/
363 : char16_t*
364 0 : nsLinebreakConverter::ConvertUnicharLineBreaks(const char16_t* aSrc,
365 : ELinebreakType aSrcBreaks,
366 : ELinebreakType aDestBreaks,
367 : int32_t aSrcLen,
368 : int32_t* aOutLen)
369 : {
370 0 : NS_ASSERTION(aDestBreaks != eLinebreakAny &&
371 : aSrcBreaks != eLinebreakSpace, "Invalid parameter");
372 0 : if (!aSrc) {
373 0 : return nullptr;
374 : }
375 :
376 0 : int32_t bufLen = (aSrcLen == kIgnoreLen) ? NS_strlen(aSrc) + 1 : aSrcLen;
377 :
378 : char16_t* resultString;
379 0 : if (aSrcBreaks == eLinebreakAny) {
380 0 : resultString = ConvertUnknownBreaks(aSrc, bufLen,
381 0 : GetLinebreakString(aDestBreaks));
382 : } else
383 0 : resultString = ConvertBreaks(aSrc, bufLen, GetLinebreakString(aSrcBreaks),
384 0 : GetLinebreakString(aDestBreaks));
385 :
386 0 : if (aOutLen) {
387 0 : *aOutLen = bufLen;
388 : }
389 0 : return resultString;
390 : }
391 :
392 :
393 : /*----------------------------------------------------------------------------
394 : ConvertStringLineBreaks
395 :
396 : ----------------------------------------------------------------------------*/
397 : nsresult
398 0 : nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(
399 : char16_t** aIoBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
400 : int32_t aSrcLen, int32_t* aOutLen)
401 : {
402 0 : NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
403 0 : if (!aIoBuffer || !*aIoBuffer) {
404 0 : return NS_ERROR_NULL_POINTER;
405 : }
406 0 : NS_ASSERTION(aDestBreaks != eLinebreakAny &&
407 : aSrcBreaks != eLinebreakSpace, "Invalid parameter");
408 :
409 : int32_t sourceLen =
410 0 : (aSrcLen == kIgnoreLen) ? NS_strlen(*aIoBuffer) + 1 : aSrcLen;
411 :
412 : // can we convert in-place?
413 0 : const char* srcBreaks = GetLinebreakString(aSrcBreaks);
414 0 : const char* dstBreaks = GetLinebreakString(aDestBreaks);
415 :
416 0 : if ((aSrcBreaks != eLinebreakAny) &&
417 0 : (strlen(srcBreaks) == 1) &&
418 0 : (strlen(dstBreaks) == 1)) {
419 0 : ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
420 0 : if (aOutLen) {
421 0 : *aOutLen = sourceLen;
422 : }
423 : } else {
424 : char16_t* destBuffer;
425 :
426 0 : if (aSrcBreaks == eLinebreakAny) {
427 0 : destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
428 : } else {
429 0 : destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
430 : }
431 :
432 0 : if (!destBuffer) {
433 0 : return NS_ERROR_OUT_OF_MEMORY;
434 : }
435 0 : *aIoBuffer = destBuffer;
436 0 : if (aOutLen) {
437 0 : *aOutLen = sourceLen;
438 : }
439 : }
440 :
441 0 : return NS_OK;
442 : }
443 :
444 : /*----------------------------------------------------------------------------
445 : ConvertStringLineBreaks
446 :
447 : ----------------------------------------------------------------------------*/
448 : nsresult
449 0 : nsLinebreakConverter::ConvertStringLineBreaks(nsString& aIoString,
450 : ELinebreakType aSrcBreaks,
451 : ELinebreakType aDestBreaks)
452 : {
453 :
454 0 : NS_ASSERTION(aDestBreaks != eLinebreakAny &&
455 : aSrcBreaks != eLinebreakSpace, "Invalid parameter");
456 :
457 : // nothing to do
458 0 : if (aIoString.IsEmpty()) {
459 0 : return NS_OK;
460 : }
461 :
462 : nsresult rv;
463 :
464 : // remember the old buffer in case
465 : // we blow it away later
466 : nsString::char_iterator stringBuf;
467 0 : if (!aIoString.BeginWriting(stringBuf, fallible)) {
468 0 : return NS_ERROR_OUT_OF_MEMORY;
469 : }
470 :
471 : int32_t newLen;
472 :
473 0 : rv = ConvertUnicharLineBreaksInSitu(&stringBuf,
474 : aSrcBreaks, aDestBreaks,
475 0 : aIoString.Length() + 1, &newLen);
476 0 : if (NS_FAILED(rv)) {
477 0 : return rv;
478 : }
479 :
480 0 : if (stringBuf != aIoString.get()) {
481 0 : aIoString.Adopt(stringBuf, newLen - 1);
482 : }
483 :
484 0 : return NS_OK;
485 : }
486 :
487 :
488 :
|