Explorar o código

From CHAR8 to CHAR16, module Files finally places .tmp.. files in /tmp

Arthur Yefimov %!s(int64=3) %!d(string=hai) anos
pai
achega
e446b5b98d
Modificáronse 9 ficheiros con 218 adicións e 244 borrados
  1. 1 1
      src/Editor.Mod
  2. 11 39
      src/EditorText.Mod
  3. 149 102
      src/Files.Mod
  4. 23 46
      src/FreeOberon.Mod
  5. 20 22
      src/Graph.Mod
  6. 1 2
      src/In.Mod
  7. 2 19
      src/OV.Mod
  8. 4 11
      src/Terminal.Mod
  9. 7 2
      src/Utf8.Mod

+ 1 - 1
src/Editor.Mod

@@ -962,7 +962,7 @@ END EditorMouseMove;
 
 PROCEDURE EditorTextInput(c: OV.Control; ch: CHAR);
 BEGIN
-  IF ch # 0X THEN c(Editor).text.InsertChar(OV.ToCP866(ch)); T.ResetCursorBlink;
+  IF ch # 0X THEN c(Editor).text.InsertChar(ch); T.ResetCursorBlink;
     c(Editor).text.selected := FALSE; PrintText(c(Editor))
   END
 END EditorTextInput;

+ 11 - 39
src/EditorText.Mod

@@ -321,29 +321,15 @@ BEGIN
   buf[i] := 0X
 END CopySelection;
 
-PROCEDURE WriteString(VAR r: Files.Rider; s: ARRAY OF CHAR);
-VAR i: INTEGER; ch: CHAR;
-BEGIN
-  i := 0; s[LEN(s) - 1] := 0X;
-  WHILE s[i] # 0X DO
-    ch := s[i];
-    IF ORD(ch) < 128 THEN Files.WriteChar(r, SHORT(ch)) (* ASCII *)
-    ELSIF ORD(ch) = 240 THEN (* Big cyrillic Yo *)
-      Files.WriteChar(r, 0D0X); Files.WriteChar(r, 81X)
-    ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
-      Files.WriteChar(r, 0D0X);
-      Files.WriteChar(r, SHORT(CHR(ORD(ch) - 128 + 090H)))
-    ELSE
-      Files.WriteChar(r, 0D1X);
-      Files.WriteChar(r, SHORT(CHR(ORD(ch) - 224 + 080H)))
-    END;
-    INC(i)
-  END
+PROCEDURE WriteString(VAR r: Files.Rider; IN s: ARRAY OF CHAR);
+VAR i: INTEGER;
+BEGIN i := 0;
+  WHILE (i # LEN(s)) & (s[i] # 0X) DO Files.WriteChar(r, s[i]); INC(i) END
 END WriteString;
 
 PROCEDURE WriteLn(VAR r: Files.Rider);
 BEGIN
-  IF Config.isWindows THEN Files.WriteChar(r, 0DX) END;
+  IF Config.isWindows THEN Files.WriteChar(r, 0DX) END;(*!TODO crossplatform*)
   Files.WriteChar(r, 0AX)
 END WriteLn;
 
@@ -362,11 +348,9 @@ PROCEDURE (t: Text) SaveToFile*(fname: ARRAY OF CHAR): BOOLEAN, NEW;
 VAR f: Files.File; r: Files.Rider;
   success: BOOLEAN;
   L: Line;
-  s: ARRAY 2048 OF SHORTCHAR;
 BEGIN success := FALSE;
   FixFname(fname);
-  Utf8.Encode(fname, s);
-  f := Files.New(s);
+  f := Files.New(fname);
   IF f # NIL THEN (*!FIXME error checking is wrong *)
     Files.Set(r, f, 0);
     L := t.first;
@@ -388,26 +372,16 @@ BEGIN success := FALSE;
 RETURN success END SaveToFile;
 
 PROCEDURE ReadLine(VAR r: Files.Rider; L: Line);
-VAR x: SHORTCHAR; ch: CHAR; i: INTEGER;
+VAR ch: CHAR; i: INTEGER;
 BEGIN i := 0;
-  Files.ReadChar(r, x); ch := LONG(x);
+  Files.ReadChar(r, ch);
   WHILE ~r.eof & (i < LEN(L.s) - 1) & (ch # 0AX) & (ch # 0DX) DO
-    IF ORD(ch) < 128 THEN L.s[i] := ch
-    ELSIF ch = 0D0X THEN
-      Files.ReadChar(r, x); ch := LONG(x);
-      IF ch = 081X THEN L.s[i] := CHR(240) (* Big Yo *)
-      ELSE L.s[i] := CHR(ORD(ch) - 16)
-      END
-    ELSIF ch = 0D1X THEN
-      Files.ReadChar(r, x); ch := LONG(x); L.s[i] := CHR(ORD(ch) + 96)
-    END;
-    Files.ReadChar(r, x); ch := LONG(x);
-    INC(i)
+    L.s[i] := ch; Files.ReadChar(r, ch); INC(i)
   END;
   L.s[i] := 0X;
   IF ch = 0DX THEN
     L.lineEndLen := 2; (* Assume CRLF (DOS) *)
-    Files.ReadChar(r, x) (* Skip LF *)
+    Files.ReadChar(r, ch) (* Skip LF *)
   ELSE L.lineEndLen := 1 (* Assume LF (UNIX) *)
   END
 END ReadLine;
@@ -416,12 +390,10 @@ PROCEDURE (t: Text) LoadFromFile*(fname: ARRAY OF CHAR): BOOLEAN, NEW;
 VAR f: Files.File; r: Files.Rider;
   success: BOOLEAN;
   L: Line;
-  s: ARRAY 2048 OF SHORTCHAR;
 BEGIN success := FALSE;
   IF fname[0] # 0X THEN
     FixFname(fname);
-    Utf8.Encode(fname, s);
-    f := Files.Old(s);
+    f := Files.Old(fname);
     IF f # NIL THEN
       Files.Set(r, f, 0);
       L := NewLine();

+ 149 - 102
src/Files.Mod

@@ -28,7 +28,10 @@ CONST
                  just write directly to final register name *)
 
 TYPE
-  FileName = ARRAY 256 OF SHORTCHAR;
+  SBYTE* = BYTE;
+  BYTE* = UBYTE;
+
+  FileName = ARRAY 256 OF CHAR;
   File* = POINTER TO FileDesc;
   Buffer = POINTER TO BufDesc;
 
@@ -48,7 +51,7 @@ TYPE
     chg: BOOLEAN;
     org: LONGINT;
     size: INTEGER;
-    data: ARRAY BufSize OF BYTE
+    data: ARRAY BufSize OF SBYTE
   END;
 
   Rider* = RECORD
@@ -64,8 +67,8 @@ VAR
   files: POINTER [notag] TO FileDesc;
 
   tempno: INTEGER;
-  home: ARRAY 1024 OF SHORTCHAR;
-  SearchPath: ARRAY 4096 OF SHORTCHAR;
+  home: ARRAY 1024 OF CHAR;
+  SearchPath: ARRAY 4096 OF CHAR;
 
 PROCEDURE -IdxTrap(pos: INTEGER) '__HALT(-1, "Files.Mod", pos)';
 
@@ -75,8 +78,8 @@ PROCEDURE Err(IN s: ARRAY OF CHAR; f: File; errcode: Platform.ErrorCode);
 BEGIN
   Out.Ln; Out.String('-- '); Out.String(s); Out.String(': ');
   IF f # NIL THEN
-    IF f.registerName # '' THEN Out.Utf8(f.registerName)
-    ELSE Out.Utf8(f.workName)
+    IF f.registerName # '' THEN Out.String(f.registerName)
+    ELSE Out.String(f.workName)
     END;
     IF f.fd # 0 THEN Out.String(' f.fd = '); Out.Int(f.fd, 1) END
   END;
@@ -85,8 +88,7 @@ BEGIN
   HALT(99)
 END Err;
 
-PROCEDURE MakeFileName(IN dir, name: ARRAY OF SHORTCHAR;
-    VAR dest: ARRAY OF SHORTCHAR);
+PROCEDURE MakeFileName(IN dir, name: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
 VAR i, j: INTEGER;
 BEGIN i := 0; j := 0;
   WHILE dir[i] # 0X DO dest[i] := dir[i]; INC(i) END;
@@ -95,30 +97,19 @@ BEGIN i := 0; j := 0;
   dest[i] := 0X
 END MakeFileName;
 
-PROCEDURE GetTempName(IN finalName: ARRAY OF SHORTCHAR;
-    VAR name: ARRAY OF SHORTCHAR);
-VAR n, i, j: INTEGER;
-BEGIN
-  INC(tempno); n := tempno; i := 0;
-  IF finalName[0] # PathDelimiter THEN  (* relative pathname *)
-    WHILE Platform.CWD[i] # 0X DO name[i] := Platform.CWD[i]; INC(i) END;
-    IF Platform.CWD[i-1] # PathDelimiter THEN
-      name[i] := PathDelimiter; INC(i)
-    END
-  END;
-  j := 0;
-  WHILE finalName[j] # 0X DO name[i] := finalName[j]; INC(i); INC(j) END;
-  DEC(i);
-  WHILE name[i] # PathDelimiter DO DEC(i) END;
-  name[i + 1] := '.'; name[i + 2] := 't'; name[i + 3] := 'm';
-  name[i + 4] := 'p'; name[i + 5] := '.'; INC(i, 6);
-  WHILE n > 0 DO
-    name[i] := SHORT(CHR(n MOD 10 + ORD('0'))); n := n DIV 10; INC(i)
-  END;
+PROCEDURE GetTempName(IN finalName: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
+VAR n, i: INTEGER;
+  q: ARRAY 256 OF SHORTCHAR;
+BEGIN INC(tempno); Platform.GetTempPath(q); Utf8.Decode(q, name);
+  i := 0; WHILE name[i] # 0X DO INC(i) END;
+
+  name[i] := 'o'; INC(i); name[i] := 'b'; INC(i); name[i] := 'e'; INC(i);
+  name[i] := 'r'; INC(i); name[i] := 'o'; INC(i); name[i] := 'n'; INC(i);
+  name[i] := '.'; INC(i); n := tempno;
+  REPEAT name[i] := CHR(n MOD 10 + 30H); n := n DIV 10; INC(i) UNTIL n = 0;
+
   name[i] := '.'; INC(i); n := Platform.PID;
-  WHILE n > 0 DO
-    name[i] := SHORT(CHR(n MOD 10 + ORD('0'))); n := n DIV 10; INC(i)
-  END;
+  REPEAT name[i] := CHR(n MOD 10 + 30H); n := n DIV 10; INC(i) UNTIL n = 0;
   name[i] := 0X
 END GetTempName;
 
@@ -131,12 +122,14 @@ END GetTempName;
    a real Oberon system, where registering the new file has the effect of
    unregistering the old file. To simulate this we need to change the old
    Files.File back to a temp file. *)
-PROCEDURE Deregister(IN name: ARRAY OF SHORTCHAR);
+PROCEDURE Deregister(IN name: ARRAY OF CHAR);
 VAR identity: Platform.FileIdentity;
   osfile: File;
   error: Platform.ErrorCode;
+  q, s: ARRAY 2048 OF SHORTCHAR;
 BEGIN
-  IF Platform.IdentifyByName(name, identity) = 0 THEN
+  Utf8.Encode(name, q);
+  IF Platform.IdentifyByName(q, identity) = 0 THEN
     (* The name we are registering is an already existing file. *)
     osfile := files;
     WHILE (osfile # NIL) & ~Platform.SameFile(osfile.identity, identity) DO
@@ -151,7 +144,9 @@ BEGIN
       osfile.tempFile := TRUE;
       error := Platform.CloseFile(osfile.fd);
       osfile.state := close;
-      error := Platform.RenameFile(osfile.registerName, osfile.workName);
+      Utf8.Encode(osfile.registerName, q);
+      Utf8.Encode(osfile.workName, s);
+      error := Platform.RenameFile(q, s);
       IF error # 0 THEN
         Err('Could not rename previous version of file being registered',
           osfile, error)
@@ -160,14 +155,15 @@ BEGIN
   END
 END Deregister;
 
+(** Makes sure there is an OS file backing this Oberon file.
+    Used when more data has been written to an unregistered new file than
+    buffers can hold, or when registering a new file whose data is all in
+    buffers. *)
 PROCEDURE Create(f: File);
-(* Makes sure there is an OS file backing this Oberon file.
-   Used when more data has been written to an unregistered new file than
-   buffers can hold, or when registering a new file whose data is all in
-   buffers. *)
 VAR done: BOOLEAN;
   error: Platform.ErrorCode;
   err: ARRAY 32 OF CHAR;
+  q: ARRAY 2048 OF SHORTCHAR;
 BEGIN
   IF f.fd = Platform.InvalidHandleValue() THEN
     IF f.state = create THEN
@@ -182,9 +178,10 @@ BEGIN
       f.workName := f.registerName; f.registerName := ''; f.tempFile := FALSE
     END;
     (* Unlink first to avoid stale NFS handles and to avoid reuse of inodes *)
-    error := Platform.DeleteFile(f.workName);
+    Utf8.Encode(f.workName, q);
+    error := Platform.DeleteFile(q);
 
-    error := Platform.NewFile(f.workName, f.fd);
+    error := Platform.NewFile(q, f.fd);
     done := error = 0;
     IF done THEN
       (* Link this file into the list of OS backed files. *)
@@ -247,7 +244,7 @@ END Close;
 PROCEDURE Length*(f: File): LONGINT;
 BEGIN RETURN f.len END Length;
 
-PROCEDURE New*(IN name: ARRAY OF SHORTCHAR): File;
+PROCEDURE New*(IN name: ARRAY OF CHAR): File;
 VAR f: File;
 BEGIN
   NEW(f); f.workName := ''; f.registerName := name$;
@@ -255,11 +252,11 @@ BEGIN
   f.len := 0; f.pos := 0; f.swapper := -1 ;
 RETURN f END New;
 
-PROCEDURE ScanPath(VAR pos: INTEGER; VAR dir: ARRAY OF SHORTCHAR);
+PROCEDURE ScanPath(VAR pos: INTEGER; VAR dir: ARRAY OF CHAR);
 (* Extract next individual directory from searchpath starting at pos,
    updating pos and returning dir.
    Supports ~, ~user and blanks inside path *)
-VAR i: INTEGER; ch: SHORTCHAR;
+VAR i: INTEGER; ch: CHAR;
 BEGIN
   i := 0;
   IF SearchPath = '' THEN (* Default search path is the current directory *)
@@ -284,8 +281,8 @@ BEGIN
   dir[i] := 0X
 END ScanPath;
 
-PROCEDURE HasDir(IN name: ARRAY OF SHORTCHAR): BOOLEAN;
-VAR i: INTEGER; ch: SHORTCHAR;
+PROCEDURE HasDir(IN name: ARRAY OF CHAR): BOOLEAN;
+VAR i: INTEGER; ch: CHAR;
 BEGIN i := 0; ch := name[0];
   WHILE (ch # 0X) & (ch # PathDelimiter) DO INC(i); ch := name[i] END ;
 RETURN ch = PathDelimiter END HasDir;
@@ -310,35 +307,34 @@ BEGIN f := files;
   RETURN NIL
 END CacheEntry;
 
-PROCEDURE Old*(IN name: ARRAY OF SHORTCHAR): File;
+PROCEDURE Old*(IN name: ARRAY OF CHAR): File;
 VAR f: File;
   fd: Platform.FileHandle;
   pos: INTEGER;
   done: BOOLEAN;
-  dir, path: ARRAY 256 OF SHORTCHAR;
+  dir, path: ARRAY 256 OF CHAR;
   error: Platform.ErrorCode;
   identity: Platform.FileIdentity;
+  q: ARRAY 4096 OF SHORTCHAR;
 BEGIN
-  (* Out.String('Files.Old '); Out.String(name); Out.Ln; *)
   IF name # '' THEN
     IF HasDir(name) THEN dir := ''; path := name$
     ELSE pos := 0; ScanPath(pos, dir);
       MakeFileName(dir, name, path); ScanPath(pos, dir)
     END;
-    LOOP
-      error := Platform.OldRW(path, fd); done := error = 0;
+    LOOP Utf8.Encode(path, q);
+      error := Platform.OldRW(q, fd); done := error = 0;
       IF ~done & Platform.TooManyFiles(error) THEN
         Err('too many files open', f, error)
       END;
       IF ~done & Platform.Inaccessible(error) THEN
-        error := Platform.OldRO(path, fd); done := error = 0
+        error := Platform.OldRO(q, fd); done := error = 0
       END;
       IF ~done & ~Platform.Absent(error) THEN
-        Out.String('Warning: Files.Old '); Out.Utf8(name);
-        Out.String(' error = '); Out.Int(error, 0); Out.Ln;
+        Out.String('Warning: Files.Old '); Out.String(name);
+        Out.String(' error = '); Out.Int(error, 0); Out.Ln
       END;
       IF done THEN
-        (* Out.String('  fd = '); Out.Int(fd,1); Out.Ln; *)
         error := Platform.Identify(fd, identity);
         f := CacheEntry(identity);
         IF f # NIL THEN
@@ -428,7 +424,7 @@ BEGIN
   r.buf := buf; r.org := org; r.offset := offset; r.eof := FALSE; r.res := 0
 END Set;
 
-PROCEDURE ReadByte*(VAR r: Rider; VAR x: BYTE);
+PROCEDURE Read*(VAR r: Rider; VAR x: BYTE);
 VAR offset: INTEGER; buf: Buffer;
 BEGIN
   buf := r.buf; offset := r.offset;
@@ -436,13 +432,13 @@ BEGIN
     Set(r, buf.f, r.org + offset); buf := r.buf; offset := r.offset
   END;
   IF (offset < buf.size) THEN
-    x := buf.data[offset]; r.offset := offset + 1
+    x := SYSTEM.VAL(BYTE, buf.data[offset]); r.offset := offset + 1
   ELSIF r.org + offset < buf.f.len THEN
     Set(r, r.buf.f, r.org + offset);
-    x := r.buf.data[0]; r.offset := 1
+    x := SYSTEM.VAL(BYTE, r.buf.data[0]); r.offset := 1
   ELSE x := 0; r.eof := TRUE
   END
-END ReadByte;
+END Read;
 
 PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF BYTE; n: INTEGER);
 VAR xpos, min, restInBuf, offset: INTEGER; buf: Buffer;
@@ -463,15 +459,34 @@ BEGIN
   r.res := 0; r.eof := FALSE
 END ReadBytes;
 
-PROCEDURE ReadChar*(VAR r: Rider; VAR x: SHORTCHAR);
-BEGIN ReadByte(r, SYSTEM.VAL(BYTE, x))
+PROCEDURE ReadShortChar*(VAR r: Rider; VAR x: SHORTCHAR);
+BEGIN Read(r, SYSTEM.VAL(BYTE, x))
+END ReadShortChar;
+
+PROCEDURE ReadChar*(VAR r: Rider; VAR x: CHAR);
+VAR b: BYTE; y: INTEGER;
+BEGIN Read(r, b); y := b;
+  IF y > 80H THEN Read(r, b); b := SYSTEM.VAL(BYTE, b MOD 64); (* Not 1 byte *)
+    IF y DIV 32 = 6 THEN (* 2 bytes *)
+      y := y MOD 32 * 64 + b
+    ELSIF y DIV 16 = 14 THEN (* 3 bytes *)
+      y := (y MOD 16 * 64 + b) * 64;
+      Read(r, b); INC(y, b MOD 64)
+    ELSIF y DIV 8 = 30 THEN (* 4 bytes *)
+      y := (y MOD 8 * 64 + b) * 64;
+      Read(r, b); y := (y + b MOD 64) * 64;
+      Read(r, b); INC(y, b MOD 64) (*!FIXME UTF-16 surrogate pairs *)
+    ELSE y := 0
+    END
+  END;
+  x := CHR(y)
 END ReadChar;
 
 PROCEDURE Base*(VAR r: Rider): File;
 BEGIN RETURN r.buf.f
 END Base;
 
-PROCEDURE WriteByte*(VAR r: Rider; x: BYTE);
+PROCEDURE Write*(VAR r: Rider; x: BYTE);
 VAR buf: Buffer; offset: INTEGER;
 BEGIN
   buf := r.buf; offset := r.offset;
@@ -485,7 +500,7 @@ BEGIN
     INC(buf.size); INC(buf.f.len)
   END;
   r.offset := offset + 1; r.res := 0
-END WriteByte;
+END Write;
 
 PROCEDURE WriteBytes*(VAR r: Rider; IN x: ARRAY OF BYTE; n: INTEGER);
 VAR xpos, min, restInBuf, offset: INTEGER; buf: Buffer;
@@ -507,9 +522,10 @@ BEGIN
   r.res := 0
 END WriteBytes;
 
-PROCEDURE Delete*(IN name: ARRAY OF SHORTCHAR; VAR res: INTEGER);
+PROCEDURE Delete*(IN name: ARRAY OF CHAR; VAR res: INTEGER);
 VAR pos: INTEGER;
-  dir, path: ARRAY 256 OF SHORTCHAR;
+  dir, path: ARRAY 2048 OF CHAR;
+  q: ARRAY 2048 OF SHORTCHAR;
 BEGIN
   IF name # '' THEN
     IF HasDir(name) THEN dir := ''; path := name$
@@ -518,7 +534,8 @@ BEGIN
     END;
     LOOP
       Deregister(path);
-      res := Platform.DeleteFile(path);
+      Utf8.Encode(path, q);
+      res := Platform.DeleteFile(q);
       IF (res = 0) OR (dir = '') THEN RETURN
       ELSE MakeFileName(dir, name, path); ScanPath(pos, dir)
       END
@@ -527,21 +544,21 @@ BEGIN
   END
 END Delete;
 
-PROCEDURE Rename*(IN old, new: ARRAY OF SHORTCHAR; VAR res: INTEGER);
+PROCEDURE Rename*(IN old, new: ARRAY OF CHAR; VAR res: INTEGER);
 VAR n: INTEGER;
   fdold, fdnew: Platform.FileHandle;
   error, ignore: Platform.ErrorCode;
   oldidentity, newidentity: Platform.FileIdentity;
-  buf: ARRAY 4096 OF SHORTCHAR;
+  buf, old2, new2: ARRAY 4096 OF SHORTCHAR;
 BEGIN
-  error := Platform.IdentifyByName(old, oldidentity);
+  Utf8.Encode(old, old2); Utf8.Encode(new, new2);
+  error := Platform.IdentifyByName(old2, oldidentity);
   IF error = 0 THEN
-    error := Platform.IdentifyByName(new, newidentity);
+    error := Platform.IdentifyByName(new2, newidentity);
     IF (error # 0) & ~Platform.SameFile(oldidentity, newidentity) THEN
       Delete(new, error) (* work around stale nfs handles *)
     END;
-    error := Platform.RenameFile(old, new);
-    (* Out.String('Platform.Rename error code '); Out.Int(error,1); Out.Ln; *)
+    error := Platform.RenameFile(old2, new2);
     (* TODO, if we already have a FileDesc for old, it ought to be updated
        with the new workname. *)
     IF ~Platform.DifferentFilesystems(error) THEN
@@ -549,12 +566,12 @@ BEGIN
       RETURN
     ELSE
       (* cross device link, move the file *)
-      error := Platform.OldRO(old, fdold);
+      error := Platform.OldRO(old2, fdold);
       IF error # 0 THEN
         res := 2;
         RETURN
       END;
-      error := Platform.NewFile(new, fdnew);
+      error := Platform.NewFile(new2, fdnew);
       IF error # 0 THEN
         error := Platform.CloseFile(fdold); res := 3;
         RETURN
@@ -571,17 +588,17 @@ BEGIN
       END;
       ignore := Platform.CloseFile(fdold);
       ignore := Platform.CloseFile(fdnew);
-      IF n = 0 THEN error := Platform.DeleteFile(old); res := 0
+      IF n = 0 THEN error := Platform.DeleteFile(old2); res := 0
       ELSE Err('cannot move file', NIL, error)
       END
     END
-  ELSE
-    res := 2 (* old file not found *)
+  ELSE res := 2 (* old file not found *)
   END
 END Rename;
 
 PROCEDURE Register*(f: File);
 VAR errcode: INTEGER;
+  q: ARRAY 2048 OF SHORTCHAR;
 BEGIN
   IF (f.state = create) & (f.registerName # '') THEN f.state := close END;
   Close(f);
@@ -590,8 +607,8 @@ BEGIN
     errcode := Platform.CloseFile(f.fd);
     IF errcode = 0 THEN (* Platform.RenameFile requires a closed file *)
       Rename(f.workName, f.registerName, errcode);
-      IF errcode = 0 THEN
-        errcode := Platform.OldRW(f.registerName, f.fd);
+      IF errcode = 0 THEN Utf8.Encode(f.registerName, q);
+        errcode := Platform.OldRW(q, f.fd);
         IF errcode = 0 THEN f.workName := f.registerName;
           f.registerName := ''; f.tempFile := FALSE;
           RETURN
@@ -602,9 +619,9 @@ BEGIN
   END
 END Register;
 
-PROCEDURE ChangeDirectory*(IN path: ARRAY OF SHORTCHAR; VAR res: INTEGER);
-BEGIN
-  res := Platform.ChDir(path)
+PROCEDURE ChangeDirectory*(IN path: ARRAY OF CHAR; VAR res: INTEGER);
+VAR q: ARRAY 2048 OF SHORTCHAR;
+BEGIN Utf8.Encode(path, q); res := Platform.ChDir(q)
 END ChangeDirectory;
 
 PROCEDURE FlipBytes(VAR src, dest: ARRAY OF BYTE);
@@ -617,7 +634,7 @@ BEGIN
 END FlipBytes;
 
 PROCEDURE ReadBool*(VAR R: Rider; VAR x: BOOLEAN);
-BEGIN ReadByte(R, SYSTEM.VAL(BYTE, x))
+BEGIN Read(R, SYSTEM.VAL(BYTE, x))
 END ReadBool;
 
 PROCEDURE ReadSInt*(VAR R: Rider; VAR x: SHORTINT);
@@ -659,9 +676,15 @@ BEGIN ReadBytes(R, b, 8);
   FlipBytes(b, SYSTEM.THISARR(SYSTEM.ADR(x), 8))
 END ReadLReal;
 
-PROCEDURE ReadString*(VAR R: Rider; VAR x: ARRAY OF SHORTCHAR);
+PROCEDURE ReadShortString*(VAR R: Rider; VAR x: ARRAY OF SHORTCHAR);
 VAR i: INTEGER; ch: SHORTCHAR;
-BEGIN i := 0;
+BEGIN i := 0; (*!FIXME code from scratch*)
+  REPEAT ReadShortChar(R, ch); x[i] := ch; INC(i) UNTIL ch = 0X
+END ReadShortString;
+
+PROCEDURE ReadString*(VAR R: Rider; VAR x: ARRAY OF CHAR);
+VAR i: INTEGER; ch: CHAR;
+BEGIN i := 0; (*!FIXME code from scratch*)
   REPEAT ReadChar(R, ch); x[i] := ch; INC(i) UNTIL ch = 0X
 END ReadString;
 
@@ -669,7 +692,7 @@ PROCEDURE ReadLine*(VAR R: Rider; VAR x: ARRAY OF SHORTCHAR);
 VAR i: INTEGER; ch: SHORTCHAR; b: BOOLEAN;
 BEGIN i := 0; b := FALSE;
   REPEAT
-    ReadChar(R, ch);
+    ReadShortChar(R, ch);
     IF ((ch = 0X) OR (ch = 0AX) OR (ch = 0DX)) THEN b := TRUE
     ELSE x[i] := ch; INC(i)
     END
@@ -677,20 +700,28 @@ BEGIN i := 0; b := FALSE;
 END ReadLine;
 
 PROCEDURE ReadNum*(VAR R: Rider; VAR x: LONGINT);
-VAR s: INTEGER; ch: SHORTCHAR;
-BEGIN s := 0; x := 0; ReadChar(R, ch);
-  WHILE ORD(ch) >= 128 DO
-    INC(x, ASH(LONG(ORD(ch) - 128), s)); INC(s, 7); ReadChar(R, ch)
+VAR n: INTEGER; b: BYTE;
+BEGIN n := 0; x := 0; Read(R, b);
+  WHILE b >= 128 DO
+    INC(x, ASH(LONG(b) - 128, n)); INC(n, 7); Read(R, b)
   END;
-  INC(x, ASH(LONG(ORD(ch) MOD 64 - ORD(ch) DIV 64 * 64), s))
+  INC(x, ASH(LONG(b) MOD 64 - LONG(b) DIV 64 * 64, n))
 END ReadNum;
 
 PROCEDURE WriteBool*(VAR R: Rider; x: BOOLEAN);
-BEGIN WriteByte(R, SYSTEM.VAL(BYTE, x))
+BEGIN Write(R, SYSTEM.VAL(BYTE, x))
 END WriteBool;
 
-PROCEDURE WriteChar*(VAR R: Rider; x: SHORTCHAR);
-BEGIN WriteByte(R, SYSTEM.VAL(BYTE, x))
+PROCEDURE WriteShortChar*(VAR R: Rider; x: SHORTCHAR);
+BEGIN Write(R, SYSTEM.VAL(BYTE, x))
+END WriteShortChar;
+
+PROCEDURE WriteChar*(VAR R: Rider; c: CHAR);
+VAR i, L: INTEGER;
+  q: ARRAY 5 OF SHORTCHAR;
+BEGIN
+  Utf8.EncodeChar(c, q, L); i := 0;
+  WHILE i # L DO Write(R, SYSTEM.VAL(BYTE, q[i])); INC(i) END
 END WriteChar;
 
 PROCEDURE WriteSInt*(VAR R: Rider; x: SHORTINT);
@@ -733,37 +764,53 @@ BEGIN FlipBytes(SYSTEM.THISARR(SYSTEM.ADR(x), 8), b);
   WriteBytes(R, b, 8)
 END WriteLReal;
 
-PROCEDURE WriteString*(VAR R: Rider; IN x: ARRAY OF SHORTCHAR);
+PROCEDURE WriteShortString*(VAR R: Rider; IN s: ARRAY OF SHORTCHAR);
+VAR i: INTEGER;
+BEGIN i := 0; WHILE s[i] # 0X DO INC(i) END;
+  WriteBytes(R, SYSTEM.THISARR(SYSTEM.ADR(s), LEN(s)), i + 1)
+END WriteShortString;
+
+PROCEDURE WriteString*(VAR R: Rider; IN s: ARRAY OF CHAR);
 VAR i: INTEGER;
-BEGIN i := 0; WHILE x[i] # 0X DO INC(i) END;
-  WriteBytes(R, SYSTEM.THISARR(SYSTEM.ADR(x), LEN(x)), i+1)
+BEGIN i := -1;
+  REPEAT INC(i); WriteChar(R, s[i]) UNTIL (i = LEN(s)) OR (s[i] = 0X)
 END WriteString;
 
 PROCEDURE WriteNum*(VAR R: Rider; x: LONGINT);
 BEGIN
   WHILE (x < - 64) OR (x > 63) DO
-    WriteChar(R, SHORT(CHR(SHORT(x) MOD 128 + 128))); x := x DIV 128
+    Write(R, SYSTEM.VAL(BYTE, x MOD 128 + 128)); x := x DIV 128
   END;
-  WriteChar(R, SHORT(CHR(SHORT(x) MOD 128)))
+  Write(R, SYSTEM.VAL(BYTE, x MOD 128))
 END WriteNum;
 
-PROCEDURE GetName*(f: File; VAR name: ARRAY OF SHORTCHAR);
+PROCEDURE GetName*(f: File; VAR name: ARRAY OF CHAR);
 BEGIN name := f.workName$
 END GetName;
 
 PROCEDURE Finalize(o: SYSTEM.PTR);
 VAR f: File; res: INTEGER;
+  q: ARRAY 2048 OF SHORTCHAR;
 BEGIN f := SYSTEM.VAL(File, o);
   IF f.fd # Platform.InvalidHandleValue() THEN
     CloseOSFile(f);
-    IF f.tempFile THEN res := Platform.DeleteFile(f.workName) END
+    IF f.tempFile THEN
+      Utf8.Encode(f.workName, q);
+      res := Platform.DeleteFile(q)
+    END
   END
 END Finalize;
 
 PROCEDURE SetSearchPath*(IN path: ARRAY OF CHAR);
-BEGIN Utf8.Encode(path, SearchPath)
+BEGIN SearchPath := path$
 END SetSearchPath;
 
+PROCEDURE Init;
+VAR q: ARRAY 2048 OF SHORTCHAR;
 BEGIN tempno := -1; Heap.FileCount := 0; SearchPath := '';
-  home := ''; Platform.GetEnv('HOME', home)
+  Platform.GetEnv('HOME', q); Utf8.Decode(q, home);
+END Init;
+
+BEGIN
+  Init
 END Files.

+ 23 - 46
src/FreeOberon.Mod

@@ -57,7 +57,7 @@ TYPE
 
 VAR
   progBuf: ARRAY 16300 OF SHORTCHAR; (* For interacting with a launched program *)
-  inputBuf: ARRAY 16300 OF SHORTCHAR; (* Saves entered chars before Enter pressed *)
+  inputBuf: ARRAY 16300 OF CHAR; (* Saves entered chars before Enter pressed *)
   inputBufLen: INTEGER;
   programFinished: BOOLEAN;
   tempWindowed: BOOLEAN; (* TRUE if editor windowed while program is running *)
@@ -300,26 +300,14 @@ BEGIN
   END
 END PollProgram;
 
-PROCEDURE WriteToProcess(s: ARRAY OF SHORTCHAR; len: INTEGER);
-VAR buf: ARRAY 2048 OF SHORTCHAR; i, bufLen: INTEGER; ch: SHORTCHAR;
-BEGIN
-  bufLen := 0; i := 0;
+PROCEDURE WriteToProcess(s: ARRAY OF CHAR; len: INTEGER);
+VAR buf: ARRAY 2048 OF SHORTCHAR;
+  q: ARRAY 5 OF SHORTCHAR;
+  i, j, L, bufLen: INTEGER;
+BEGIN bufLen := 0; i := 0;
   WHILE i < len DO
-    ch := s[i];
-    IF ch < 80X THEN
-      buf[bufLen] := ch; INC(bufLen)
-    ELSIF ORD(ch) = 240 THEN (* Big cyrillic Yo *)
-      buf[bufLen] := 0D0X; buf[bufLen + 1] := 81X;
-      INC(bufLen, 2)
-    ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
-      buf[bufLen] := 0D0X;
-      buf[bufLen + 1] := SHORT(CHR(ORD(ch) - 128 + 090H));
-      INC(bufLen, 2)
-    ELSE
-      buf[bufLen] := 0D1X;
-      buf[bufLen + 1] := SHORT(CHR(ORD(ch) - 224 + 080H));
-      INC(bufLen, 2)
-    END;
+    Utf8.EncodeChar(s[i], q, L); j := 0;
+    WHILE j # L DO buf[bufLen] := q[j]; INC(bufLen); INC(j) END;
     INC(i)
   END;
   Term.WriteToProcess(buf, bufLen)
@@ -359,8 +347,7 @@ END HandleTerminalKeyDown;
 PROCEDURE HandleTerminalTextInput(s: ARRAY OF CHAR; ch: CHAR);
 BEGIN
   IF (ch # 0X) & (inputBufLen < LEN(inputBuf)) THEN
-    inputBuf[inputBufLen] := SHORT(OV.ToCP866(ch)); INC(inputBufLen);
-    T.Write(OV.ToCP866(ch))
+    inputBuf[inputBufLen] := ch; INC(inputBufLen); T.Write(ch)
   END
 END HandleTerminalTextInput;
 
@@ -393,9 +380,7 @@ RETURN p # NIL END IsSysModule;
 PROCEDURE ModuleExists(IN fname: ARRAY OF CHAR): BOOLEAN;
 VAR F: Files.File;
   exists: BOOLEAN;
-  s: ARRAY 2048 OF SHORTCHAR;
-BEGIN Utf8.Encode(fname, s);
-  F := Files.Old(s); exists := F # NIL;
+BEGIN F := Files.Old(fname); exists := F # NIL;
   IF F # NIL THEN Files.Close(F) END ;
 RETURN exists END ModuleExists;
 
@@ -517,7 +502,6 @@ PROCEDURE Link(IN fname, mod: ARRAY OF CHAR;
     graph: BOOLEAN; list: StrList; VAR exename: ARRAY OF CHAR): BOOLEAN;
 VAR ok: BOOLEAN;
   s: ARRAY 2048 OF CHAR;
-  e2, s2: ARRAY 2048 OF SHORTCHAR;
   res: INTEGER;
 BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list);
   IF ok THEN (* Move executable file if workDir is non-standard *)
@@ -525,10 +509,7 @@ BEGIN ok := RunCommand(fname, mod, TRUE, graph, FALSE, list);
     exename := 'bin/'; Strings.Append(s, exename);
     IF workDir # Editor.stdPath THEN
       Strings.Insert(workDir, 0, s);
-
-      Utf8.Encode(exename, e2);
-      Utf8.Encode(s, s2);
-      Files.Rename(e2, s2, res);
+      Files.Rename(exename, s, res);
       IF res = 0 THEN exename := s$ END
     END
   END ;
@@ -653,25 +634,24 @@ END FileSave;
 
 PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
 VAR last: CHAR;
-  x: SHORTCHAR;
-BEGIN last := ch; Files.ReadChar(R, x); ch := LONG(x);
+BEGIN last := ch; Files.ReadChar(R, ch);
   WHILE ~R.eof & ((last # '*') OR (ch # ')')) DO
     IF (last = '(') & (ch = '*') THEN SkipComment(R, ch, s) END;
-    last := ch; Files.ReadChar(R, x); ch := LONG(x)
+    last := ch; Files.ReadChar(R, ch)
   END;
-  IF ~R.eof THEN Files.ReadChar(R, x); ch := LONG(x) END;
-  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, x); ch := LONG(x) END
+  IF ~R.eof THEN Files.ReadChar(R, ch) END;
+  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, ch) END
 END SkipComment;
 
 PROCEDURE GetSym(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
-VAR i: INTEGER; x: SHORTCHAR;
+VAR i: INTEGER;
 BEGIN
-  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, x); ch := LONG(x) END;
+  WHILE ~R.eof & (ch <= ' ') DO Files.ReadChar(R, ch) END;
   i := 0;
   IF ~R.eof THEN
     IF ch = '(' THEN
-      Files.ReadChar(R, x); ch := LONG(x);
-      IF ch = '*' THEN Files.ReadChar(R, x); ch := LONG(x); SkipComment(R, ch, s)
+      Files.ReadChar(R, ch);
+      IF ch = '*' THEN Files.ReadChar(R, ch); SkipComment(R, ch, s)
       ELSE s[i] := ch; INC(i)
       END
     END;
@@ -680,14 +660,14 @@ BEGIN
             (('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
              ('0' <= ch) & (ch <= '9')) DO
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
-        Files.ReadChar(R, x); ch := LONG(x)
+        Files.ReadChar(R, ch)
       END
     ELSE
       WHILE ~R.eof & (ch > ' ') &
             ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
               ('0' <= ch) & (ch <= '9')) DO
         IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
-        Files.ReadChar(R, x); ch := LONG(x)
+        Files.ReadChar(R, ch)
       END
     END
   END;
@@ -701,14 +681,11 @@ VAR F: Files.File;
   top, p: StrList;
   ch: CHAR;
   mod, s, fname2: ARRAY 256 OF CHAR;
-  q: ARRAY 2048 OF SHORTCHAR;
-  x: SHORTCHAR;
   exit: BOOLEAN;
 BEGIN NEW(top); top.next := NIL; p := top;
-  Utf8.Encode(fname, q);
-  F := Files.Old(q);
+  F := Files.Old(fname);
   IF F # NIL THEN
-    Files.Set(R, F, 0); Files.ReadChar(R, x); ch := LONG(x); GetSym(R, ch, s);
+    Files.Set(R, F, 0); Files.ReadChar(R, ch); GetSym(R, ch, s);
     ok := s = 'MODULE'; GetSym(R, ch, s); GetSym(R, ch, s); (*!FIXME check module name*)
     IF ok THEN
       ok := s = ';'; GetSym(R, ch, s);

+ 20 - 22
src/Graph.Mod

@@ -660,16 +660,31 @@ BEGIN bmp := LoadBitmap(filename);
   END ;
 RETURN font END LoadFont;
 
+PROCEDURE FindFontChar(c: CHAR; OUT n: INTEGER);
+BEGIN
+  IF c <= 0FFX THEN n := ORD(c)
+  ELSIF (410X(*A*) <= c) & (c <= 43FX(*p*)) THEN n := ORD(c) - (410H - 80H)
+  ELSIF (440X(*r*) <= c) & (c <= 44FX(*ja*)) THEN n := ORD(c) - (440H - 0E0H)
+  ELSIF c = 401X(*JO*) THEN n := 0F0H
+  ELSIF c = 451X(*jo*) THEN n := 0F1H
+  ELSE n := -1
+  END
+END FindFontChar;
+
 PROCEDURE DrawCharacter*(dest: Bitmap; font: Font;
   x, y: INTEGER; ch: CHAR; fg: INTEGER);
-VAR fx, fy, r, g, b: INTEGER; dstRect: SDL.Rect;
+VAR n, fx, fy, r, g, b: INTEGER; dstRect: SDL.Rect;
 BEGIN dstRect.x := x; dstRect.y := y;
-  fx := ORD(ch) MOD font.charsInRow;
-  fy := ORD(ch) DIV font.charsInRow;
+  FindFontChar(ch, n);
+  IF n < 0 THEN n := 1 (* "Bad" character *) END;
+  fx := n MOD font.charsInRow;
+  fy := n DIV font.charsInRow;
   ColorToRGB(fg, r, g, b);
   SDL.SetSurfaceColorMod(font.bmp.surface, r, g, b);
-  SDL.BlitSurface(font.bmp.surface, font.sprites[fy, fx],
-    screen.surface, dstRect)
+  IF (fy < LEN(font.sprites)) & (fx < LEN(font.sprites[0])) THEN
+    SDL.BlitSurface(font.bmp.surface, font.sprites[fy, fx],
+      screen.surface, dstRect)
+  END
 END DrawCharacter;
 
 PROCEDURE DrawString*(dest: Bitmap; font: Font;
@@ -718,23 +733,6 @@ VAR sym: INTEGER;
 BEGIN
   IF events.len < LEN(events.buf) THEN
     e := SYSTEM.VAL(SDL.TextInputEvent, SYSTEM.ADR(event));
-    IF e.text[1] = 0X THEN (* ASCII character *)
-      sym := ORD(e.text[0])
-    ELSE (* Unicode character. Assuming 2 bytes *)
-      sym := ORD(e.text[1]);
-      (* UTF-8 cyrillic *)
-      IF (e.text[0] = 0D0X) OR (e.text[0] = 0D1X) THEN
-        IF e.text[0] = 0D0X THEN DEC(sym, 090H)
-        ELSE DEC(sym, 060H - 16)
-        END;
-        (* Convert to CP866 *)
-        IF sym = 65 THEN sym := 0F1H (* jo *)
-        ELSIF sym = -15 THEN sym := 0F0H (* JO *)
-        ELSIF sym < 48 THEN INC(sym, 80H) (* A..JA, a..p *)
-        ELSE INC(sym, 0E0H - 48) (* r..ja *)
-        END
-      END
-    END;
     QueueEvent;
     events.buf[events.last].type := textInput;
     Utf8.Decode(e.text, events.buf[events.last].s);

+ 1 - 2
src/In.Mod

@@ -52,8 +52,7 @@ END Byte;
 
 PROCEDURE ReadChar;
 VAR x, y: INTEGER;
-BEGIN
-  x := GetByte();
+BEGIN x := GetByte();
   IF readstate = ready THEN
     IF cp = utf8 THEN
       IF x > 80H THEN y := GetByte() MOD 64; (* Not 1 byte *)

+ 2 - 19
src/OV.Mod

@@ -796,23 +796,6 @@ BEGIN
   IF ('a' <= ch) & (ch <= 'z') THEN ch := CAP(ch) END ;
 RETURN ch END Cap;
 
-PROCEDURE ToCP866*(ch: CHAR): CHAR;
-VAR n: INTEGER;
-BEGIN n := ORD(ch);
-  IF n >= 80H THEN
-    IF (410H(*A*) <= n) & (n <= 42FH(*YA*)) THEN n := n - 410H + 80H
-    ELSIF (430H(*a*) <= n) & (n <= 44FH(*ya*)) THEN
-      IF n <= 43FH(*p*) THEN n := n - 430H + 0A0H (*a..p*)
-      ELSE n := n - 440H + 0E0H (*r..ya*)
-      END
-    ELSIF n = 401H(*YO*) THEN n := 0F0H
-    ELSIF n = 451H(*yo*) THEN n := 0F1H
-    ELSIF n = 2116H(*No*) THEN n := 0FCH
-    ELSE n := 0
-    END
-  END ;
-RETURN CHR(n) END ToCP866;
-
 PROCEDURE Refresh*(c: Control);
 BEGIN
   IF c.do.refresh # NIL THEN c.do.refresh(c) END
@@ -1769,7 +1752,7 @@ PROCEDURE EditTextInput*(c: Control; ch: CHAR);
 VAR e: Edit;
 BEGIN
   IF ch # 0X THEN e := c(Edit);
-    InsertChar(ToCP866(ch), e.pos, e.caption, e.len);
+    InsertChar(ch, e.pos, e.caption, e.len);
     IF e.pos < e.len THEN INC(e.pos) END;
     NeedRedraw(c.app); T.ResetCursorBlink
   END
@@ -1971,7 +1954,7 @@ PROCEDURE ColumnListTextInput*(c: Control; ch: CHAR);
 VAR C: ColumnList; n: INTEGER;
 BEGIN C := c(ColumnList);
   IF ch # 0X THEN
-    n := FindFirstLetterInList(C.items, Cap(ToCP866(ch)));
+    n := FindFirstLetterInList(C.items, CAP(ch));
     IF n # -1 THEN ColumnListSetCur(C, n) END
   END
 END ColumnListTextInput;

+ 4 - 11
src/Terminal.Mod

@@ -95,20 +95,14 @@ BEGIN
 RETURN color END InvertColor;
 
 PROCEDURE DrawChar*(ch: CHAR; x, y, fg, bg: INTEGER);
-VAR color: INTEGER;
-BEGIN
-  G.RectFill(screen, x, y, x + charW - 1,
-    y + charH - 1, ExpandColor(bg));
-  IF ch # ' ' THEN
-    G.DrawCharacter(screen, font, x, y, ch, ExpandColor(fg))
-  END
+BEGIN G.RectFill(screen, x, y, x + charW - 1, y + charH - 1, ExpandColor(bg));
+  IF ch # ' ' THEN G.DrawCharacter(screen, font, x, y, ch, ExpandColor(fg)) END
 END DrawChar;
 
 PROCEDURE DrawMouse;
 VAR x, y, bg, fg: INTEGER; ch: CHAR;
 BEGIN
-  IF (mouseX >= 0) & (mouseX < charsX) &
-     (mouseY >= 0) & (mouseY < charsY) THEN
+  IF (mouseX >= 0) & (mouseX < charsX) & (mouseY >= 0) & (mouseY < charsY) THEN
     bg := InvertColor(chars[mouseY, mouseX].bg);
     fg := InvertColor(chars[mouseY, mouseX].fg);
     ch := chars[mouseY, mouseX].ch
@@ -122,8 +116,7 @@ END DrawMouse;
 PROCEDURE Draw*(): BOOLEAN;
 VAR x, y, color: INTEGER;
   drawn: BOOLEAN;
-BEGIN
-  drawn := needRedraw;
+BEGIN drawn := needRedraw;
   IF needRedraw THEN
     needRedraw := FALSE;
     (* Chars *)

+ 7 - 2
src/Utf8.Mod

@@ -32,7 +32,7 @@ PROCEDURE EncodeEx*(IN in: ARRAY OF CHAR; inLen: INTEGER;
 VAR i, j, val, lim: INTEGER;
 BEGIN Done := TRUE; i := 0; j := 0; lim := LEN(out) - 1;
   IF inLen < 0 THEN inLen := LEN(in) END;
-  WHILE Done & (i < inLen) & (in[i] # 0X) & (j < lim) DO
+  WHILE Done & (i # inLen) & (in[i] # 0X) & (j < lim) DO
     val := ORD(in[i]); INC(i);
     IF val < 128 THEN
       out[j] := SHORT(CHR(val)); INC(j)
@@ -47,7 +47,7 @@ BEGIN Done := TRUE; i := 0; j := 0; lim := LEN(out) - 1;
     END
   END;
   out[j] := 0X; outLen := j;
-  IF in[i] # 0X THEN Done := FALSE END
+  IF (i # inLen) & (in[i] # 0X) THEN Done := FALSE END
 END EncodeEx;
 
 PROCEDURE Encode*(IN in: ARRAY OF CHAR; OUT out: ARRAY OF SHORTCHAR);
@@ -70,4 +70,9 @@ BEGIN c := ORD(s[0]);
   END ;
 RETURN CHR(c) END DecodeChar;
 
+PROCEDURE EncodeChar*(c: CHAR; OUT s: ARRAY OF SHORTCHAR; OUT len: INTEGER);
+VAR q: ARRAY 1 OF CHAR;
+BEGIN q[0] := c; EncodeEx(q, 1, s, len)
+END EncodeChar;
+
 END Utf8.