Subversion Repositories Kolibri OS

Rev

Rev 9200 | Rev 9560 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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