[NTDLL]
[reactos.git] / base / applications / notepad / text.c
1 /*
2 * Notepad (text.c)
3 *
4 * Copyright 1998,99 Marcel Baur <mbaur@g26.ethz.ch>
5 * Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6 * Copyright 2002 Andriy Palamarchuk
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <notepad.h>
24
25 static BOOL Append(LPWSTR *ppszText, DWORD *pdwTextLen, LPCWSTR pszAppendText, DWORD dwAppendLen)
26 {
27 LPWSTR pszNewText;
28
29 if (dwAppendLen > 0)
30 {
31 if (*ppszText)
32 {
33 pszNewText = (LPWSTR) HeapReAlloc(GetProcessHeap(), 0, *ppszText, (*pdwTextLen + dwAppendLen) * sizeof(WCHAR));
34 }
35 else
36 {
37 pszNewText = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwAppendLen * sizeof(WCHAR));
38 }
39
40 if (!pszNewText)
41 return FALSE;
42
43 memcpy(pszNewText + *pdwTextLen, pszAppendText, dwAppendLen * sizeof(WCHAR));
44 *ppszText = pszNewText;
45 *pdwTextLen += dwAppendLen;
46 }
47 return TRUE;
48 }
49
50 BOOL ReadText(HANDLE hFile, LPWSTR *ppszText, DWORD *pdwTextLen, int *piEncoding, int *piEoln)
51 {
52 DWORD dwSize;
53 LPBYTE pBytes = NULL;
54 LPCWSTR pszText;
55 LPWSTR pszAllocText = NULL;
56 DWORD dwPos, i;
57 DWORD dwCharCount;
58 BOOL bSuccess = FALSE;
59 BYTE b = 0;
60 int iEncoding = ENCODING_ANSI;
61 int iCodePage = 0;
62 WCHAR szCrlf[2] = { '\r', '\n' };
63 DWORD adwEolnCount[3] = { 0, 0, 0 };
64
65 *ppszText = NULL;
66 *pdwTextLen = 0;
67
68 dwSize = GetFileSize(hFile, NULL);
69 if (dwSize == INVALID_FILE_SIZE)
70 goto done;
71
72 pBytes = HeapAlloc(GetProcessHeap(), 0, dwSize + 2);
73 if (!pBytes)
74 goto done;
75
76 if (!ReadFile(hFile, pBytes, dwSize, &dwSize, NULL))
77 goto done;
78 dwPos = 0;
79
80 /* Make sure that there is a NUL character at the end, in any encoding */
81 pBytes[dwSize + 0] = '\0';
82 pBytes[dwSize + 1] = '\0';
83
84 /* Look for Byte Order Marks */
85 if ((dwSize >= 2) && (pBytes[0] == 0xFF) && (pBytes[1] == 0xFE))
86 {
87 iEncoding = ENCODING_UNICODE;
88 dwPos += 2;
89 }
90 else if ((dwSize >= 2) && (pBytes[0] == 0xFE) && (pBytes[1] == 0xFF))
91 {
92 iEncoding = ENCODING_UNICODE_BE;
93 dwPos += 2;
94 }
95 else if ((dwSize >= 3) && (pBytes[0] == 0xEF) && (pBytes[1] == 0xBB) && (pBytes[2] == 0xBF))
96 {
97 iEncoding = ENCODING_UTF8;
98 dwPos += 3;
99 }
100
101 switch(iEncoding)
102 {
103 case ENCODING_UNICODE_BE:
104 for (i = dwPos; i < dwSize-1; i += 2)
105 {
106 b = pBytes[i+0];
107 pBytes[i+0] = pBytes[i+1];
108 pBytes[i+1] = b;
109 }
110 /* fall through */
111
112 case ENCODING_UNICODE:
113 pszText = (LPCWSTR) &pBytes[dwPos];
114 dwCharCount = (dwSize - dwPos) / sizeof(WCHAR);
115 break;
116
117 case ENCODING_ANSI:
118 case ENCODING_UTF8:
119 if (iEncoding == ENCODING_ANSI)
120 iCodePage = CP_ACP;
121 else if (iEncoding == ENCODING_UTF8)
122 iCodePage = CP_UTF8;
123
124 if ((dwSize - dwPos) > 0)
125 {
126 dwCharCount = MultiByteToWideChar(iCodePage, 0, (LPCSTR)&pBytes[dwPos], dwSize - dwPos, NULL, 0);
127 if (dwCharCount == 0)
128 goto done;
129 }
130 else
131 {
132 /* special case for files with no characters (other than BOMs) */
133 dwCharCount = 0;
134 }
135
136 pszAllocText = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (dwCharCount + 1) * sizeof(WCHAR));
137 if (!pszAllocText)
138 goto done;
139
140 if ((dwSize - dwPos) > 0)
141 {
142 if (!MultiByteToWideChar(iCodePage, 0, (LPCSTR)&pBytes[dwPos], dwSize - dwPos, pszAllocText, dwCharCount))
143 goto done;
144 }
145
146 pszAllocText[dwCharCount] = '\0';
147 pszText = pszAllocText;
148 break;
149 }
150
151 dwPos = 0;
152 for (i = 0; i < dwCharCount; i++)
153 {
154 switch(pszText[i])
155 {
156 case '\r':
157 if ((i < dwCharCount-1) && (pszText[i+1] == '\n'))
158 {
159 i++;
160 adwEolnCount[EOLN_CRLF]++;
161 break;
162 }
163 /* fall through */
164
165 case '\n':
166 if (!Append(ppszText, pdwTextLen, &pszText[dwPos], i - dwPos))
167 return FALSE;
168 if (!Append(ppszText, pdwTextLen, szCrlf, sizeof(szCrlf) / sizeof(szCrlf[0])))
169 return FALSE;
170 dwPos = i + 1;
171
172 if (pszText[i] == '\r')
173 adwEolnCount[EOLN_CR]++;
174 else
175 adwEolnCount[EOLN_LF]++;
176 break;
177 }
178 }
179
180 if (!*ppszText && (pszText == pszAllocText))
181 {
182 /* special case; don't need to reallocate */
183 *ppszText = pszAllocText;
184 *pdwTextLen = dwCharCount;
185 pszAllocText = NULL;
186 }
187 else
188 {
189 /* append last remaining text */
190 if (!Append(ppszText, pdwTextLen, &pszText[dwPos], i - dwPos + 1))
191 return FALSE;
192 }
193
194 /* chose which eoln to use */
195 *piEoln = EOLN_CRLF;
196 if (adwEolnCount[EOLN_LF] > adwEolnCount[*piEoln])
197 *piEoln = EOLN_LF;
198 if (adwEolnCount[EOLN_CR] > adwEolnCount[*piEoln])
199 *piEoln = EOLN_CR;
200 *piEncoding = iEncoding;
201
202 bSuccess = TRUE;
203
204 done:
205 if (pBytes)
206 HeapFree(GetProcessHeap(), 0, pBytes);
207 if (pszAllocText)
208 HeapFree(GetProcessHeap(), 0, pszAllocText);
209
210 if (!bSuccess && *ppszText)
211 {
212 HeapFree(GetProcessHeap(), 0, *ppszText);
213 *ppszText = NULL;
214 *pdwTextLen = 0;
215 }
216 return bSuccess;
217 }
218
219 static BOOL WriteEncodedText(HANDLE hFile, LPCWSTR pszText, DWORD dwTextLen, int iEncoding)
220 {
221 LPBYTE pBytes = NULL;
222 LPBYTE pAllocBuffer = NULL;
223 DWORD dwPos = 0;
224 DWORD dwByteCount;
225 BYTE buffer[1024];
226 UINT iCodePage = 0;
227 DWORD dwDummy, i;
228 BOOL bSuccess = FALSE;
229 int iBufferSize, iRequiredBytes;
230 BYTE b;
231
232 while(dwPos < dwTextLen)
233 {
234 switch(iEncoding)
235 {
236 case ENCODING_UNICODE:
237 pBytes = (LPBYTE) &pszText[dwPos];
238 dwByteCount = (dwTextLen - dwPos) * sizeof(WCHAR);
239 dwPos = dwTextLen;
240 break;
241
242 case ENCODING_UNICODE_BE:
243 dwByteCount = (dwTextLen - dwPos) * sizeof(WCHAR);
244 if (dwByteCount > sizeof(buffer))
245 dwByteCount = sizeof(buffer);
246
247 memcpy(buffer, &pszText[dwPos], dwByteCount);
248 for (i = 0; i < dwByteCount; i += 2)
249 {
250 b = buffer[i+0];
251 buffer[i+0] = buffer[i+1];
252 buffer[i+1] = b;
253 }
254 pBytes = (LPBYTE) &buffer[dwPos];
255 dwPos += dwByteCount / sizeof(WCHAR);
256 break;
257
258 case ENCODING_ANSI:
259 case ENCODING_UTF8:
260 if (iEncoding == ENCODING_ANSI)
261 iCodePage = CP_ACP;
262 else if (iEncoding == ENCODING_UTF8)
263 iCodePage = CP_UTF8;
264
265 iRequiredBytes = WideCharToMultiByte(iCodePage, 0, &pszText[dwPos], dwTextLen - dwPos, NULL, 0, NULL, NULL);
266 if (iRequiredBytes <= 0)
267 {
268 goto done;
269 }
270 else if (iRequiredBytes < sizeof(buffer))
271 {
272 pBytes = buffer;
273 iBufferSize = sizeof(buffer);
274 }
275 else
276 {
277 pAllocBuffer = (LPBYTE) HeapAlloc(GetProcessHeap(), 0, iRequiredBytes);
278 if (!pAllocBuffer)
279 return FALSE;
280 pBytes = pAllocBuffer;
281 iBufferSize = iRequiredBytes;
282 }
283
284 dwByteCount = WideCharToMultiByte(iCodePage, 0, &pszText[dwPos], dwTextLen - dwPos, (LPSTR) pBytes, iBufferSize, NULL, NULL);
285 if (!dwByteCount)
286 goto done;
287
288 dwPos = dwTextLen;
289 break;
290
291 default:
292 goto done;
293 }
294
295 if (!WriteFile(hFile, pBytes, dwByteCount, &dwDummy, NULL))
296 goto done;
297
298 /* free the buffer, if we have allocated one */
299 if (pAllocBuffer)
300 {
301 HeapFree(GetProcessHeap(), 0, pAllocBuffer);
302 pAllocBuffer = NULL;
303 }
304 }
305 bSuccess = TRUE;
306
307 done:
308 if (pAllocBuffer)
309 HeapFree(GetProcessHeap(), 0, pAllocBuffer);
310 return bSuccess;
311 }
312
313 BOOL WriteText(HANDLE hFile, LPCWSTR pszText, DWORD dwTextLen, int iEncoding, int iEoln)
314 {
315 WCHAR wcBom;
316 LPCWSTR pszLF = L"\n";
317 DWORD dwPos, dwNext;
318
319 /* Write the proper byte order marks if not ANSI */
320 if (iEncoding != ENCODING_ANSI)
321 {
322 wcBom = 0xFEFF;
323 if (!WriteEncodedText(hFile, &wcBom, 1, iEncoding))
324 return FALSE;
325 }
326
327 dwPos = 0;
328
329 /* pszText eoln are always \r\n */
330
331 do
332 {
333 /* Find the next eoln */
334 dwNext = dwPos;
335 while(dwNext < dwTextLen)
336 {
337 if (pszText[dwNext] == '\r' && pszText[dwNext + 1] == '\n')
338 break;
339 dwNext++;
340 }
341
342 if (dwNext != dwTextLen)
343 {
344 switch (iEoln)
345 {
346 case EOLN_LF:
347 /* Write text (without eoln) */
348 if (!WriteEncodedText(hFile, &pszText[dwPos], dwNext - dwPos, iEncoding))
349 return FALSE;
350 /* Write eoln */
351 if (!WriteEncodedText(hFile, pszLF, 1, iEncoding))
352 return FALSE;
353 break;
354 case EOLN_CR:
355 /* Write text (including \r as eoln) */
356 if (!WriteEncodedText(hFile, &pszText[dwPos], dwNext - dwPos + 1, iEncoding))
357 return FALSE;
358 break;
359 case EOLN_CRLF:
360 /* Write text (including \r\n as eoln) */
361 if (!WriteEncodedText(hFile, &pszText[dwPos], dwNext - dwPos + 2, iEncoding))
362 return FALSE;
363 break;
364 default:
365 return FALSE;
366 }
367 }
368 else
369 {
370 /* Write text (without eoln, since this is the end of the file) */
371 if (!WriteEncodedText(hFile, &pszText[dwPos], dwNext - dwPos, iEncoding))
372 return FALSE;
373 }
374
375 /* Skip \r\n */
376 dwPos = dwNext + 2;
377 }
378 while (dwPos < dwTextLen);
379
380 return TRUE;
381 }
382