Subversion Repositories Kolibri OS

Rev

Rev 9174 | 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.     TAB_SIZE* = 4;
  33.  
  34.     BUF_SIZE = 65536;
  35.  
  36.     NAME_LEN = 1024;
  37.  
  38.     EOL_LF* = 0; EOL_CRLF* = 1; EOL_CR* = 2;
  39.  
  40.  
  41. TYPE
  42.  
  43.     tFileName* = ARRAY NAME_LEN OF CHAR;
  44.  
  45.     tEOL = ARRAY 3 OF WCHAR;
  46.  
  47.     tInput* = POINTER TO RECORD
  48.         buffer:   INTEGER;
  49.         pos, cnt: INTEGER;
  50.         enc:      INTEGER;
  51.         CR:       BOOLEAN;
  52.         clipbrd:  BOOLEAN;
  53.         getChar:  PROCEDURE (file: tInput): INTEGER
  54.     END;
  55.  
  56.     tOutput* = POINTER TO RECORD
  57.         handle:   File.FS;
  58.         buffer:   ARRAY BUF_SIZE OF BYTE;
  59.         pos:      INTEGER;
  60.         eol:      tEOL;
  61.         putChar:  PROCEDURE (file: tOutput; code: INTEGER): BOOLEAN
  62.     END;
  63.  
  64.  
  65. VAR
  66.  
  67.     eol*: ARRAY 3 OF tEOL;
  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.  
  169. PROCEDURE getString* (file: tInput; line: Lines.tLine; VAR eol: BOOLEAN): INTEGER;
  170. VAR
  171.     c: WCHAR;
  172.     i, L, k, n: INTEGER;
  173.     s: ARRAY 1000 OF WCHAR;
  174. BEGIN
  175.     L := LEN(s);
  176.     eol := FALSE;
  177.     n := 0;
  178.     i := ORD(file.cnt > 0) - 1;
  179.     WHILE (file.cnt > 0) & ~eol DO
  180.         c := WCHR(file.getChar(file) MOD 65536);
  181.         IF c = CR THEN
  182.             eol := TRUE;
  183.             file.CR := TRUE
  184.         ELSIF (c = LF) OR (c = 0X) THEN
  185.             IF ~file.CR THEN
  186.                 eol := TRUE
  187.             END;
  188.             file.CR := FALSE
  189.         ELSIF c = TAB THEN
  190.             k := TAB_SIZE - i MOD TAB_SIZE;
  191.             WHILE k > 0 DO
  192.                 s[i] := SPACE;
  193.                 INC(i);
  194.                 IF i = L THEN
  195.                     Lines.concat(line, s);
  196.                     INC(n, i);
  197.                     i := 0
  198.                 END;
  199.                 DEC(k)
  200.             END;
  201.             file.CR := FALSE
  202.         ELSIF c = BOM THEN
  203.             file.CR := FALSE
  204.         ELSE
  205.             s[i] := c;
  206.             INC(i);
  207.             IF i = L THEN
  208.                 Lines.concat(line, s);
  209.                 INC(n, i);
  210.                 i := 0
  211.             END;
  212.             file.CR := FALSE
  213.         END
  214.     END;
  215.     IF i >= 0 THEN
  216.         s[i] := 0X;
  217.         Lines.concat(line, s);
  218.     END;
  219.     INC(n, i)
  220.     RETURN n
  221. END getString;
  222.  
  223.  
  224. PROCEDURE detectEncoding (text: tInput): INTEGER;
  225. VAR
  226.     pos, cnt, res: INTEGER;
  227.     continue, bom: BOOLEAN;
  228.     b: BYTE;
  229.     cp866, w1251: INTEGER;
  230. BEGIN
  231.     pos := text.pos;
  232.     cnt := text.cnt;
  233.     continue := TRUE;
  234.     WHILE (text.cnt > 0) & continue DO
  235.         IF getByte(text) > 127 THEN
  236.             continue := FALSE
  237.         END
  238.     END;
  239.     text.cnt := cnt;
  240.     text.pos := pos;
  241.     IF continue THEN
  242.         res := E.CP866
  243.     ELSE
  244.         bom := getCharUTF8(text) = ORD(BOM);
  245.         continue := TRUE;
  246.         text.cnt := cnt;
  247.         text.pos := pos;
  248.         WHILE (text.cnt > 0) & continue DO
  249.             IF getCharUTF8(text) = E.UNDEF THEN
  250.                 continue := FALSE
  251.             END
  252.         END;
  253.         IF continue THEN
  254.             IF bom THEN
  255.                 res := E.UTF8BOM
  256.             ELSE
  257.                 res := E.UTF8
  258.             END
  259.         ELSE
  260.             text.cnt := cnt;
  261.             text.pos := pos;
  262.             cp866 := 0;
  263.             w1251 := 0;
  264.             WHILE text.cnt > 0 DO
  265.                 b := getByte(text);
  266.                 IF b > 127 THEN
  267.                     IF b >= 192 THEN
  268.                         INC(w1251)
  269.                     ELSE
  270.                         INC(cp866)
  271.                     END
  272.                 END
  273.             END;
  274.             IF w1251 > cp866 THEN
  275.                 res := E.W1251
  276.             ELSE
  277.                 res := E.CP866
  278.             END
  279.         END;
  280.         text.cnt := cnt;
  281.         text.pos := pos
  282.     END
  283.     RETURN res
  284. END detectEncoding;
  285.  
  286.  
  287. PROCEDURE load* (name: tFileName; VAR enc: INTEGER): tInput;
  288. VAR
  289.     res: tInput;
  290.     fsize: INTEGER;
  291. BEGIN
  292.     NEW(res);
  293.     res.pos := 0;
  294.     res.CR := FALSE;
  295.     res.getChar := NIL;
  296.     res.clipbrd := FALSE;
  297.     fsize := File.FileSize(name);
  298.     IF fsize = 0 THEN
  299.         res.buffer := KOSAPI.malloc(4096);
  300.         ASSERT(res.buffer # 0);
  301.         res.cnt := 0
  302.     ELSE
  303.         res.buffer := File.Load(name, res.cnt)
  304.     END;
  305.     IF res.buffer = 0 THEN
  306.         DISPOSE(res)
  307.     ELSE
  308.         enc := detectEncoding(res);
  309.         IF (enc = E.UTF8BOM) OR (enc = E.UTF8) THEN
  310.             res.getChar := getCharUTF8
  311.         ELSIF enc = E.CP866 THEN
  312.             res.getChar := getCharCP866
  313.         ELSIF enc = E.W1251 THEN
  314.             res.getChar := getCharW1251
  315.         END;
  316.         res.enc := enc
  317.     END
  318.     RETURN res
  319. END load;
  320.  
  321.  
  322. PROCEDURE clipboard* (): tInput;
  323. VAR
  324.     res: tInput;
  325. BEGIN
  326.     NEW(res);
  327.     res.pos := 0;
  328.     res.CR := FALSE;
  329.     res.clipbrd := TRUE;
  330.     res.getChar := NIL;
  331.     res.enc := E.CP866;
  332.     res.getChar := getCharCP866;
  333.     res.buffer := CB.get(res.cnt);
  334.     IF res.buffer = 0 THEN
  335.         DISPOSE(res)
  336.     END
  337.     RETURN res
  338. END clipboard;
  339.  
  340.  
  341. PROCEDURE putByte (file: tOutput; b: BYTE);
  342. VAR
  343.     c: INTEGER;
  344. BEGIN
  345.     IF file.pos = BUF_SIZE THEN
  346.         c := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), BUF_SIZE);
  347.         file.pos := 0
  348.     END;
  349.     file.buffer[file.pos] := b;
  350.     INC(file.pos)
  351. END putByte;
  352.  
  353.  
  354. PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER): INTEGER;
  355. VAR
  356.     i: INTEGER;
  357. BEGIN
  358.     i := 0;
  359.     WHILE (i < n) & file.putChar(file, ORD(Lines.getChar(line, i))) DO
  360.         INC(i)
  361.     END
  362.     RETURN i
  363. END putString;
  364.  
  365.  
  366. PROCEDURE newLine* (file: tOutput): BOOLEAN;
  367. VAR
  368.     i: INTEGER;
  369. BEGIN
  370.     i := 0;
  371.     WHILE (file.eol[i] # 0X) & file.putChar(file, ORD(file.eol[i])) DO
  372.         INC(i)
  373.     END
  374.     RETURN i = LENGTH(file.eol)
  375. END newLine;
  376.  
  377.  
  378. PROCEDURE putCharUTF8 (file: tOutput; code: INTEGER): BOOLEAN;
  379. VAR
  380.     res: BOOLEAN;
  381. BEGIN
  382.     res := TRUE;
  383.     IF code <= 7FH THEN
  384.         putByte(file, code)
  385.     ELSIF (80H <= code) & (code <= 7FFH) THEN
  386.         putByte(file, code DIV 64 + 0C0H);
  387.         putByte(file, code MOD 64 + 080H)
  388.     ELSIF (800H <= code) & (code <= 0FFFFH) THEN
  389.         putByte(file, code DIV 4096 + 0E0H);
  390.         putByte(file, (code DIV 64) MOD 64 + 080H);
  391.         putByte(file, code MOD 64 + 080H)
  392.     ELSE
  393.         res := FALSE
  394.     END
  395.     RETURN res
  396. END putCharUTF8;
  397.  
  398.  
  399. PROCEDURE putCharW1251 (file: tOutput; code: INTEGER): BOOLEAN;
  400. VAR
  401.     n: INTEGER;
  402.     res: BOOLEAN;
  403. BEGIN
  404.     res := TRUE;
  405.     n := E.UNI[code, E.W1251];
  406.     IF n # E.UNDEF THEN
  407.         putByte(file, n)
  408.     ELSE
  409.         res := FALSE
  410.     END
  411.     RETURN res
  412. END putCharW1251;
  413.  
  414.  
  415. PROCEDURE putCharCP866 (file: tOutput; code: INTEGER): BOOLEAN;
  416. VAR
  417.     n: INTEGER;
  418.     res: BOOLEAN;
  419. BEGIN
  420.     res := TRUE;
  421.     n := E.UNI[code, E.CP866];
  422.     IF n # E.UNDEF THEN
  423.         putByte(file, n)
  424.     ELSE
  425.         res := FALSE
  426.     END
  427.     RETURN res
  428. END putCharCP866;
  429.  
  430.  
  431. PROCEDURE putCharUTF16LE (file: tOutput; code: INTEGER): BOOLEAN;
  432. VAR
  433.     res: BOOLEAN;
  434. BEGIN
  435.     IF (0 <= code) & (code <= 65535) THEN
  436.         res := TRUE;
  437.         putByte(file, code MOD 256);
  438.         putByte(file, code DIV 256)
  439.     ELSE
  440.         res := FALSE
  441.     END
  442.     RETURN res
  443. END putCharUTF16LE;
  444.  
  445.  
  446. PROCEDURE close* (VAR file: tOutput): BOOLEAN;
  447. VAR
  448.     res: BOOLEAN;
  449. BEGIN
  450.     res := TRUE;
  451.     IF file # NIL THEN
  452.         IF file.handle # NIL THEN
  453.             IF file.pos > 0 THEN
  454.                 res := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), file.pos) = file.pos
  455.             END;
  456.             File.Close(file.handle)
  457.         END;
  458.         DISPOSE(file)
  459.     END
  460.     RETURN res
  461. END close;
  462.  
  463.  
  464. PROCEDURE create* (name: tFileName; enc, nl: INTEGER): tOutput;
  465. VAR
  466.     res: tOutput;
  467. BEGIN
  468.     NEW(res);
  469.     res.pos := 0;
  470.     res.eol := eol[nl];
  471.     res.putChar := NIL;
  472.     IF (enc = E.UTF8) OR (enc = E.UTF8BOM) THEN
  473.         res.putChar := putCharUTF8;
  474.         IF enc = E.UTF8BOM THEN
  475.             ASSERT(res.putChar(res, ORD(BOM)))
  476.         END
  477.     ELSIF enc = E.UTF16LE THEN
  478.         res.putChar := putCharUTF16LE;
  479.     ELSIF enc = E.W1251 THEN
  480.         res.putChar := putCharW1251
  481.     ELSIF enc = E.CP866 THEN
  482.         res.putChar := putCharCP866
  483.     END;
  484.     ASSERT(res.putChar # NIL);
  485.     res.handle := File.Create(name);
  486.     IF res.handle = NIL THEN
  487.         DISPOSE(res)
  488.     END
  489.     RETURN res
  490. END create;
  491.  
  492.  
  493. PROCEDURE destroy* (VAR file: tInput);
  494. BEGIN
  495.     IF file # NIL THEN
  496.         IF file.buffer # 0 THEN
  497.             file.buffer := KOSAPI.free(file.buffer - 12*ORD(file.clipbrd))
  498.         END;
  499.         DISPOSE(file)
  500.     END
  501. END destroy;
  502.  
  503.  
  504. BEGIN
  505.     eol[EOL_LF] := LF;
  506.     eol[EOL_CRLF] := CR + LF;
  507.     eol[EOL_CR] := CR
  508. END RW.