1. На самом деле кодировка указывается в переменной LANG (например, ru_RU.UTF-8, ru_RU.KOI8-R) и не обязательно может быть UTF-8. (Если LANG не определена или LANG=C, то подразумевается кодировка ASCII).
Эта кодировка используется для ввода/вывода на консоль, в именах файлов, в переменных окружения и т.д.
Много кто использует кодировку, отличную от UTF-8. В России под Linux/FreeBSD часто используют KOI8-R. В Японии -- SJIS и т.д, в Китае -- GB2312 и т.д. Много кто (в Европе и т.д.) пользуется кодировками ISO8859-*
У меня кодировка всегда была KOI8-R.
2. У нас преобразования ARRAY OF CHAR <-> ARRAY OF SHORTCHAR нужны, как минимум, в HostFiles, в HostConsole, после Libc.getenv.
Кроме того, преобразования кодировок могут понадобиться при написании программ, в которых осуществляется взаимодействие с внешним миром, например, по сети.
3. Для преобразования кодировок можно написать подсистему на КП, а можно воспользоваться существующей библиотекой libiconv. Список поддерживаемых кодировок libiconv можно посмотреть, выполнив "iconv -l". В Linux также работают mbtowcs (используется, кодировка определённая в LANG).
Некоторые кодировки определены здесь:
http://www.unicode.org/Public/MAPPINGS/ (все основные, не CJK (не китайские-японские-корейские)).
Если писать свою подсистему, то она будет менее эффективной, чем libiconv. Но не так существенно. Кое-что есть в подсистеме Unicode, но там много того, что не нужно, и не всё из того, что нужно.
4. Поэтому можно сделать абстрактный модуль EncCodecs:
Код:
MODULE EncCodecs;
TYPE
Encoding* = ARRAY 32 OF CHAR;
Directory* = POINTER TO ABSTRACT RECORD END;
Encoder* = POINTER TO ABSTRACT RECORD END;
Decoder* = POINTER TO ABSTRACT RECORD END;
VAR
dir-: Directory;
(* Directory *)
PROCEDURE (dir: Directory) NewEncoder* (enc: Encoding): Encoder, NEW, ABSTRACT;
PROCEDURE (dir: Directory) NewDecoder* (enc: Encoding): Decoder, NEW, ABSTRACT;
(* Encoder *)
PROCEDURE (e: Encoder) Encode* (IN f: ARRAY OF CHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF SHORTCHAR; VAR tW: INTEGER), NEW, ABSTRACT;
(* у энкодера нет состояния, сброс не нужен *)
(* Decoder *)
PROCEDURE (d: Decoder) Decode* (IN f: ARRAY OF SHORTCHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF CHAR; VAR tW: INTEGER; OUT state: BOOLEAN), NEW, ABSTRACT;
PROCEDURE (d: Decoder) Reset*, NEW, ABSTRACT; (* сброс состояния декодера *)
PROCEDURE SetDir* (d: Directory);
BEGIN
dir := d
END SetDir;
END EncCodecs.
И конкретную реализацию на основе libiconv (для OpenBSD) или mbtowc (для Linux) -- в HostEncCodecs.
А в будущем сделать реализацию на КП: EncStdCodecs:
Код:
MODULE EncStdCodecs;
IMPORT Meta, Codecs := EncCodecs, Aliases := EncStdAliases;
TYPE
Directory* = POINTER TO RECORD (Codecs.Directory) END;
(* Directory *)
PROCEDURE (dir: Directory) NewEncoder* (enc: Codecs.Encoding): Codecs.Encoder;
VAR modName: ARRAY LEN(Codecs.Encoding) + 16 OF CHAR;
item: Meta.Item; ok: BOOLEAN;
item0: RECORD (Meta.Value)
fun: PROCEDURE (): Codecs.Encoder
END;
e: Codecs.Encoder;
BEGIN
e := NIL;
Aliases.GetModName(enc, modName, ok);
IF ok THEN
Meta.Lookup(modName, item);
IF item.obj = Meta.modObj THEN
item.Lookup("NewEncoder", item);
IF item.obj = Meta.procObj THEN
item.GetVal(item0, ok);
IF ok THEN
e := item0.fun()
END
END
END
END;
RETURN e
END NewEncoder;
PROCEDURE (dir: Directory) NewDecoder* (enc: Codecs.Encoding): Codecs.Decoder;
VAR modName: ARRAY LEN(Codecs.Encoding) + 16 OF CHAR;
item: Meta.Item; ok: BOOLEAN;
item0: RECORD (Meta.Value)
fun: PROCEDURE (): Codecs.Decoder
END;
d: Codecs.Decoder;
BEGIN
d := NIL;
Aliases.GetModName(enc, modName, ok);
IF ok THEN
Meta.Lookup(modName, item);
IF item.obj = Meta.modObj THEN
item.Lookup("NewDecoder", item);
IF item.obj = Meta.procObj THEN
item.GetVal(item0, ok);
IF ok THEN
d := item0.fun()
END
END
END
END;
RETURN d
END NewDecoder;
PROCEDURE Init;
VAR dir: Directory;
BEGIN
NEW(dir); Codecs.SetDir(dir)
END Init;
BEGIN
Init
END EncStdCodecs.
Enc/Mod: Codecs, StdAliases, StdCodecs, StdMap_ascii, StdMap_utf_8, StdMap_utf_16be, StdMap_utf_16le и генерируемые автоматичеки из таблиц на сайте unicode.org и csets:
StdMap_iso8859_1, StdMap_iso8859_2, StdMap_iso8859_3, StdMap_iso8859_4, StdMap_iso8859_5, StdMap_iso8859_6, StdMap_iso8859_7, StdMap_iso8859_8, StdMap_iso8859_9, StdMap_iso8859_10, StdMap_iso8859_11, StdMap_iso8859_13, StdMap_iso8859_14, StdMap_iso8859_15, StdMap_iso8859_16,
StdMap_cp037, StdMap_cp1026, StdMap_cp1140.txt
StdMap_cp1250, StdMap_cp1251, StdMap_cp1252, StdMap_cp1253, StdMap_cp1254, StdMap_cp1255, StdMap_cp1256, StdMap_cp1257, StdMap_cp1258, StdMap_cp424, StdMap_cp437, StdMap_cp500, StdMap_cp720, StdMap_cp737, StdMap_cp775, StdMap_cp850, StdMap_cp852, StdMap_cp855, StdMap_cp856, StdMap_cp857, StdMap_cp858, StdMap_cp860, StdMap_cp861, StdMap_cp862, StdMap_cp863, StdMap_cp864, StdMap_cp865, StdMap_cp866, StdMap_cp869, StdMap_cp874, StdMap_cp936, StdMap_cp949,
StdMap_mac_centraleurope, StdMap_mac_cyrillic, StdMap_mac_greek, StdMap_mac_iceland, StdMap_viscii, StdMap_mac_roman, StdMap_mac_turkish,
StdMap_koi8_r, StdMap_koi8_u,
StdMap_georgian_academy, StdMap_georgian_ps,
StdMap_atarist, StdMap_hp_roman8,
StdMap_kps9566, StdMap_kz1048, StdMap_ptcp154
( Не указаны модули CJK (в том числе CP932, CP950) )
Модуль StdAliases также должен генерироваться автоматически.
Модули StdMap_ascii, StdMap_utf_* -- реализовывать вручную.
Пример автоматически сгенерированного модуля:
Код:
MODULE EncStdMap_cp1251;
(* This file was generated automatically *)
(* Source: http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT *)
IMPORT Codecs := EncCodecs;
TYPE
Encoder = POINTER TO RECORD (Codecs.Encoder) END;
Decoder = POINTER TO RECORD (Codecs.Decoder) END;
(* Encoder *)
PROCEDURE (e: Encoder) Encode (IN f: ARRAY OF CHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF SHORTCHAR; VAR tW: INTEGER);
VAR x, y: INTEGER;
BEGIN
WHILE fLen > 0 DO
x := ORD(f[fR]);
CASE x OF
| 00H..7FH: y := x
| 0A0H: y := 0A0H
| 0A4H: y := 0A4H
| 0A6H..0A7H: y := x
| 0A9H: y := 0A9H
| 0ABH..0AEH: y := x
| 0B0H..0B1H: y := x
| 0B5H..0B7H: y := x
| 0BBH: y := 0BBH
| 0401H: y := 0A8H
| 0402H..0403H: y := x - 0382H
| 0404H: y := 0AAH
| 0405H: y := 0BDH
| 0406H: y := 0B2H
| 0407H: y := 0AFH
| 0408H: y := 0A3H
| 0409H: y := 8AH
| 040AH: y := 8CH
| 040BH: y := 8EH
| 040CH: y := 8DH
| 040EH: y := 0A1H
| 040FH: y := 8FH
| 0410H..044FH: y := x - 0350H
| 0451H: y := 0B8H
| 0452H: y := 90H
| 0453H: y := 83H
| 0454H: y := 0BAH
| 0455H: y := 0BEH
| 0456H: y := 0B3H
| 0457H: y := 0BFH
| 0458H: y := 0BCH
| 0459H: y := 9AH
| 045AH: y := 9CH
| 045BH: y := 9EH
| 045CH: y := 9DH
| 045EH: y := 0A2H
| 045FH: y := 9FH
| 0490H: y := 0A5H
| 0491H: y := 0B4H
| 2013H..2014H: y := x - 1F7DH
| 2018H..2019H: y := x - 1F87H
| 201AH: y := 82H
| 201CH..201DH: y := x - 1F89H
| 201EH: y := 84H
| 2020H..2021H: y := x - 1F9AH
| 2022H: y := 95H
| 2026H: y := 85H
| 2030H: y := 89H
| 2039H: y := 8BH
| 203AH: y := 9BH
| 20ACH: y := 88H
| 2116H: y := 0B9H
| 2122H: y := 99H
ELSE
RETURN
END;
t[tW] := SHORT(CHR(y)); INC(tW);
INC(fR);
DEC(fLen)
END
END Encode;
PROCEDURE NewEncoder* (): Codecs.Encoder;
VAR e: Encoder;
BEGIN
NEW(e); RETURN e
END NewEncoder;
(* Decoder *)
PROCEDURE (d: Decoder) Decode (IN f: ARRAY OF SHORTCHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF CHAR; VAR tW: INTEGER; OUT state: BOOLEAN);
BEGIN
HALT(126) (* not implemented *)
END Decode;
PROCEDURE (d: Decoder) Reset;
BEGIN
HALT(126) (* not implemented *)
END Reset;
PROCEDURE NewDecoder* (): Codecs.Decoder;
VAR d: Decoder;
BEGIN
NEW(d); RETURN d
END NewDecoder;
END EncStdMap_cp1251.
Черновик StdMap_utf_8:
Код:
MODULE EncMap_utf_8;
IMPORT Codecs := EncCodecs;
TYPE
Encoder = POINTER TO RECORD (Codecs.Encoder) END;
Decoder = POINTER TO RECORD (Codecs.Decoder) END;
(* Encoder *)
PROCEDURE (e: Encoder) Encode (IN f: ARRAY OF CHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF SHORTCHAR; VAR tW: INTEGER);
VAR x: INTEGER;
BEGIN
WHILE fLen > 0 DO
x := ORD(f[fR]);
(* http://www.lemoda.net/c/ucs2-to-utf8/index.html *)
CASE x OF 00H..7FH:
t[tW] := SHORT(CHR(x)); INC(tW)
| 80H..7FFH:
t[tW] := SHORT(CHR(x DIV 64 + 192));
t[tW+1] := SHORT(CHR(x MOD 64 + 128));
INC(tW, 2)
| 800H..0FFFFH:
t[tW] := SHORT(CHR(x DIV 4096 + 224));
t[tW+1] := SHORT(CHR((x DIV 64) MOD 64 + 128));
t[tW+2] := SHORT(CHR(x MOD 64 + 128));
INC(tW, 3)
ELSE
RETURN
END;
INC(fR);
DEC(fLen)
END
END Encode;
PROCEDURE NewEncoder* (): Codecs.Encoder;
VAR e: Encoder;
BEGIN
NEW(e); RETURN e
END NewEncoder;
(* Decoder *)
PROCEDURE (d: Decoder) Decode (IN f: ARRAY OF SHORTCHAR; VAR fR, fLen: INTEGER; VAR t: ARRAY OF CHAR; VAR tW: INTEGER; OUT state: BOOLEAN);
BEGIN
HALT(126) (* not implemented *)
END Decode;
PROCEDURE (d: Decoder) Reset;
BEGIN
HALT(126) (* not implemented *)
END Reset;
PROCEDURE NewDecoder* (): Codecs.Decoder;
VAR d: Decoder;
BEGIN
NEW(d); RETURN d
END NewDecoder;
END EncMap_utf_8.