Subversion Repositories Kolibri OS

Rev

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

  1. (*
  2.     Copyright 2021, 2022 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 <http://www.gnu.org/licenses/>.
  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;
  43.         strBuf:   Lines.tLine;
  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;
  54.         eol:      E.tEOL;
  55.         putChar:  PROCEDURE (file: tOutput; code: INTEGER);
  56.         error:    BOOLEAN
  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
  102.     ELSE
  103.         n := -1
  104. (*    ELSIF (0F0H <= b) & (b <= 0F7H) THEN
  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
  119.         n := -1*)
  120.     END;
  121.  
  122.     code := b;
  123. (*
  124.     IF n > 2 THEN
  125.         n := -1
  126.     END;
  127. *)
  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.  
  155. (*
  156. PROCEDURE getCharUTF16LE (file: tInput): INTEGER;
  157.     RETURN getByte(file) + getByte(file) * 256
  158. END getCharUTF16LE;
  159. *)
  160.  
  161. PROCEDURE getString* (file: tInput; line: Lines.tLine; tabs: BOOLEAN; VAR eol: BOOLEAN): INTEGER;
  162. VAR
  163.     c: WCHAR;
  164.     i, k, n: INTEGER;
  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);
  171.         IF c = Lines.TAB1 THEN
  172.                 c := SPACE
  173.         ELSIF c = 0X THEN
  174.                 c := Lines.NUL
  175.         END;
  176.  
  177.         IF c = CR THEN
  178.             eol := TRUE;
  179.             file.CR := TRUE
  180.         ELSIF c = LF THEN
  181.             IF ~file.CR THEN
  182.                 eol := TRUE
  183.             END;
  184.             file.CR := FALSE
  185.         ELSIF c = TAB THEN
  186.             k := Lines.tab - i MOD Lines.tab;
  187.             IF tabs THEN
  188.                 Lines.setChar(file.strBuf, i, TAB)
  189.             ELSE
  190.                 Lines.setChar(file.strBuf, i, SPACE)
  191.             END;
  192.             INC(i);
  193.             DEC(k);
  194.             WHILE k > 0 DO
  195.                 IF tabs THEN
  196.                     Lines.setChar(file.strBuf, i, Lines.TAB1)
  197.                 ELSE
  198.                     Lines.setChar(file.strBuf, i, SPACE)
  199.                 END;
  200.                 INC(i);
  201.                 DEC(k)
  202.             END;
  203.             file.CR := FALSE
  204.         ELSIF c = BOM THEN
  205.             file.CR := FALSE
  206.         ELSE
  207.                 Lines.setChar(file.strBuf, i, c);
  208.             INC(i);
  209.             file.CR := FALSE
  210.         END
  211.     END;
  212.     IF i >= 0 THEN
  213.         Lines.setChar(file.strBuf, i, 0X);
  214.         file.strBuf.length := i;
  215.         Lines._insert2(line, 0, file.strBuf)
  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.  
  285. PROCEDURE detectEOL (text: tInput): INTEGER;
  286. VAR
  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);
  294.                 IF c = ORD(LF) THEN
  295.                         res := E.EOL_LF
  296.                 ELSIF c = ORD(CR) THEN
  297.                         IF text.getChar(text) = ORD(LF) THEN
  298.                                 res := E.EOL_CRLF
  299.                         ELSE
  300.                                 res := E.EOL_CR
  301.                         END
  302.                 END
  303.         END;
  304.         text.cnt := cnt;
  305.         text.pos := pos;
  306.         IF res = -1 THEN
  307.                 res := E.EOL_CRLF
  308.         END
  309.         RETURN res
  310. END detectEOL;
  311.  
  312.  
  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.  
  347. PROCEDURE load* (name: tFileName; VAR enc, eol: INTEGER): tInput;
  348. VAR
  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);
  368.         IF enc IN {E.UTF8BOM, E.UTF8} THEN
  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;
  375.         eol := detectEOL(res);
  376.         createStrBuf(res)
  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)
  395.     ELSE
  396.         createStrBuf(res)
  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
  405.         IF File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), BUF_SIZE) # BUF_SIZE THEN
  406.                 file.error := TRUE
  407.         END;
  408.         file.pos := 0
  409.     END;
  410.     file.buffer[file.pos] := b;
  411.     INC(file.pos)
  412. END putByte;
  413.  
  414.  
  415. PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER);
  416. VAR
  417.     i: INTEGER;
  418.     c: WCHAR;
  419. BEGIN
  420.     FOR i := 0 TO n - 1 DO
  421.         c := Lines.getChar(line, i);
  422.         IF c = Lines.NUL THEN
  423.                 file.putChar(file, 0)
  424.         ELSIF c # Lines.TAB1 THEN
  425.                 file.putChar(file, ORD(c))
  426.         END
  427.     END
  428. END putString;
  429.  
  430.  
  431. PROCEDURE newLine* (file: tOutput);
  432. VAR
  433.     i: INTEGER;
  434. BEGIN
  435.     i := 0;
  436.     WHILE file.eol[i] # 0X DO
  437.         file.putChar(file, ORD(file.eol[i]));
  438.         INC(i)
  439.     END
  440. END newLine;
  441.  
  442.  
  443. PROCEDURE putCharUTF8 (file: tOutput; code: INTEGER);
  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
  455.         putByte(file, ORD("?"))
  456.     END
  457. END putCharUTF8;
  458.  
  459.  
  460. PROCEDURE putCharCP (file: tOutput; code, cp: INTEGER);
  461. VAR
  462.     n: INTEGER;
  463. BEGIN
  464.     n := E.UNI[code, cp];
  465.     IF n # E.UNDEF THEN
  466.         putByte(file, n)
  467.     ELSE
  468.         putByte(file, ORD("?"))
  469.     END
  470. END putCharCP;
  471.  
  472.  
  473. PROCEDURE putCharW1251 (file: tOutput; code: INTEGER);
  474. BEGIN
  475.         putCharCP(file, code, E.W1251)
  476. END putCharW1251;
  477.  
  478.  
  479. PROCEDURE putCharCP866 (file: tOutput; code: INTEGER);
  480. BEGIN
  481.         putCharCP(file, code, E.CP866)
  482. END putCharCP866;
  483.  
  484. (*
  485. PROCEDURE putCharUTF16LE (file: tOutput; code: INTEGER);
  486. BEGIN
  487.     IF ~((0 <= code) & (code <= 65535)) THEN
  488.         code := ORD("?")
  489.     END;
  490.     putByte(file, code MOD 256);
  491.     putByte(file, code DIV 256)
  492. END putCharUTF16LE;*)
  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;
  505.             res := res & ~file.error;
  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;
  520.     res.eol := E.eol[nl];
  521.     res.putChar := NIL;
  522.     IF enc IN {E.UTF8BOM, E.UTF8} THEN
  523.         IF enc = E.UTF8BOM THEN
  524.             putCharUTF8(res, ORD(BOM))
  525.         END;
  526.         res.putChar := putCharUTF8
  527.     (*ELSIF enc = E.UTF16LE THEN
  528.         res.putChar := putCharUTF16LE*)
  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);
  536.     res.error := FALSE;
  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);
  545. VAR
  546.         null: INTEGER;
  547. BEGIN
  548.     IF file # NIL THEN
  549.         IF file.buffer # 0 THEN
  550.             null := KOSAPI.free(file.buffer - 12*ORD(file.clipbrd))
  551.         END;
  552.         IF file.strBuf # NIL THEN
  553.                 Lines.resize(file.strBuf, 0)
  554.         END;
  555.         DISPOSE(file)
  556.     END
  557. END destroy;
  558.  
  559.  
  560. END RW.