43d1c6ad2f13f8cb373870940f447eba9dade205
[reactos.git] / reactos / subsys / system / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #define UNICODE
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <windows.h>
28 #include <commdlg.h>
29
30 #include "main.h"
31
32 static BOOL Append(LPWSTR *ppszText, DWORD *pdwTextLen, LPCWSTR pszAppendText, DWORD dwAppendLen)
33 {
34 LPWSTR pszNewText;
35
36 if (dwAppendLen > 0)
37 {
38 if (*ppszText)
39 {
40 pszNewText = (LPWSTR) HeapReAlloc(GetProcessHeap(), 0, *ppszText, (*pdwTextLen + dwAppendLen) * sizeof(WCHAR));
41 }
42 else
43 {
44 pszNewText = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwAppendLen * sizeof(WCHAR));
45 }
46
47 if (!pszNewText)
48 return FALSE;
49
50 memcpy(pszNewText + *pdwTextLen, pszAppendText, dwAppendLen * sizeof(WCHAR));
51 *ppszText = pszNewText;
52 *pdwTextLen += dwAppendLen;
53 }
54 return TRUE;
55 }
56
57 BOOL ReadText(HANDLE hFile, LPWSTR *ppszText, DWORD *pdwTextLen, int *piEncoding, int *piEoln)
58 {
59 DWORD dwSize;
60 LPBYTE pBytes = NULL;
61 LPCWSTR pszText;
62 LPWSTR pszAllocText = NULL;
63 DWORD dwPos, i;
64 DWORD dwCharCount;
65 BOOL bSuccess = FALSE;
66 BYTE b = 0;
67 int iEncoding = ENCODING_ANSI;
68 int iCodePage;
69 WCHAR szCrlf[2] = { '\r', '\n' };
70 DWORD adwEolnCount[3] = { 0, 0, 0 };
71
72 *ppszText = NULL;
73 *pdwTextLen = 0;
74
75 dwSize = GetFileSize(hFile, NULL);
76 if (dwSize == INVALID_FILE_SIZE)
77 goto done;
78
79 pBytes = HeapAlloc(GetProcessHeap(), 0, dwSize + 2);
80 if (!pBytes)
81 goto done;
82
83 if (!ReadFile(hFile, pBytes, dwSize, &dwSize, NULL))
84 goto done;
85 dwPos = 0;
86
87 /* Make sure that there is a NUL character at the end, in any encoding */
88 pBytes[dwSize + 0] = '\0';
89 pBytes[dwSize + 1] = '\0';
90
91 /* Look for Byte Order Marks */
92 if ((dwSize >= 2) && (pBytes[0] == 0xFF) && (pBytes[1] == 0xFE))
93 {
94 iEncoding = ENCODING_UNICODE;
95 dwPos += 2;
96 }
97 else if ((dwSize >= 2) && (pBytes[0] == 0xFE) && (pBytes[1] == 0xFF))
98 {
99 iEncoding = ENCODING_UNICODE_BE;
100 dwPos += 2;
101 }
102 else if ((dwSize >= 3) && (pBytes[0] == 0xEF) && (pBytes[1] == 0xBB) && (pBytes[2] == 0xBF))
103 {
104 iEncoding = ENCODING_UTF8;
105 dwPos += 3;
106 }
107
108 switch(iEncoding)
109 {
110 case ENCODING_UNICODE_BE:
111 for (i = dwPos; i < dwSize-1; i += 2)
112 {
113 b = pBytes[i+0];
114 pBytes[i+0] = pBytes[i+1];
115 pBytes[i+1] = b;
116 }
117 /* fall through */
118
119 case ENCODING_UNICODE:
120 pszText = (LPCWSTR) &pBytes[dwPos];
121 dwCharCount = (dwSize - dwPos) / sizeof(WCHAR);
122 break;
123
124 case ENCODING_ANSI:
125 case ENCODING_UTF8:
126 if (iEncoding == ENCODING_ANSI)
127 iCodePage = CP_ACP;
128 else if (iEncoding == ENCODING_UTF8)
129 iCodePage = CP_UTF8;
130 else
131 goto done;
132
133 if ((dwSize - dwPos) > 0)
134 {
135 dwCharCount = MultiByteToWideChar(iCodePage, 0, (LPCSTR)&pBytes[dwPos], dwSize - dwPos, NULL, 0);
136 if (dwCharCount == 0)
137 goto done;
138 }
139 else
140 {
141 /* special case for files with no characters (other than BOMs) */
142 dwCharCount = 0;
143 }
144
145 pszAllocText = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, (dwCharCount + 1) * sizeof(WCHAR));
146 if (!pszAllocText)
147 goto done;
148
149 if ((dwSize - dwPos) > 0)
150 {
151 if (!MultiByteToWideChar(iCodePage, 0, (LPCSTR)&pBytes[dwPos], dwSize - dwPos, pszAllocText, dwCharCount))
152 goto done;
153 }
154
155 pszAllocText[dwCharCount] = '\0';
156 pszText = pszAllocText;
157 break;
158 }
159
160 dwPos = 0;
161 for (i = 0; i < dwCharCount; i++)
162 {
163 switch(pszText[i])
164 {
165 case '\r':
166 if ((i < dwCharCount-1) && (pszText[i+1] == '\n'))
167 {
168 i++;
169 adwEolnCount[EOLN_CRLF]++;
170 break;
171 }
172 /* fall through */
173
174 case '\n':
175 if (!Append(ppszText, pdwTextLen, &pszText[dwPos], i - dwPos))
176 return FALSE;
177 if (!Append(ppszText, pdwTextLen, szCrlf, sizeof(szCrlf) / sizeof(szCrlf[0])))
178 return FALSE;
179 dwPos = i + 1;
180
181 if (pszText[i] == '\r')
182 adwEolnCount[EOLN_CR]++;
183 else
184 adwEolnCount[EOLN_LF]++;
185 break;
186 }
187 }
188
189 if (!*ppszText && (pszText == pszAllocText))
190 {
191 /* special case; don't need to reallocate */
192 *ppszText = pszAllocText;
193 *pdwTextLen = dwCharCount;
194 pszAllocText = NULL;
195 }
196 else
197 {
198 /* append last remaining text */
199 if (!Append(ppszText, pdwTextLen, &pszText[dwPos], i - dwPos + 1))
200 return FALSE;
201 }
202
203 /* chose which eoln to use */
204 *piEoln = EOLN_CRLF;
205 if (adwEolnCount[EOLN_LF] > adwEolnCount[*piEoln])
206 *piEoln = EOLN_LF;
207 if (adwEolnCount[EOLN_CR] > adwEolnCount[*piEoln])
208 *piEoln = EOLN_CR;
209 *piEncoding = iEncoding;
210
211 bSuccess = TRUE;
212
213 done:
214 if (pBytes)
215 HeapFree(GetProcessHeap(), 0, pBytes);
216 if (pszAllocText)
217 HeapFree(GetProcessHeap(), 0, pszAllocText);
218
219 if (!bSuccess && *ppszText)
220 {
221 HeapFree(GetProcessHeap(), 0, *ppszText);
222 *ppszText = NULL;
223 *pdwTextLen = 0;
224 }
225 return bSuccess;
226 }
227
228 static BOOL WriteEncodedText(HANDLE hFile, LPCWSTR pszText, DWORD dwTextLen, int iEncoding)
229 {
230 LPBYTE pBytes = NULL;
231 LPBYTE pAllocBuffer = NULL;
232 DWORD dwPos = 0;
233 DWORD dwByteCount;
234 BYTE buffer[1024];
235 UINT iCodePage;
236 DWORD dwDummy, i;
237 BOOL bSuccess = FALSE;
238 int iBufferSize, iRequiredBytes;
239 BYTE b;
240
241 while(dwPos < dwTextLen)
242 {
243 switch(iEncoding)
244 {
245 case ENCODING_UNICODE:
246 pBytes = (LPBYTE) &pszText[dwPos];
247 dwByteCount = (dwTextLen - dwPos) * sizeof(WCHAR);
248 dwPos = dwTextLen;
249 break;
250
251 case ENCODING_UNICODE_BE:
252 dwByteCount = (dwTextLen - dwPos) * sizeof(WCHAR);
253 if (dwByteCount > sizeof(buffer))
254 dwByteCount = sizeof(buffer);
255
256 memcpy(buffer, &pszText[dwPos], dwByteCount);
257 for (i = 0; i < dwByteCount; i += 2)
258 {
259 b = buffer[i+0];
260 buffer[i+0] = buffer[i+1];
261 buffer[i+1] = b;
262 }
263 dwPos += dwByteCount / sizeof(WCHAR);
264 break;
265
266 case ENCODING_ANSI:
267 case ENCODING_UTF8:
268 if (iEncoding == ENCODING_ANSI)
269 iCodePage = CP_ACP;
270 else if (iEncoding == ENCODING_UTF8)
271 iCodePage = CP_UTF8;
272 else
273 goto done;
274
275 iRequiredBytes = WideCharToMultiByte(iCodePage, 0, &pszText[dwPos], dwTextLen - dwPos, NULL, 0, NULL, NULL);
276 if (iRequiredBytes <= 0)
277 {
278 goto done;
279 }
280 else if (iRequiredBytes < sizeof(buffer))
281 {
282 pBytes = buffer;
283 iBufferSize = sizeof(buffer);
284 }
285 else
286 {
287 pAllocBuffer = (LPBYTE) HeapAlloc(GetProcessHeap(), 0, iRequiredBytes);
288 if (!pAllocBuffer)
289 return FALSE;
290 pBytes = pAllocBuffer;
291 iBufferSize = iRequiredBytes;
292 }
293
294 dwByteCount = WideCharToMultiByte(iCodePage, 0, &pszText[dwPos], dwTextLen - dwPos, (LPSTR) pBytes, iBufferSize, NULL, NULL);
295 if (!dwByteCount)
296 goto done;
297
298 dwPos = dwTextLen;
299 break;
300
301 default:
302 goto done;
303 }
304
305 if (!WriteFile(hFile, pBytes, dwByteCount, &dwDummy, NULL))
306 goto done;
307
308 /* free the buffer, if we have allocated one */
309 if (pAllocBuffer)
310 {
311 HeapFree(GetProcessHeap(), 0, pAllocBuffer);
312 pAllocBuffer = NULL;
313 }
314 }
315 bSuccess = TRUE;
316
317 done:
318 if (pAllocBuffer)
319 HeapFree(GetProcessHeap(), 0, pAllocBuffer);
320 return bSuccess;
321 }
322
323 BOOL WriteText(HANDLE hFile, LPCWSTR pszText, DWORD dwTextLen, int iEncoding, int iEoln)
324 {
325 WCHAR wcBom;
326 WCHAR wcEoln;
327 BYTE bEoln;
328 LPBYTE pbEoln = NULL;
329 DWORD dwDummy, dwPos, dwNext, dwEolnSize = 0;
330
331 /* Write the proper byte order marks if not ANSI */
332 if (iEncoding != ENCODING_ANSI)
333 {
334 wcBom = 0xFEFF;
335 if (!WriteEncodedText(hFile, &wcBom, 1, iEncoding))
336 return FALSE;
337 }
338
339 /* Identify the proper eoln to use */
340 switch(iEoln)
341 {
342 case EOLN_LF:
343 bEoln = '\n';
344 pbEoln = &bEoln;
345 dwEolnSize = sizeof(bEoln);
346 break;
347 case EOLN_CR:
348 bEoln = '\r';
349 pbEoln = &bEoln;
350 dwEolnSize = sizeof(bEoln);
351 break;
352 }
353
354 /* If we have an eoln, make sure it is of the proper encoding */
355 if (pbEoln && ((iEncoding == ENCODING_UNICODE) || (iEncoding == ENCODING_UNICODE_BE)))
356 {
357 wcEoln = bEoln;
358 pbEoln = (LPBYTE) &wcEoln;
359 dwEolnSize = sizeof(wcEoln);
360 }
361
362 dwPos = 0;
363
364 while(dwPos < dwTextLen)
365 {
366 if (pbEoln)
367 {
368 /* Find the next eoln */
369 dwNext = dwPos;
370 while(dwNext < dwTextLen-1)
371 {
372 if ((pszText[dwNext] == '\r') && (pszText[dwNext+1] == '\n'))
373 break;
374 dwNext++;
375 }
376 }
377 else
378 {
379 /* No eoln conversion is necessary */
380 dwNext = dwTextLen;
381 }
382
383 if (!WriteEncodedText(hFile, &pszText[dwPos], dwNext - dwPos, iEncoding))
384 return FALSE;
385 dwPos = dwNext;
386
387 /* are we at an eoln? */
388 while ((dwPos < dwTextLen-1) &&
389 ((pszText[dwPos] == '\r') && (pszText[dwPos+1] == '\n')))
390 {
391 if (!WriteFile(hFile, pbEoln, dwEolnSize, &dwDummy, NULL))
392 return FALSE;
393 dwPos += 2;
394 }
395 }
396 while(dwPos < dwTextLen);
397
398 return TRUE;
399 }
400