Subversion Repositories Kolibri OS

Rev

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

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