Subversion Repositories Kolibri OS

Rev

Rev 9174 | Rev 9200 | 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_CRLF* = 0; EOL_LF* = 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.         CR:       BOOLEAN;
  49.         clipbrd:  BOOLEAN;
  50.         getChar:  PROCEDURE (file: tInput): INTEGER
  51.     END;
  52.  
  53.     tOutput* = POINTER TO RECORD
  54.         handle:   File.FS;
  55.         buffer:   ARRAY BUF_SIZE OF BYTE;
  56.         pos:      INTEGER;
  57.         eol:      tEOL;
  58.         putChar:  PROCEDURE (file: tOutput; code: INTEGER): BOOLEAN
  59.     END;
  60.  
  61.  
  62. VAR
  63.  
  64.     eol*: ARRAY 3 OF tEOL;
  65.     eolNames*: ARRAY 3, 16 OF WCHAR;
  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 detectEOL (text: tInput): INTEGER;
  300. VAR
  301.         pos, cnt, c, res: INTEGER;
  302. BEGIN
  303.         res := -1;
  304.         pos := text.pos;
  305.         cnt := text.cnt;
  306.         WHILE (text.cnt > 0) & (res = -1) DO
  307.                 c := text.getChar(text);
  308.                 IF c = 10 THEN
  309.                         res := EOL_LF
  310.                 ELSIF c = 13 THEN
  311.                         IF text.getChar(text) = 10 THEN
  312.                                 res := EOL_CRLF
  313.                         ELSE
  314.                                 res := EOL_CR
  315.                         END
  316.                 END
  317.         END;
  318.         text.cnt := cnt;
  319.         text.pos := pos;
  320.         IF res = -1 THEN
  321.                 res := EOL_CRLF
  322.         END
  323.         RETURN res
  324. END detectEOL;
  325.  
  326.  
  327. PROCEDURE load* (name: tFileName; VAR enc, eol: INTEGER): tInput;
  328. VAR
  329.     res: tInput;
  330.     fsize: INTEGER;
  331. BEGIN
  332.     NEW(res);
  333.     res.pos := 0;
  334.     res.CR := FALSE;
  335.     res.getChar := NIL;
  336.     res.clipbrd := FALSE;
  337.     fsize := File.FileSize(name);
  338.     IF fsize = 0 THEN
  339.         res.buffer := KOSAPI.malloc(4096);
  340.         ASSERT(res.buffer # 0);
  341.         res.cnt := 0
  342.     ELSE
  343.         res.buffer := File.Load(name, res.cnt)
  344.     END;
  345.     IF res.buffer = 0 THEN
  346.         DISPOSE(res)
  347.     ELSE
  348.         enc := detectEncoding(res);
  349.         IF (enc = E.UTF8BOM) OR (enc = E.UTF8) THEN
  350.             res.getChar := getCharUTF8
  351.         ELSIF enc = E.CP866 THEN
  352.             res.getChar := getCharCP866
  353.         ELSIF enc = E.W1251 THEN
  354.             res.getChar := getCharW1251
  355.         END;
  356.         eol := detectEOL(res)
  357.     END
  358.     RETURN res
  359. END load;
  360.  
  361.  
  362. PROCEDURE clipboard* (): tInput;
  363. VAR
  364.     res: tInput;
  365. BEGIN
  366.     NEW(res);
  367.     res.pos := 0;
  368.     res.CR := FALSE;
  369.     res.clipbrd := TRUE;
  370.     res.getChar := NIL;
  371.     res.getChar := getCharCP866;
  372.     res.buffer := CB.get(res.cnt);
  373.     IF res.buffer = 0 THEN
  374.         DISPOSE(res)
  375.     END
  376.     RETURN res
  377. END clipboard;
  378.  
  379.  
  380. PROCEDURE putByte (file: tOutput; b: BYTE);
  381. VAR
  382.     c: INTEGER;
  383. BEGIN
  384.     IF file.pos = BUF_SIZE THEN
  385.         c := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), BUF_SIZE);
  386.         file.pos := 0
  387.     END;
  388.     file.buffer[file.pos] := b;
  389.     INC(file.pos)
  390. END putByte;
  391.  
  392.  
  393. PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER): INTEGER;
  394. VAR
  395.     i: INTEGER;
  396.     c: WCHAR;
  397.     err: BOOLEAN;
  398. BEGIN
  399.     i := 0;
  400.     err := FALSE;
  401.     WHILE (i < n) & ~err DO
  402.         c := Lines.getChar(line, i);
  403.         IF c # Lines.TAB1 THEN
  404.             IF ~file.putChar(file, ORD(c)) THEN
  405.                 err := TRUE;
  406.                 DEC(i)
  407.             END
  408.         END;
  409.         INC(i)
  410.     END
  411.     RETURN i
  412. END putString;
  413.  
  414.  
  415. PROCEDURE newLine* (file: tOutput): BOOLEAN;
  416. VAR
  417.     i: INTEGER;
  418. BEGIN
  419.     i := 0;
  420.     WHILE (file.eol[i] # 0X) & file.putChar(file, ORD(file.eol[i])) DO
  421.         INC(i)
  422.     END
  423.     RETURN i = LENGTH(file.eol)
  424. END newLine;
  425.  
  426.  
  427. PROCEDURE putCharUTF8 (file: tOutput; code: INTEGER): BOOLEAN;
  428. VAR
  429.     res: BOOLEAN;
  430. BEGIN
  431.     res := TRUE;
  432.     IF code <= 7FH THEN
  433.         putByte(file, code)
  434.     ELSIF (80H <= code) & (code <= 7FFH) THEN
  435.         putByte(file, code DIV 64 + 0C0H);
  436.         putByte(file, code MOD 64 + 080H)
  437.     ELSIF (800H <= code) & (code <= 0FFFFH) THEN
  438.         putByte(file, code DIV 4096 + 0E0H);
  439.         putByte(file, (code DIV 64) MOD 64 + 080H);
  440.         putByte(file, code MOD 64 + 080H)
  441.     ELSE
  442.         res := FALSE
  443.     END
  444.     RETURN res
  445. END putCharUTF8;
  446.  
  447.  
  448. PROCEDURE putCharW1251 (file: tOutput; code: INTEGER): BOOLEAN;
  449. VAR
  450.     n: INTEGER;
  451.     res: BOOLEAN;
  452. BEGIN
  453.     res := TRUE;
  454.     n := E.UNI[code, E.W1251];
  455.     IF n # E.UNDEF THEN
  456.         putByte(file, n)
  457.     ELSE
  458.         res := FALSE
  459.     END
  460.     RETURN res
  461. END putCharW1251;
  462.  
  463.  
  464. PROCEDURE putCharCP866 (file: tOutput; code: INTEGER): BOOLEAN;
  465. VAR
  466.     n: INTEGER;
  467.     res: BOOLEAN;
  468. BEGIN
  469.     res := TRUE;
  470.     n := E.UNI[code, E.CP866];
  471.     IF n # E.UNDEF THEN
  472.         putByte(file, n)
  473.     ELSE
  474.         res := FALSE
  475.     END
  476.     RETURN res
  477. END putCharCP866;
  478.  
  479.  
  480. PROCEDURE putCharUTF16LE (file: tOutput; code: INTEGER): BOOLEAN;
  481. VAR
  482.     res: BOOLEAN;
  483. BEGIN
  484.     IF (0 <= code) & (code <= 65535) THEN
  485.         res := TRUE;
  486.         putByte(file, code MOD 256);
  487.         putByte(file, code DIV 256)
  488.     ELSE
  489.         res := FALSE
  490.     END
  491.     RETURN res
  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.             File.Close(file.handle)
  506.         END;
  507.         DISPOSE(file)
  508.     END
  509.     RETURN res
  510. END close;
  511.  
  512.  
  513. PROCEDURE create* (name: tFileName; enc, nl: INTEGER): tOutput;
  514. VAR
  515.     res: tOutput;
  516. BEGIN
  517.     NEW(res);
  518.     res.pos := 0;
  519.     res.eol := eol[nl];
  520.     res.putChar := NIL;
  521.     IF (enc = E.UTF8) OR (enc = E.UTF8BOM) THEN
  522.         res.putChar := putCharUTF8;
  523.         IF enc = E.UTF8BOM THEN
  524.             ASSERT(res.putChar(res, ORD(BOM)))
  525.         END
  526.     ELSIF enc = E.UTF16LE THEN
  527.         res.putChar := putCharUTF16LE;
  528.     ELSIF enc = E.W1251 THEN
  529.         res.putChar := putCharW1251
  530.     ELSIF enc = E.CP866 THEN
  531.         res.putChar := putCharCP866
  532.     END;
  533.     ASSERT(res.putChar # NIL);
  534.     res.handle := File.Create(name);
  535.     IF res.handle = NIL THEN
  536.         DISPOSE(res)
  537.     END
  538.     RETURN res
  539. END create;
  540.  
  541.  
  542. PROCEDURE destroy* (VAR file: tInput);
  543. BEGIN
  544.     IF file # NIL THEN
  545.         IF file.buffer # 0 THEN
  546.             file.buffer := KOSAPI.free(file.buffer - 12*ORD(file.clipbrd))
  547.         END;
  548.         DISPOSE(file)
  549.     END
  550. END destroy;
  551.  
  552.  
  553. BEGIN
  554.     eol[EOL_CRLF] := CR + LF;
  555.     eol[EOL_LF] := LF;
  556.     eol[EOL_CR] := CR;
  557.     eolNames[EOL_CRLF] := "CRLF";
  558.     eolNames[EOL_LF] := "LF";
  559.     eolNames[EOL_CR] := "CR"
  560. END RW.