Subversion Repositories Kolibri OS

Rev

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

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