CEDIT: minor fixes
[kolibrios.git] / programs / develop / cedit / SRC / RW.ob07
blob4da1281e75b8430bb44b845d56cd7d9179ead798
1 (*\r
2     Copyright 2021 Anton Krotov\r
3 \r
4     This file is part of CEdit.\r
5 \r
6     CEdit is free software: you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation, either version 3 of the License, or\r
9     (at your option) any later version.\r
11     CEdit is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
16     You should have received a copy of the GNU General Public License\r
17     along with CEdit. If not, see <http://www.gnu.org/licenses/>.\r
18 *)\r
20 MODULE RW;\r
22 IMPORT\r
23     File, SYSTEM, KOSAPI, E := Encodings,\r
24     CB := Clipboard, Lines;\r
27 CONST\r
29     CR = 0DX; LF = 0AX; TAB = 9X; SPACE = 20X;\r
30     BOM = 0FEFFX;\r
32     BUF_SIZE = 65536;\r
34     NAME_LEN = 1024;\r
36     EOL_CRLF* = 0; EOL_LF* = 1; EOL_CR* = 2;\r
39 TYPE\r
41     tFileName* = ARRAY NAME_LEN OF CHAR;\r
43     tEOL = ARRAY 3 OF WCHAR;\r
45     tInput* = POINTER TO RECORD\r
46         buffer:   INTEGER;\r
47         pos, cnt: INTEGER;\r
48         CR:       BOOLEAN;\r
49         clipbrd:  BOOLEAN;\r
50         getChar:  PROCEDURE (file: tInput): INTEGER\r
51     END;\r
53     tOutput* = POINTER TO RECORD\r
54         handle:   File.FS;\r
55         buffer:   ARRAY BUF_SIZE OF BYTE;\r
56         pos:      INTEGER;\r
57         eol:      tEOL;\r
58         putChar:  PROCEDURE (file: tOutput; code: INTEGER): BOOLEAN\r
59     END;\r
62 VAR\r
64     eol*: ARRAY 3 OF tEOL;\r
65     eolNames*: ARRAY 3, 16 OF WCHAR;\r
66     strBuf: ARRAY 1000000 OF WCHAR;\r
69 PROCEDURE getByte (file: tInput): BYTE;\r
70 VAR\r
71     res: BYTE;\r
72 BEGIN\r
73     IF file.cnt > 0 THEN\r
74         SYSTEM.GET8(file.buffer + file.pos, res);\r
75         INC(file.pos);\r
76         DEC(file.cnt)\r
77     ELSE\r
78         res := 0\r
79     END\r
80     RETURN res\r
81 END getByte;\r
84 PROCEDURE peakByte (file: tInput): BYTE;\r
85 VAR\r
86     res: BYTE;\r
87 BEGIN\r
88     IF file.cnt > 0 THEN\r
89         SYSTEM.GET8(file.buffer + file.pos, res)\r
90     ELSE\r
91         res := 0\r
92     END\r
93     RETURN res\r
94 END peakByte;\r
97 PROCEDURE getCharUTF8 (file: tInput): INTEGER;\r
98 VAR\r
99     code, n: INTEGER;\r
100     b: BYTE;\r
101 BEGIN\r
102     b := getByte(file);\r
103     IF b <= 07FH THEN\r
104         n := 0\r
105     ELSIF (0C0H <= b) & (b <= 0DFH) THEN\r
106         DEC(b, 0C0H);\r
107         n := 1\r
108     ELSIF (0E0H <= b) & (b <= 0EFH) THEN\r
109         DEC(b, 0E0H);\r
110         n := 2\r
111     ELSIF (0F0H <= b) & (b <= 0F7H) THEN\r
112         DEC(b, 0F0H);\r
113         n := 3\r
114     ELSIF (0F8H <= b) & (b <= 0FBH) THEN\r
115         DEC(b, 0F8H);\r
116         n := 4\r
117     ELSIF (0FCH <= b) & (b <= 0FDH) THEN\r
118         DEC(b, 0FCH);\r
119         n := 5\r
120     ELSIF b = 0FEH THEN\r
121         b := 0;\r
122         n := 6\r
123     ELSIF b = 0FFH THEN\r
124         n := -1\r
125     ELSIF (080H <= b) & (b <= 0BFH) THEN\r
126         n := -1\r
127     END;\r
129     code := b;\r
131     IF n > 2 THEN\r
132         n := -1\r
133     END;\r
135     WHILE n > 0 DO\r
136         DEC(n);\r
137         b := peakByte(file);\r
138         IF (080H <= b) & (b <= 0BFH) THEN\r
139             code := code*64 + getByte(file) - 080H\r
140         ELSE\r
141             n := -1\r
142         END\r
143     END;\r
145     IF n = -1 THEN\r
146         code := E.UNDEF\r
147     END\r
149     RETURN code\r
150 END getCharUTF8;\r
153 PROCEDURE getCharW1251 (file: tInput): INTEGER;\r
154     RETURN E.cpW1251[getByte(file)]\r
155 END getCharW1251;\r
158 PROCEDURE getCharCP866 (file: tInput): INTEGER;\r
159     RETURN E.cp866[getByte(file)]\r
160 END getCharCP866;\r
163 PROCEDURE getCharUTF16LE (file: tInput): INTEGER;\r
164     RETURN getByte(file) + getByte(file) * 256\r
165 END getCharUTF16LE;\r
168 PROCEDURE getString* (file: tInput; line: Lines.tLine; tabs: BOOLEAN; VAR eol: BOOLEAN): INTEGER;\r
169 VAR\r
170     c: WCHAR;\r
171     i, L, k, n: INTEGER;\r
172 BEGIN\r
173     L := LEN(strBuf);\r
174     eol := FALSE;\r
175     n := 0;\r
176     i := ORD(file.cnt > 0) - 1;\r
177     WHILE (file.cnt > 0) & ~eol DO\r
178         c := WCHR(file.getChar(file) MOD 65536);\r
179         IF c = Lines.TAB1 THEN\r
180                 c := SPACE\r
181         END;\r
182         IF c = CR THEN\r
183             eol := TRUE;\r
184             file.CR := TRUE\r
185         ELSIF (c = LF) OR (c = 0X) THEN\r
186             IF ~file.CR THEN\r
187                 eol := TRUE\r
188             END;\r
189             file.CR := FALSE\r
190         ELSIF c = TAB THEN\r
191             k := Lines.tab - i MOD Lines.tab;\r
192             IF tabs THEN\r
193                 strBuf[i] := TAB\r
194             ELSE\r
195                 strBuf[i] := SPACE\r
196             END;\r
197             INC(i);\r
198             DEC(k);\r
199             WHILE k > 0 DO\r
200                 IF tabs THEN\r
201                     strBuf[i] := Lines.TAB1\r
202                 ELSE\r
203                     strBuf[i] := SPACE\r
204                 END;\r
205                 INC(i);\r
206                 IF i = L THEN\r
207                     Lines.concat(line, strBuf);\r
208                     INC(n, i);\r
209                     i := 0\r
210                 END;\r
211                 DEC(k)\r
212             END;\r
213             file.CR := FALSE\r
214         ELSIF c = BOM THEN\r
215             file.CR := FALSE\r
216         ELSE\r
217             strBuf[i] := c;\r
218             INC(i);\r
219             IF i = L THEN\r
220                 Lines.concat(line, strBuf);\r
221                 INC(n, i);\r
222                 i := 0\r
223             END;\r
224             file.CR := FALSE\r
225         END\r
226     END;\r
227     IF i >= 0 THEN\r
228         strBuf[i] := 0X;\r
229         Lines.concat(line, strBuf);\r
230     END;\r
231     INC(n, i)\r
232     RETURN n\r
233 END getString;\r
236 PROCEDURE detectEncoding (text: tInput): INTEGER;\r
237 VAR\r
238     pos, cnt, res: INTEGER;\r
239     continue, bom: BOOLEAN;\r
240     b: BYTE;\r
241     cp866, w1251: INTEGER;\r
242 BEGIN\r
243     pos := text.pos;\r
244     cnt := text.cnt;\r
245     continue := TRUE;\r
246     WHILE (text.cnt > 0) & continue DO\r
247         IF getByte(text) > 127 THEN\r
248             continue := FALSE\r
249         END\r
250     END;\r
251     text.cnt := cnt;\r
252     text.pos := pos;\r
253     IF continue THEN\r
254         res := E.CP866\r
255     ELSE\r
256         bom := getCharUTF8(text) = ORD(BOM);\r
257         continue := TRUE;\r
258         text.cnt := cnt;\r
259         text.pos := pos;\r
260         WHILE (text.cnt > 0) & continue DO\r
261             IF getCharUTF8(text) = E.UNDEF THEN\r
262                 continue := FALSE\r
263             END\r
264         END;\r
265         IF continue THEN\r
266             IF bom THEN\r
267                 res := E.UTF8BOM\r
268             ELSE\r
269                 res := E.UTF8\r
270             END\r
271         ELSE\r
272             text.cnt := cnt;\r
273             text.pos := pos;\r
274             cp866 := 0;\r
275             w1251 := 0;\r
276             WHILE text.cnt > 0 DO\r
277                 b := getByte(text);\r
278                 IF b > 127 THEN\r
279                     IF b >= 192 THEN\r
280                         INC(w1251)\r
281                     ELSE\r
282                         INC(cp866)\r
283                     END\r
284                 END\r
285             END;\r
286             IF w1251 > cp866 THEN\r
287                 res := E.W1251\r
288             ELSE\r
289                 res := E.CP866\r
290             END\r
291         END;\r
292         text.cnt := cnt;\r
293         text.pos := pos\r
294     END\r
295     RETURN res\r
296 END detectEncoding;\r
299 PROCEDURE detectEOL (text: tInput): INTEGER;\r
300 VAR\r
301         pos, cnt, c, res: INTEGER;\r
302 BEGIN\r
303         res := -1;\r
304         pos := text.pos;\r
305         cnt := text.cnt;\r
306         WHILE (text.cnt > 0) & (res = -1) DO\r
307                 c := text.getChar(text);\r
308                 IF c = 10 THEN\r
309                         res := EOL_LF\r
310                 ELSIF c = 13 THEN\r
311                         IF text.getChar(text) = 10 THEN\r
312                                 res := EOL_CRLF\r
313                         ELSE\r
314                                 res := EOL_CR\r
315                         END\r
316                 END\r
317         END;\r
318         text.cnt := cnt;\r
319         text.pos := pos;\r
320         IF res = -1 THEN\r
321                 res := EOL_CRLF\r
322         END\r
323         RETURN res\r
324 END detectEOL;\r
327 PROCEDURE load* (name: tFileName; VAR enc, eol: INTEGER): tInput;\r
328 VAR\r
329     res: tInput;\r
330     fsize: INTEGER;\r
331 BEGIN\r
332     NEW(res);\r
333     res.pos := 0;\r
334     res.CR := FALSE;\r
335     res.getChar := NIL;\r
336     res.clipbrd := FALSE;\r
337     fsize := File.FileSize(name);\r
338     IF fsize = 0 THEN\r
339         res.buffer := KOSAPI.malloc(4096);\r
340         ASSERT(res.buffer # 0);\r
341         res.cnt := 0\r
342     ELSE\r
343         res.buffer := File.Load(name, res.cnt)\r
344     END;\r
345     IF res.buffer = 0 THEN\r
346         DISPOSE(res)\r
347     ELSE\r
348         enc := detectEncoding(res);\r
349         IF (enc = E.UTF8BOM) OR (enc = E.UTF8) THEN\r
350             res.getChar := getCharUTF8\r
351         ELSIF enc = E.CP866 THEN\r
352             res.getChar := getCharCP866\r
353         ELSIF enc = E.W1251 THEN\r
354             res.getChar := getCharW1251\r
355         END;\r
356         eol := detectEOL(res)\r
357     END\r
358     RETURN res\r
359 END load;\r
362 PROCEDURE clipboard* (): tInput;\r
363 VAR\r
364     res: tInput;\r
365 BEGIN\r
366     NEW(res);\r
367     res.pos := 0;\r
368     res.CR := FALSE;\r
369     res.clipbrd := TRUE;\r
370     res.getChar := NIL;\r
371     res.getChar := getCharCP866;\r
372     res.buffer := CB.get(res.cnt);\r
373     IF res.buffer = 0 THEN\r
374         DISPOSE(res)\r
375     END\r
376     RETURN res\r
377 END clipboard;\r
380 PROCEDURE putByte (file: tOutput; b: BYTE);\r
381 VAR\r
382     c: INTEGER;\r
383 BEGIN\r
384     IF file.pos = BUF_SIZE THEN\r
385         c := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), BUF_SIZE);\r
386         file.pos := 0\r
387     END;\r
388     file.buffer[file.pos] := b;\r
389     INC(file.pos)\r
390 END putByte;\r
393 PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER): INTEGER;\r
394 VAR\r
395     i: INTEGER;\r
396     c: WCHAR;\r
397     err: BOOLEAN;\r
398 BEGIN\r
399     i := 0;\r
400     err := FALSE;\r
401     WHILE (i < n) & ~err DO\r
402         c := Lines.getChar(line, i);\r
403         IF c # Lines.TAB1 THEN\r
404             IF ~file.putChar(file, ORD(c)) THEN\r
405                 err := TRUE;\r
406                 DEC(i)\r
407             END\r
408         END;\r
409         INC(i)\r
410     END\r
411     RETURN i\r
412 END putString;\r
415 PROCEDURE newLine* (file: tOutput): BOOLEAN;\r
416 VAR\r
417     i: INTEGER;\r
418 BEGIN\r
419     i := 0;\r
420     WHILE (file.eol[i] # 0X) & file.putChar(file, ORD(file.eol[i])) DO\r
421         INC(i)\r
422     END\r
423     RETURN i = LENGTH(file.eol)\r
424 END newLine;\r
427 PROCEDURE putCharUTF8 (file: tOutput; code: INTEGER): BOOLEAN;\r
428 VAR\r
429     res: BOOLEAN;\r
430 BEGIN\r
431     res := TRUE;\r
432     IF code <= 7FH THEN\r
433         putByte(file, code)\r
434     ELSIF (80H <= code) & (code <= 7FFH) THEN\r
435         putByte(file, code DIV 64 + 0C0H);\r
436         putByte(file, code MOD 64 + 080H)\r
437     ELSIF (800H <= code) & (code <= 0FFFFH) THEN\r
438         putByte(file, code DIV 4096 + 0E0H);\r
439         putByte(file, (code DIV 64) MOD 64 + 080H);\r
440         putByte(file, code MOD 64 + 080H)\r
441     ELSE\r
442         res := FALSE\r
443     END\r
444     RETURN res\r
445 END putCharUTF8;\r
448 PROCEDURE putCharW1251 (file: tOutput; code: INTEGER): BOOLEAN;\r
449 VAR\r
450     n: INTEGER;\r
451     res: BOOLEAN;\r
452 BEGIN\r
453     res := TRUE;\r
454     n := E.UNI[code, E.W1251];\r
455     IF n # E.UNDEF THEN\r
456         putByte(file, n)\r
457     ELSE\r
458         res := FALSE\r
459     END\r
460     RETURN res\r
461 END putCharW1251;\r
464 PROCEDURE putCharCP866 (file: tOutput; code: INTEGER): BOOLEAN;\r
465 VAR\r
466     n: INTEGER;\r
467     res: BOOLEAN;\r
468 BEGIN\r
469     res := TRUE;\r
470     n := E.UNI[code, E.CP866];\r
471     IF n # E.UNDEF THEN\r
472         putByte(file, n)\r
473     ELSE\r
474         res := FALSE\r
475     END\r
476     RETURN res\r
477 END putCharCP866;\r
480 PROCEDURE putCharUTF16LE (file: tOutput; code: INTEGER): BOOLEAN;\r
481 VAR\r
482     res: BOOLEAN;\r
483 BEGIN\r
484     IF (0 <= code) & (code <= 65535) THEN\r
485         res := TRUE;\r
486         putByte(file, code MOD 256);\r
487         putByte(file, code DIV 256)\r
488     ELSE\r
489         res := FALSE\r
490     END\r
491     RETURN res\r
492 END putCharUTF16LE;\r
495 PROCEDURE close* (VAR file: tOutput): BOOLEAN;\r
496 VAR\r
497     res: BOOLEAN;\r
498 BEGIN\r
499     res := TRUE;\r
500     IF file # NIL THEN\r
501         IF file.handle # NIL THEN\r
502             IF file.pos > 0 THEN\r
503                 res := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), file.pos) = file.pos\r
504             END;\r
505             File.Close(file.handle)\r
506         END;\r
507         DISPOSE(file)\r
508     END\r
509     RETURN res\r
510 END close;\r
513 PROCEDURE create* (name: tFileName; enc, nl: INTEGER): tOutput;\r
514 VAR\r
515     res: tOutput;\r
516 BEGIN\r
517     NEW(res);\r
518     res.pos := 0;\r
519     res.eol := eol[nl];\r
520     res.putChar := NIL;\r
521     IF (enc = E.UTF8) OR (enc = E.UTF8BOM) THEN\r
522         res.putChar := putCharUTF8;\r
523         IF enc = E.UTF8BOM THEN\r
524             ASSERT(res.putChar(res, ORD(BOM)))\r
525         END\r
526     ELSIF enc = E.UTF16LE THEN\r
527         res.putChar := putCharUTF16LE;\r
528     ELSIF enc = E.W1251 THEN\r
529         res.putChar := putCharW1251\r
530     ELSIF enc = E.CP866 THEN\r
531         res.putChar := putCharCP866\r
532     END;\r
533     ASSERT(res.putChar # NIL);\r
534     res.handle := File.Create(name);\r
535     IF res.handle = NIL THEN\r
536         DISPOSE(res)\r
537     END\r
538     RETURN res\r
539 END create;\r
542 PROCEDURE destroy* (VAR file: tInput);\r
543 BEGIN\r
544     IF file # NIL THEN\r
545         IF file.buffer # 0 THEN\r
546             file.buffer := KOSAPI.free(file.buffer - 12*ORD(file.clipbrd))\r
547         END;\r
548         DISPOSE(file)\r
549     END\r
550 END destroy;\r
553 BEGIN\r
554     eol[EOL_CRLF] := CR + LF;\r
555     eol[EOL_LF] := LF;\r
556     eol[EOL_CR] := CR;\r
557     eolNames[EOL_CRLF] := "CRLF";\r
558     eolNames[EOL_LF] := "LF";\r
559     eolNames[EOL_CR] := "CR"\r
560 END RW.