Subversion Repositories Kolibri OS

Rev

Rev 9671 | Rev 9708 | 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.cp1251[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 _loadFromMem (ptr, size: INTEGER; cp866: BOOLEAN; VAR enc, eol: INTEGER): tInput;
  348. VAR
  349.     res: tInput;
  350. BEGIN
  351.     NEW(res);
  352.     res.pos := 0;
  353.     res.CR := FALSE;
  354.     res.getChar := NIL;
  355.     res.clipbrd := FALSE;
  356.     res.buffer := ptr;
  357.     res.cnt := size;
  358.     IF res.buffer = 0 THEN
  359.         DISPOSE(res)
  360.     ELSE
  361.         enc := detectEncoding(res);
  362.         IF enc IN {E.UTF8BOM, E.UTF8} THEN
  363.             res.getChar := getCharUTF8
  364.         ELSIF enc = E.CP866 THEN
  365.             res.getChar := getCharCP866
  366.         ELSIF enc = E.W1251 THEN
  367.                 IF cp866 THEN
  368.                         enc := E.CP866;
  369.                         res.getChar := getCharCP866
  370.                 ELSE
  371.                 res.getChar := getCharW1251
  372.             END
  373.         END;
  374.         eol := detectEOL(res);
  375.         createStrBuf(res)
  376.     END
  377.     RETURN res
  378. END _loadFromMem;
  379.  
  380.  
  381. PROCEDURE loadFromMem* (ptr, size: INTEGER; VAR enc, eol: INTEGER): tInput;
  382.     RETURN _loadFromMem(ptr, size, TRUE, enc, eol)
  383. END loadFromMem;
  384.  
  385.  
  386. PROCEDURE loadFromFile* (name: tFileName; VAR enc, eol: INTEGER): tInput;
  387. VAR
  388.     ptr, size: INTEGER;
  389. BEGIN
  390.         size := File.FileSize(name);
  391.     IF size = 0 THEN
  392.         ptr := KOSAPI.malloc(4096)
  393.     ELSE
  394.         ptr := File.Load(name, size)
  395.     END
  396.         RETURN _loadFromMem(ptr, size, FALSE, enc, eol)
  397. END loadFromFile;
  398.  
  399.  
  400. PROCEDURE clipboard* (): tInput;
  401. VAR
  402.     res: tInput;
  403. BEGIN
  404.     NEW(res);
  405.     res.pos := 0;
  406.     res.CR := FALSE;
  407.     res.clipbrd := TRUE;
  408.     res.getChar := NIL;
  409.     res.getChar := getCharCP866;
  410.     res.buffer := CB.get(res.cnt);
  411.     IF res.buffer = 0 THEN
  412.         DISPOSE(res)
  413.     ELSE
  414.         createStrBuf(res)
  415.     END
  416.     RETURN res
  417. END clipboard;
  418.  
  419.  
  420. PROCEDURE putByte (file: tOutput; b: BYTE);
  421. BEGIN
  422.     IF file.pos = BUF_SIZE THEN
  423.         IF File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), BUF_SIZE) # BUF_SIZE THEN
  424.                 file.error := TRUE
  425.         END;
  426.         file.pos := 0
  427.     END;
  428.     file.buffer[file.pos] := b;
  429.     INC(file.pos)
  430. END putByte;
  431.  
  432.  
  433. PROCEDURE putString* (file: tOutput; line: Lines.tLine; n: INTEGER);
  434. VAR
  435.     i: INTEGER;
  436.     c: WCHAR;
  437. BEGIN
  438.     FOR i := 0 TO n - 1 DO
  439.         c := Lines.getChar(line, i);
  440.         IF c = Lines.NUL THEN
  441.                 file.putChar(file, 0)
  442.         ELSIF c # Lines.TAB1 THEN
  443.                 file.putChar(file, ORD(c))
  444.         END
  445.     END
  446. END putString;
  447.  
  448.  
  449. PROCEDURE newLine* (file: tOutput);
  450. VAR
  451.     i: INTEGER;
  452. BEGIN
  453.     i := 0;
  454.     WHILE file.eol[i] # 0X DO
  455.         file.putChar(file, ORD(file.eol[i]));
  456.         INC(i)
  457.     END
  458. END newLine;
  459.  
  460.  
  461. PROCEDURE putCharUTF8 (file: tOutput; code: INTEGER);
  462. BEGIN
  463.     IF code <= 7FH THEN
  464.         putByte(file, code)
  465.     ELSIF (80H <= code) & (code <= 7FFH) THEN
  466.         putByte(file, code DIV 64 + 0C0H);
  467.         putByte(file, code MOD 64 + 080H)
  468.     ELSIF (800H <= code) & (code <= 0FFFFH) THEN
  469.         putByte(file, code DIV 4096 + 0E0H);
  470.         putByte(file, (code DIV 64) MOD 64 + 080H);
  471.         putByte(file, code MOD 64 + 080H)
  472.     ELSE
  473.         putByte(file, ORD("?"))
  474.     END
  475. END putCharUTF8;
  476.  
  477.  
  478. PROCEDURE putCharCP (file: tOutput; code, cp: INTEGER);
  479. VAR
  480.     n: INTEGER;
  481. BEGIN
  482.     n := E.UNI[code, cp];
  483.     IF n # E.UNDEF THEN
  484.         putByte(file, n)
  485.     ELSE
  486.         putByte(file, ORD("?"))
  487.     END
  488. END putCharCP;
  489.  
  490.  
  491. PROCEDURE putCharW1251 (file: tOutput; code: INTEGER);
  492. BEGIN
  493.         putCharCP(file, code, E.W1251)
  494. END putCharW1251;
  495.  
  496.  
  497. PROCEDURE putCharCP866 (file: tOutput; code: INTEGER);
  498. BEGIN
  499.         putCharCP(file, code, E.CP866)
  500. END putCharCP866;
  501.  
  502. (*
  503. PROCEDURE putCharUTF16LE (file: tOutput; code: INTEGER);
  504. BEGIN
  505.     IF ~((0 <= code) & (code <= 65535)) THEN
  506.         code := ORD("?")
  507.     END;
  508.     putByte(file, code MOD 256);
  509.     putByte(file, code DIV 256)
  510. END putCharUTF16LE;*)
  511.  
  512.  
  513. PROCEDURE close* (VAR file: tOutput): BOOLEAN;
  514. VAR
  515.     res: BOOLEAN;
  516. BEGIN
  517.     res := TRUE;
  518.     IF file # NIL THEN
  519.         IF file.handle # NIL THEN
  520.             IF file.pos > 0 THEN
  521.                 res := File.Write(file.handle, SYSTEM.ADR(file.buffer[0]), file.pos) = file.pos
  522.             END;
  523.             res := res & ~file.error;
  524.             File.Close(file.handle)
  525.         END;
  526.         DISPOSE(file)
  527.     END
  528.     RETURN res
  529. END close;
  530.  
  531.  
  532. PROCEDURE create* (name: tFileName; enc, nl: INTEGER): tOutput;
  533. VAR
  534.     res: tOutput;
  535. BEGIN
  536.     NEW(res);
  537.     res.pos := 0;
  538.     res.eol := E.eol[nl];
  539.     res.putChar := NIL;
  540.     IF enc IN {E.UTF8BOM, E.UTF8} THEN
  541.         IF enc = E.UTF8BOM THEN
  542.             putCharUTF8(res, ORD(BOM))
  543.         END;
  544.         res.putChar := putCharUTF8
  545.     (*ELSIF enc = E.UTF16LE THEN
  546.         res.putChar := putCharUTF16LE*)
  547.     ELSIF enc = E.W1251 THEN
  548.         res.putChar := putCharW1251
  549.     ELSIF enc = E.CP866 THEN
  550.         res.putChar := putCharCP866
  551.     END;
  552.     ASSERT(res.putChar # NIL);
  553.     res.handle := File.Create(name);
  554.     res.error := FALSE;
  555.     IF res.handle = NIL THEN
  556.         DISPOSE(res)
  557.     END
  558.     RETURN res
  559. END create;
  560.  
  561.  
  562. PROCEDURE destroy* (VAR file: tInput);
  563. VAR
  564.         null: INTEGER;
  565. BEGIN
  566.     IF file # NIL THEN
  567.         IF file.buffer # 0 THEN
  568.             null := KOSAPI.free(file.buffer - 12*ORD(file.clipbrd))
  569.         END;
  570.         IF file.strBuf # NIL THEN
  571.                 Lines.resize(file.strBuf, 0)
  572.         END;
  573.         DISPOSE(file)
  574.     END
  575. END destroy;
  576.  
  577.  
  578. END RW.