cwstring.pp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2005 by Florian Klaempfl,
  4. member of the Free Pascal development team.
  5. libc based wide string support
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. {$modeswitch unicodestrings-}
  14. {$h-}
  15. {$inline on}
  16. {$implicitexceptions off}
  17. {$IFNDEF FPC_DOTTEDUNITS}
  18. unit cwstring;
  19. {$ENDIF FPC_DOTTEDUNITS}
  20. interface
  21. procedure SetCWidestringManager;
  22. implementation
  23. {$linklib c}
  24. // Linux (and maybe glibc platforms in general), have iconv in glibc.
  25. {$if defined(linux) or defined(solaris)}
  26. {$define iconv_is_in_libc}
  27. {$endif}
  28. {$ifdef netbsd}
  29. {$ifndef DISABLE_ICONV_LIBC}
  30. {$define iconv_is_in_libc}
  31. {$endif}
  32. {$endif}
  33. {$ifdef dragonfly}
  34. { iconv function are included inside libc for DragonFly version 6.3 }
  35. { See https://man.dragonflybsd.org/?command=iconv&section=3 }
  36. {$ifndef DISABLE_ICONV_LIBC}
  37. {$define iconv_is_in_libc}
  38. {$endif}
  39. {$endif}
  40. { Modern FreeBSD releases also claim to have iconv
  41. function inside libc, at least
  42. this is stated explicitly in iconv(3) man
  43. of FreeBSD from 10.0 release
  44. PM 2023-05-05 }
  45. {$ifdef freebsd}
  46. {$ifndef DISABLE_ICONV_LIBC}
  47. {$define iconv_is_in_libc}
  48. {$endif}
  49. {$endif}
  50. {$ifndef iconv_is_in_libc}
  51. {$if defined(haiku)}
  52. {$linklib textencoding}
  53. {$else}
  54. {$linklib iconv}
  55. {$endif}
  56. {$define useiconv}
  57. {$endif not iconv_is_in_libc}
  58. {$i rtldefs.inc}
  59. {$IFDEF FPC_DOTTEDUNITS}
  60. Uses
  61. UnixApi.Base,
  62. System.CTypes,
  63. UnixApi.Unix,
  64. UnixApi.Types,
  65. System.InitC,
  66. System.DynLibs,
  67. UnixApi.CP;
  68. {$ELSE FPC_DOTTEDUNITS}
  69. Uses
  70. BaseUnix,
  71. ctypes,
  72. unix,
  73. unixtype,
  74. initc,
  75. dynlibs,
  76. unixcp;
  77. {$ENDIF FPC_DOTTEDUNITS}
  78. Const
  79. {$ifndef useiconv}
  80. libiconvname='c'; // is in libc under Linux.
  81. libprefix='lib';
  82. {$else}
  83. {$ifdef haiku}
  84. libiconvname='textencoding'; // is in libtextencoding under Haiku
  85. libprefix='lib';
  86. {$else}
  87. {$if defined(darwin) or defined(aix)}
  88. libiconvname='libiconv';
  89. libprefix='';
  90. {$else}
  91. libiconvname='iconv';
  92. libprefix='lib';
  93. {$endif}
  94. {$endif}
  95. {$endif}
  96. { helper functions from libc }
  97. function towlower(__wc:wint_t):wint_t;cdecl;external clib name 'towlower';
  98. function towupper(__wc:wint_t):wint_t;cdecl;external clib name 'towupper';
  99. function wcscoll (__s1:pwchar_t; __s2:pwchar_t):cint;cdecl;external clib name 'wcscoll';
  100. function strcoll (__s1:PAnsiChar; __s2:PAnsiChar):cint;cdecl;external clib name 'strcoll';
  101. {$ifdef netbsd}
  102. { NetBSD has a new setlocale function defined in /usr/include/locale.h
  103. that should be used }
  104. function setlocale(category: cint; locale: PAnsiChar): PAnsiChar; cdecl; external clib name '__setlocale50';
  105. {$else}
  106. function setlocale(category: cint; locale: PAnsiChar): PAnsiChar; cdecl; external clib name 'setlocale';
  107. {$endif}
  108. {$if not(defined(beos) and not defined(haiku))}
  109. function mbrtowc(pwc: pwchar_t; const s: PAnsiChar; n: size_t; ps: pmbstate_t): size_t; cdecl; external clib name 'mbrtowc';
  110. function wcrtomb(s: PAnsiChar; wc: wchar_t; ps: pmbstate_t): size_t; cdecl; external clib name 'wcrtomb';
  111. function mbrlen(const s: PAnsiChar; n: size_t; ps: pmbstate_t): size_t; cdecl; external clib name 'mbrlen';
  112. {$else beos}
  113. function mbtowc(pwc: pwchar_t; const s: PAnsiChar; n: size_t): size_t; cdecl; external clib name 'mbtowc';
  114. function wctomb(s: PAnsiChar; wc: wchar_t): size_t; cdecl; external clib name 'wctomb';
  115. function mblen(const s: PAnsiChar; n: size_t): size_t; cdecl; external clib name 'mblen';
  116. {$endif beos}
  117. const
  118. {$if defined(linux)}
  119. __LC_CTYPE = 0;
  120. LC_ALL = 6;
  121. _NL_CTYPE_CLASS = (__LC_CTYPE shl 16);
  122. _NL_CTYPE_CODESET_NAME = (_NL_CTYPE_CLASS)+14;
  123. CODESET = _NL_CTYPE_CODESET_NAME;
  124. {$elseif defined(darwin)}
  125. CODESET = 0;
  126. LC_ALL = 0;
  127. {$elseif defined(FreeBSD)} // actually FreeBSD5. internationalisation is afaik not default on 4.
  128. __LC_CTYPE = 0;
  129. LC_ALL = 0;
  130. _NL_CTYPE_CLASS = (__LC_CTYPE shl 16);
  131. _NL_CTYPE_CODESET_NAME = (_NL_CTYPE_CLASS)+14;
  132. CODESET = 0; // _NL_CTYPE_CODESET_NAME;
  133. {$elseif defined(solaris)}
  134. {$define ACCEPT_646}
  135. CODESET=49;
  136. LC_ALL = 6;
  137. {$elseif defined(beos)}
  138. {$ifdef haiku}
  139. CODESET= 0; // Checked for Haiku
  140. LC_ALL = 0; // Checked for Haiku
  141. {$else}
  142. {$warning check correct value for BeOS}
  143. CODESET=49;
  144. LC_ALL = 6; // Checked for BeOS
  145. {$endif}
  146. ESysEILSEQ = EILSEQ;
  147. {$elseif defined(OpenBSD)}
  148. {$define ACCEPT_646}
  149. CODESET = 51;
  150. LC_ALL = 0;
  151. {$elseif defined(NetBSD)}
  152. {$define ACCEPT_646}
  153. CODESET = 51;
  154. LC_ALL = 0;
  155. {$elseif defined(aix)}
  156. CODESET = 49;
  157. LC_ALL = -1;
  158. {$elseif defined(dragonfly)}
  159. CODESET = 0;
  160. LC_ALL = 0;
  161. __LC_CTYPE = 0;
  162. _NL_CTYPE_CLASS = (__LC_CTYPE shl 16);
  163. _NL_CTYPE_CODESET_NAME = (_NL_CTYPE_CLASS)+14;
  164. {$else not aix}
  165. {$error lookup the value of CODESET in /usr/include/langinfo.h, and the value of LC_ALL in /usr/include/locale.h for your OS }
  166. // and while doing it, check if iconv is in libc, and if the symbols are prefixed with iconv_ or libiconv_
  167. {$endif}
  168. { unicode encoding name }
  169. {$ifdef FPC_LITTLE_ENDIAN}
  170. unicode_encoding2 = 'UTF-16LE';
  171. unicode_encoding4 = 'UCS-4LE';
  172. {$else FPC_LITTLE_ENDIAN}
  173. {$ifdef AIX}
  174. unicode_encoding2 = 'UTF-16';
  175. {$else AIX}
  176. unicode_encoding2 = 'UTF-16BE';
  177. unicode_encoding4 = 'UCS-4BE';
  178. {$endif AIX}
  179. {$endif FPC_LITTLE_ENDIAN}
  180. { en_US.UTF-8 needs maximally 6 chars, UCS-4/UTF-32 needs 4 }
  181. { -> 10 should be enough? Should actually use MB_CUR_MAX, but }
  182. { that's a libc macro mapped to internal functions/variables }
  183. { and thus not a stable external API on systems where libc }
  184. { breaks backwards compatibility every now and then }
  185. MB_CUR_MAX = 10;
  186. { Requests for iconvctl }
  187. ICONV_TRIVIALP = 0; // int *argument
  188. ICONV_GET_TRANSLITERATE = 1; // int *argument
  189. ICONV_SET_TRANSLITERATE = 2; // const int *argument
  190. ICONV_GET_DISCARD_ILSEQ = 3; // int *argument
  191. ICONV_SET_DISCARD_ILSEQ = 4; // const int *argument
  192. ICONV_SET_HOOKS = 5; // const struct iconv_hooks *argument
  193. ICONV_SET_FALLBACKS = 6; // const struct iconv_fallbacks *argument
  194. type
  195. piconv_t = ^iconv_t;
  196. iconv_t = pointer;
  197. nl_item = cint;
  198. {$ifdef haiku}
  199. function nl_langinfo(__item:nl_item):PAnsiChar;cdecl;external 'root' name 'nl_langinfo';
  200. {$else}
  201. {$ifndef beos}
  202. function nl_langinfo(__item:nl_item):PAnsiChar;cdecl;external libiconvname name 'nl_langinfo';
  203. {$endif}
  204. {$endif}
  205. {$if (not defined(bsd) and not defined(beos)) or defined(iconv_is_in_libc) or (defined(darwin) and not defined(cpupowerpc32))}
  206. function iconv_open(__tocode:PAnsiChar; __fromcode:PAnsiChar):iconv_t;cdecl;external libiconvname name 'iconv_open';
  207. function iconv(__cd:iconv_t; __inbuf:PPAnsiChar; __inbytesleft:psize_t; __outbuf:PPAnsiChar; __outbytesleft:psize_t):size_t;cdecl;external libiconvname name 'iconv';
  208. function iconv_close(__cd:iconv_t):cint;cdecl;external libiconvname name 'iconv_close';
  209. const
  210. iconvctlname='iconvctl';
  211. {$else}
  212. function iconv_open(__tocode:PAnsiChar; __fromcode:PAnsiChar):iconv_t;cdecl;external libiconvname name 'libiconv_open';
  213. function iconv(__cd:iconv_t; __inbuf:PPAnsiChar; __inbytesleft:psize_t; __outbuf:PPAnsiChar; __outbytesleft:psize_t):size_t;cdecl;external libiconvname name 'libiconv';
  214. function iconv_close(__cd:iconv_t):cint;cdecl;external libiconvname name 'libiconv_close';
  215. const
  216. iconvctlname='libiconvctl';
  217. {$endif}
  218. var
  219. iconvctl:function(__cd:iconv_t; __request:cint; __argument:pointer):cint;cdecl;
  220. procedure fpc_rangeerror; [external name 'FPC_RANGEERROR'];
  221. Const
  222. UTF8Name : ShortString = 'UTF-8';
  223. TransLitname : ShortString ='//TRANSLIT';
  224. threadvar
  225. iconv_ansi2wide,
  226. iconv_wide2ansi : iconv_t;
  227. { since we cache the iconv_t converters, we have to do the same
  228. for the DefaultSystemCodePage variable since if it changes, we
  229. have to re-initialize the converters too. We can't do that via
  230. a callback in the widestring manager because DefaultSystemCodePage
  231. is not a threadvar and we can't automatically change this in all
  232. threads }
  233. current_DefaultSystemCodePage: TSystemCodePage;
  234. procedure InitThread;
  235. var
  236. transliterate: cint;
  237. {$if not(defined(darwin) and (defined(cpuarm) or defined(cpuaarch64))) and not defined(iphonesim)}
  238. iconvindex: longint;
  239. {$endif}
  240. iconvname, toencoding: shortstring;
  241. begin
  242. current_DefaultSystemCodePage:=DefaultSystemCodePage;
  243. {$if declared(iconvindex)}
  244. iconvindex:=GetCodepageData(DefaultSystemCodePage);
  245. if iconvindex<>-1 then
  246. iconvname:=UnixCpMap[iconvindex].name
  247. else
  248. { default to UTF-8 on Unix platforms }
  249. iconvname:=UTF8Name;
  250. {$else}
  251. { Unix locale settings are ignored on iPhoneOS/iPhoneSimulator }
  252. iconvname:='UTF-8';
  253. {$endif}
  254. toencoding:=iconvname;
  255. if not assigned(iconvctl) then
  256. toencoding:=toencoding+TransLitName;
  257. iconv_wide2ansi:=iconv_open(PAnsiChar(@toencoding[1]),unicode_encoding2);
  258. iconv_ansi2wide:=iconv_open(unicode_encoding2,PAnsiChar(@iconvname[1]));
  259. if assigned(iconvctl) and
  260. (iconv_wide2ansi<>iconv_t(-1)) then
  261. begin
  262. transliterate:=1;
  263. iconvctl(iconv_wide2ansi,ICONV_SET_TRANSLITERATE,@transliterate);
  264. end;
  265. end;
  266. procedure FiniThread;
  267. begin
  268. if (iconv_wide2ansi <> iconv_t(-1)) then
  269. iconv_close(iconv_wide2ansi);
  270. if (iconv_ansi2wide <> iconv_t(-1)) then
  271. iconv_close(iconv_ansi2wide);
  272. end;
  273. {$if defined(beos) and not defined(haiku)}
  274. function nl_langinfo(__item:nl_item):PAnsiChar;
  275. begin
  276. {$warning TODO BeOS nl_langinfo or more uptodate port of iconv...}
  277. // Now implement the minimum required to correctly initialize WideString support
  278. case __item of
  279. CODESET : Result := 'UTF-8'; // BeOS use UTF-8
  280. else
  281. begin
  282. Assert(False, 'nl_langinfo was called with an unknown nl_item value');
  283. Result := '';
  284. end;
  285. end;
  286. end;
  287. {$endif}
  288. function open_iconv_for_cps(cp: TSystemCodePage; const otherencoding: PAnsiChar; cp_is_from: boolean): iconv_t;
  289. var
  290. iconvindex: longint;
  291. toencoding: rawbytestring;
  292. transliterate: cint;
  293. begin
  294. { TODO: add caching (then we also don't need separate code for
  295. the default system page and other ones)
  296. -- typecasting an ansistring function result to PAnsiChar is
  297. unsafe normally, but these are constant strings -> no
  298. problem }
  299. open_iconv_for_cps:=iconv_t(-1);
  300. iconvindex:=GetCodepageData(cp);
  301. if iconvindex=-1 then
  302. exit;
  303. repeat
  304. if cp_is_from then
  305. open_iconv_for_cps:=iconv_open(otherencoding,PAnsiChar(UnixCpMap[iconvindex].name))
  306. else
  307. begin
  308. toencoding:=UnixCpMap[iconvindex].name;
  309. if not assigned(iconvctl) then
  310. toencoding:=toencoding+'//TRANSLIT';
  311. open_iconv_for_cps:=iconv_open(PAnsiChar(toencoding),otherencoding);
  312. end;
  313. inc(iconvindex);
  314. until (open_iconv_for_cps<>iconv_t(-1)) or
  315. (iconvindex>high(UnixCpMap)) or
  316. (UnixCpMap[iconvindex].cp<>cp);
  317. if not cp_is_from and
  318. (open_iconv_for_cps<>iconv_t(-1)) and
  319. assigned(iconvctl) then
  320. begin
  321. transliterate:=1;
  322. iconvctl(open_iconv_for_cps,ICONV_SET_TRANSLITERATE,@transliterate);
  323. end;
  324. end;
  325. {$ifdef aix}
  326. {$i cwstraix.inc}
  327. {$endif aix}
  328. procedure Wide2AnsiMove(source:pwidechar; var dest:RawByteString; cp:TSystemCodePage; len:SizeInt);
  329. var
  330. outlength,
  331. outoffset,
  332. srclen,
  333. outleft : size_t;
  334. use_iconv: iconv_t;
  335. srcpos : pwidechar;
  336. destpos: PAnsiChar;
  337. mynil : PAnsiChar;
  338. my0 : size_t;
  339. err : longint;
  340. transliterate: cint;
  341. free_iconv: boolean;
  342. {$ifdef aix}
  343. intermediate: rawbytestring;
  344. {$endif aix}
  345. begin
  346. {$ifdef aix}
  347. { AIX libiconv does not support converting cp866 to anything else except
  348. for iso-8859-5 -> always first convert to iso-8859-5, then to UTF-16 }
  349. if cp=866 then
  350. begin
  351. Wide2AnsiMove(source,intermediate,28595,len);
  352. if handle_aix_intermediate(PAnsiChar(intermediate),28595,cp,dest,len) then
  353. exit;
  354. end;
  355. {$endif aix}
  356. if (cp=DefaultSystemCodePage) then
  357. begin
  358. { update iconv converter in case the DefaultSystemCodePage has been
  359. changed }
  360. if current_DefaultSystemCodePage<>DefaultSystemCodePage then
  361. begin
  362. FiniThread;
  363. InitThread;
  364. end;
  365. use_iconv:=iconv_wide2ansi;
  366. free_iconv:=false;
  367. end
  368. else
  369. begin
  370. use_iconv:=open_iconv_for_cps(cp,unicode_encoding2,false);
  371. if (use_iconv<>iconv_t(-1)) and
  372. assigned(iconvctl) then
  373. begin
  374. transliterate:=1;
  375. iconvctl(use_iconv,ICONV_SET_TRANSLITERATE,@transliterate);
  376. end;
  377. free_iconv:=true;
  378. end;
  379. { unsupported encoding -> default move }
  380. if use_iconv=iconv_t(-1) then
  381. begin
  382. DefaultUnicode2AnsiMove(source,dest,DefaultSystemCodePage,len);
  383. exit;
  384. end;
  385. mynil:=nil;
  386. my0:=0;
  387. { rought estimation }
  388. setlength(dest,len*3);
  389. outlength:=len*3;
  390. srclen:=len*2;
  391. srcpos:=source;
  392. destpos:=PAnsiChar(dest);
  393. outleft:=outlength;
  394. while iconv(use_iconv,PPAnsiChar(@srcpos),@srclen,@destpos,@outleft)=size_t(-1) do
  395. begin
  396. err:=fpgetCerrno;
  397. case err of
  398. { last character is incomplete sequence }
  399. ESysEINVAL,
  400. { incomplete sequence in the middle }
  401. ESysEILSEQ:
  402. begin
  403. { skip and set to '?' }
  404. inc(srcpos);
  405. dec(srclen,2);
  406. destpos^:='?';
  407. inc(destpos);
  408. dec(outleft);
  409. { reset }
  410. iconv(use_iconv,@mynil,@my0,@mynil,@my0);
  411. if err=ESysEINVAL then
  412. break;
  413. end;
  414. ESysE2BIG:
  415. begin
  416. outoffset:=destpos-PAnsiChar(dest);
  417. { extend }
  418. setlength(dest,outlength+len*3);
  419. inc(outleft,len*3);
  420. inc(outlength,len*3);
  421. { string could have been moved }
  422. destpos:=PAnsiChar(dest)+outoffset;
  423. end;
  424. else
  425. runerror(231);
  426. end;
  427. end;
  428. // truncate string
  429. setlength(dest,length(dest)-outleft);
  430. SetCodePage(dest,cp,false);
  431. if free_iconv then
  432. iconv_close(use_iconv);
  433. end;
  434. procedure Ansi2WideMove(source:PAnsiChar; cp:TSystemCodePage; var dest:widestring; len:SizeInt);
  435. var
  436. outlength,
  437. outoffset,
  438. outleft : size_t;
  439. use_iconv: iconv_t;
  440. srcpos,
  441. destpos: PAnsiChar;
  442. mynil : PAnsiChar;
  443. my0 : size_t;
  444. err: cint;
  445. iconvindex: longint;
  446. free_iconv: boolean;
  447. {$ifdef aix}
  448. intermediate: rawbytestring;
  449. {$endif aix}
  450. begin
  451. {$ifdef aix}
  452. { AIX libiconv does not support converting cp866 to anything else except
  453. for iso-8859-5 -> always first convert to iso-8859-5, then to UTF-16 }
  454. if cp=866 then
  455. begin
  456. if handle_aix_intermediate(source,cp,cp,intermediate,len) then
  457. source:=PAnsiChar(intermediate);
  458. end;
  459. {$endif aix}
  460. if (cp=DefaultSystemCodePage) then
  461. begin
  462. { update iconv converter in case the DefaultSystemCodePage has been
  463. changed }
  464. if current_DefaultSystemCodePage<>DefaultSystemCodePage then
  465. begin
  466. FiniThread;
  467. InitThread;
  468. end;
  469. use_iconv:=iconv_ansi2wide;
  470. free_iconv:=false;
  471. // Using Unicode RTL, we can end up here when iconv_ansi2wide is not yet initialized
  472. if (use_iconv=Nil) then
  473. begin
  474. DefaultAnsi2UnicodeMove(source,DefaultSystemCodePage,dest,len);
  475. exit;
  476. end;
  477. end
  478. else
  479. begin
  480. { TODO: add caching (then we also don't need separate code for
  481. the default system page and other ones)
  482. -- typecasting an ansistring function result to PAnsiChar is
  483. unsafe normally, but these are constant strings -> no
  484. problem }
  485. use_iconv:=open_iconv_for_cps(cp,unicode_encoding2,true);
  486. free_iconv:=true;
  487. end;
  488. { unsupported encoding -> default move }
  489. if use_iconv=iconv_t(-1) then
  490. begin
  491. DefaultAnsi2UnicodeMove(source,DefaultSystemCodePage,dest,len);
  492. exit;
  493. end;
  494. mynil:=nil;
  495. my0:=0;
  496. // extra space
  497. outlength:=len+1;
  498. setlength(dest,outlength);
  499. srcpos:=source;
  500. destpos:=PAnsiChar(dest);
  501. outleft:=outlength*2;
  502. while iconv(use_iconv,@srcpos,psize(@len),@destpos,@outleft)=size_t(-1) do
  503. begin
  504. err:=fpgetCerrno;
  505. case err of
  506. ESysEINVAL,
  507. ESysEILSEQ:
  508. begin
  509. { skip and set to '?' }
  510. inc(srcpos);
  511. dec(len);
  512. pwidechar(destpos)^:='?';
  513. inc(destpos,2);
  514. dec(outleft,2);
  515. { reset }
  516. iconv(use_iconv,@mynil,@my0,@mynil,@my0);
  517. if err=ESysEINVAL then
  518. break;
  519. end;
  520. ESysE2BIG:
  521. begin
  522. outoffset:=destpos-PAnsiChar(dest);
  523. { extend }
  524. setlength(dest,outlength+len);
  525. inc(outleft,len*2);
  526. inc(outlength,len);
  527. { string could have been moved }
  528. destpos:=PAnsiChar(dest)+outoffset;
  529. end;
  530. else
  531. runerror(231);
  532. end;
  533. end;
  534. // truncate string
  535. setlength(dest,length(dest)-outleft div 2);
  536. if free_iconv then
  537. iconv_close(use_iconv);
  538. end;
  539. function LowerWideString(const s : WideString) : WideString;
  540. var
  541. i : SizeInt;
  542. begin
  543. SetLength(result,length(s));
  544. for i:=0 to length(s)-1 do
  545. pwidechar(result)[i]:=WideChar(towlower(wint_t(s[i+1])));
  546. end;
  547. function UpperWideString(const s : WideString) : WideString;
  548. var
  549. i : SizeInt;
  550. begin
  551. SetLength(result,length(s));
  552. for i:=0 to length(s)-1 do
  553. pwidechar(result)[i]:=WideChar(towupper(wint_t(s[i+1])));
  554. end;
  555. procedure EnsureAnsiLen(var S: AnsiString; const len: SizeInt); inline;
  556. begin
  557. if (len>length(s)) then
  558. if (length(s) < 10*256) then
  559. setlength(s,length(s)+10)
  560. else
  561. setlength(s,length(s)+length(s) shr 8);
  562. end;
  563. procedure ConcatCharToAnsiStr(const c: AnsiChar; var S: AnsiString; var index: SizeInt);
  564. begin
  565. EnsureAnsiLen(s,index);
  566. PAnsiChar(@s[index])^:=c;
  567. inc(index);
  568. end;
  569. { concatenates an utf-32 Char to a ansistring. S *must* be unique when entering. }
  570. {$if not(defined(beos) and not defined(haiku))}
  571. procedure ConcatUTF32ToAnsiStr(const nc: wint_t; var S: AnsiString; var index: SizeInt; var mbstate: mbstate_t);
  572. {$else not beos}
  573. procedure ConcatUTF32ToAnsiStr(const nc: wint_t; var S: AnsiString; var index: SizeInt);
  574. {$endif beos}
  575. var
  576. p : PAnsiChar;
  577. mblen : size_t;
  578. begin
  579. { we know that s is unique -> avoid uniquestring calls}
  580. p:=@s[index];
  581. if (nc<=127) then
  582. ConcatCharToAnsiStr(AnsiChar(nc),s,index)
  583. else
  584. begin
  585. EnsureAnsiLen(s,index+MB_CUR_MAX);
  586. {$if not(defined(beos) and not defined(haiku))}
  587. mblen:=wcrtomb(p,wchar_t(nc),@mbstate);
  588. {$else not beos}
  589. mblen:=wctomb(p,wchar_t(nc));
  590. {$endif not beos}
  591. if (mblen<>size_t(-1)) then
  592. inc(index,mblen)
  593. else
  594. begin
  595. { invalid wide AnsiChar }
  596. p^:='?';
  597. inc(index);
  598. end;
  599. end;
  600. end;
  601. function LowerAnsiString(const s : AnsiString) : AnsiString;
  602. var
  603. i, slen,
  604. resindex : SizeInt;
  605. mblen : size_t;
  606. {$if not(defined(beos) and not defined(haiku))}
  607. ombstate,
  608. nmbstate : mbstate_t;
  609. {$endif beos}
  610. wc : wchar_t;
  611. begin
  612. {$if not(defined(beos) and not defined(haiku))}
  613. fillchar(ombstate,sizeof(ombstate),0);
  614. fillchar(nmbstate,sizeof(nmbstate),0);
  615. {$endif beos}
  616. slen:=length(s);
  617. SetLength(result,slen+10);
  618. i:=1;
  619. resindex:=1;
  620. while (i<=slen) do
  621. begin
  622. if (s[i]<=#127) then
  623. begin
  624. wc:=wchar_t(s[i]);
  625. mblen:= 1;
  626. end
  627. else
  628. {$if not(defined(beos) and not defined(haiku))}
  629. mblen:=mbrtowc(@wc, PAnsiChar(@s[i]), slen-i+1, @ombstate);
  630. {$else not beos}
  631. mblen:=mbtowc(@wc, PAnsiChar(@s[i]), slen-i+1);
  632. {$endif not beos}
  633. case mblen of
  634. size_t(-2):
  635. begin
  636. { partial invalid character, copy literally }
  637. while (i<=slen) do
  638. begin
  639. ConcatCharToAnsiStr(s[i],result,resindex);
  640. inc(i);
  641. end;
  642. end;
  643. size_t(-1), 0:
  644. begin
  645. { invalid or null character }
  646. ConcatCharToAnsiStr(s[i],result,resindex);
  647. inc(i);
  648. end;
  649. else
  650. begin
  651. { a valid sequence }
  652. { even if mblen = 1, the lowercase version may have a }
  653. { different length }
  654. { We can't do anything special if wchar_t is 16 bit... }
  655. {$if not(defined(beos) and not defined(haiku))}
  656. ConcatUTF32ToAnsiStr(towlower(wint_t(wc)),result,resindex,nmbstate);
  657. {$else not beos}
  658. ConcatUTF32ToAnsiStr(towlower(wint_t(wc)),result,resindex);
  659. {$endif not beos}
  660. inc(i,mblen);
  661. end;
  662. end;
  663. end;
  664. SetLength(result,resindex-1);
  665. end;
  666. function UpperAnsiString(const s : AnsiString) : AnsiString;
  667. var
  668. i, slen,
  669. resindex : SizeInt;
  670. mblen : size_t;
  671. {$if not(defined(beos) and not defined(haiku))}
  672. ombstate,
  673. nmbstate : mbstate_t;
  674. {$endif beos}
  675. wc : wchar_t;
  676. begin
  677. {$if not(defined(beos) and not defined(haiku))}
  678. fillchar(ombstate,sizeof(ombstate),0);
  679. fillchar(nmbstate,sizeof(nmbstate),0);
  680. {$endif beos}
  681. slen:=length(s);
  682. SetLength(result,slen+10);
  683. i:=1;
  684. resindex:=1;
  685. while (i<=slen) do
  686. begin
  687. if (s[i]<=#127) then
  688. begin
  689. wc:=wchar_t(s[i]);
  690. mblen:= 1;
  691. end
  692. else
  693. {$if not(defined(beos) and not defined(haiku))}
  694. mblen:=mbrtowc(@wc, PAnsiChar(@s[i]), slen-i+1, @ombstate);
  695. {$else not beos}
  696. mblen:=mbtowc(@wc, PAnsiChar(@s[i]), slen-i+1);
  697. {$endif beos}
  698. case mblen of
  699. size_t(-2):
  700. begin
  701. { partial invalid character, copy literally }
  702. while (i<=slen) do
  703. begin
  704. ConcatCharToAnsiStr(s[i],result,resindex);
  705. inc(i);
  706. end;
  707. end;
  708. size_t(-1), 0:
  709. begin
  710. { invalid or null character }
  711. ConcatCharToAnsiStr(s[i],result,resindex);
  712. inc(i);
  713. end;
  714. else
  715. begin
  716. { a valid sequence }
  717. { even if mblen = 1, the uppercase version may have a }
  718. { different length }
  719. { We can't do anything special if wchar_t is 16 bit... }
  720. {$if not(defined(beos) and not defined(haiku))}
  721. ConcatUTF32ToAnsiStr(towupper(wint_t(wc)),result,resindex,nmbstate);
  722. {$else not beos}
  723. ConcatUTF32ToAnsiStr(towupper(wint_t(wc)),result,resindex);
  724. {$endif not beos}
  725. inc(i,mblen);
  726. end;
  727. end;
  728. end;
  729. SetLength(result,resindex-1);
  730. end;
  731. function WideStringToUCS4StringNoNulls(const s : WideString) : UCS4String;
  732. var
  733. i, slen,
  734. destindex : SizeInt;
  735. uch : UCS4Char;
  736. begin
  737. slen:=length(s);
  738. setlength(result,slen+1);
  739. i:=1;
  740. destindex:=0;
  741. while (i<=slen) do
  742. begin
  743. uch:=UCS4Char(s[i]);
  744. if (uch=0) then
  745. result[destindex]:=32
  746. else if (uch<=$d7ff) or (uch>=$e000) then
  747. result[destindex]:=uch
  748. else if (uch<=$dbff) and
  749. (i<slen) and
  750. (s[i+1]>=#$dc00) and
  751. (s[i+1]<=#$dfff) then
  752. begin
  753. result[destindex]:=(UCS4Char(uch-$d7c0) shl 10)+(UCS4Char(s[i+1]) xor $dc00);
  754. inc(i);
  755. end
  756. else { invalid surrogate pair }
  757. result[destindex]:=uch;
  758. inc(i);
  759. inc(destindex);
  760. end;
  761. result[destindex]:=UCS4Char(0);
  762. { Trimming length in this particular case is just a waste of time,
  763. because result will be interpreted as null-terminated and discarded
  764. almost immediately }
  765. end;
  766. function CompareWideString(const s1, s2 : WideString; Options : TCompareOptions) : PtrInt;
  767. {$if not(defined (aix) and defined(cpupowerpc32))}
  768. var
  769. hs1,hs2 : UCS4String;
  770. us1,us2 : WideString;
  771. begin
  772. { wcscoll interprets null chars as end-of-string -> filter out }
  773. if coIgnoreCase in Options then
  774. begin
  775. us1:=UpperWideString(s1);
  776. us2:=UpperWideString(s2);
  777. end
  778. else
  779. begin
  780. us1:=s1;
  781. us2:=s2;
  782. end;
  783. hs1:=WideStringToUCS4StringNoNulls(us1);
  784. hs2:=WideStringToUCS4StringNoNulls(us2);
  785. result:=wcscoll(pwchar_t(hs1),pwchar_t(hs2));
  786. end;
  787. {$else}
  788. { AIX/PPC32 has a 16 bit wchar_t }
  789. var
  790. i, len: longint;
  791. us1,us2 : WideString;
  792. hs1, hs2: array of widechar;
  793. begin
  794. if coIgnoreCase in Options then
  795. begin
  796. us1:=UpperWideString(s1);
  797. us2:=UpperWideString(s2);
  798. end
  799. else
  800. begin
  801. us1:=s1;
  802. us2:=s2;
  803. end;
  804. len:=length(us1);
  805. setlength(hs1,len+1);
  806. for i:=1 to len do
  807. if us1[i]<>#0 then
  808. hs1[i-1]:=us1[i]
  809. else
  810. hs1[i-1]:=#32;
  811. hs1[len]:=#0;
  812. len:=length(us2);
  813. setlength(hs2,len+1);
  814. for i:=1 to len do
  815. if us2[i]<>#0 then
  816. hs2[i-1]:=us2[i]
  817. else
  818. hs2[i-1]:=#32;
  819. hs2[len]:=#0;
  820. result:=wcscoll(pwchar_t(hs1),pwchar_t(hs2));
  821. end;
  822. {$endif}
  823. { return value: number of code points in the string. Whenever an invalid
  824. code point is encountered, all characters part of this invalid code point
  825. are considered to form one "character" and the next character is
  826. considered to be the start of a new (possibly also invalid) code point }
  827. function CharLengthPChar(const Str: PAnsiChar): PtrInt;
  828. var
  829. nextlen: ptrint;
  830. s: PAnsiChar;
  831. {$if not(defined(beos) and not defined(haiku))}
  832. mbstate: mbstate_t;
  833. {$endif not beos}
  834. begin
  835. result:=0;
  836. s:=str;
  837. {$if not(defined(beos) and not defined(haiku))}
  838. fillchar(mbstate,sizeof(mbstate),0);
  839. {$endif not beos}
  840. repeat
  841. {$if defined(beos) and not defined(haiku)}
  842. nextlen:=ptrint(mblen(s,MB_CUR_MAX));
  843. {$else beos}
  844. nextlen:=ptrint(mbrlen(s,MB_CUR_MAX,@mbstate));
  845. {$endif beos}
  846. { skip invalid/incomplete sequences }
  847. if (nextlen<0) then
  848. nextlen:=1;
  849. inc(result,1);
  850. inc(s,nextlen);
  851. until (nextlen=0);
  852. end;
  853. function CodePointLength(const Str: PAnsiChar; maxlookahead: ptrint): PtrInt;
  854. {$if not(defined(beos) and not defined(haiku))}
  855. var
  856. mbstate: mbstate_t;
  857. {$endif not beos}
  858. begin
  859. {$if defined(beos) and not defined(haiku)}
  860. result:=ptrint(mblen(str,maxlookahead));
  861. {$else beos}
  862. fillchar(mbstate,sizeof(mbstate),0);
  863. result:=ptrint(mbrlen(str,maxlookahead,@mbstate));
  864. { mbrlen can also return -2 for "incomplete but potially valid character
  865. and data has been processed" }
  866. if result<0 then
  867. result:=-1;
  868. {$endif beos}
  869. end;
  870. function StrCompAnsiIntern(s1,s2 : PAnsiChar; len1, len2: PtrInt; canmodifys1, canmodifys2: boolean): PtrInt;
  871. var
  872. a,b: PAnsiChar;
  873. i: PtrInt;
  874. begin
  875. if not(canmodifys1) then
  876. getmem(a,len1+1)
  877. else
  878. a:=s1;
  879. for i:=0 to len1-1 do
  880. if s1[i]<>#0 then
  881. a[i]:=s1[i]
  882. else
  883. a[i]:=#32;
  884. a[len1]:=#0;
  885. if not(canmodifys2) then
  886. getmem(b,len2+1)
  887. else
  888. b:=s2;
  889. for i:=0 to len2-1 do
  890. if s2[i]<>#0 then
  891. b[i]:=s2[i]
  892. else
  893. b[i]:=#32;
  894. b[len2]:=#0;
  895. result:=strcoll(a,b);
  896. if not(canmodifys1) then
  897. freemem(a);
  898. if not(canmodifys2) then
  899. freemem(b);
  900. end;
  901. function CompareStrAnsiString(const s1, s2: ansistring): PtrInt;
  902. begin
  903. result:=StrCompAnsiIntern(PAnsiChar(s1),PAnsiChar(s2),length(s1),length(s2),false,false);
  904. end;
  905. function StrCompAnsi(s1,s2 : PAnsiChar): PtrInt;
  906. begin
  907. result:=strcoll(s1,s2);
  908. end;
  909. function AnsiCompareText(const S1, S2: ansistring): PtrInt;
  910. var
  911. a, b: AnsiString;
  912. begin
  913. a:=UpperAnsistring(s1);
  914. b:=UpperAnsistring(s2);
  915. result:=StrCompAnsiIntern(PAnsiChar(a),PAnsiChar(b),length(a),length(b),true,true);
  916. end;
  917. function AnsiStrIComp(S1, S2: PAnsiChar): PtrInt;
  918. begin
  919. result:=AnsiCompareText(ansistring(s1),ansistring(s2));
  920. end;
  921. function AnsiStrLComp(S1, S2: PAnsiChar; MaxLen: PtrUInt): PtrInt;
  922. var
  923. a, b: PAnsiChar;
  924. begin
  925. if (maxlen=0) then
  926. exit(0);
  927. if (s1[maxlen]<>#0) then
  928. begin
  929. getmem(a,maxlen+1);
  930. move(s1^,a^,maxlen);
  931. a[maxlen]:=#0;
  932. end
  933. else
  934. a:=s1;
  935. if (s2[maxlen]<>#0) then
  936. begin
  937. getmem(b,maxlen+1);
  938. move(s2^,b^,maxlen);
  939. b[maxlen]:=#0;
  940. end
  941. else
  942. b:=s2;
  943. result:=StrCompAnsiIntern(a,b,maxlen,maxlen,a<>s1,b<>s2);
  944. if (a<>s1) then
  945. freemem(a);
  946. if (b<>s2) then
  947. freemem(b);
  948. end;
  949. function AnsiStrLIComp(S1, S2: PAnsiChar; MaxLen: PtrUInt): PtrInt;
  950. var
  951. a, b: ansistring;
  952. begin
  953. if (maxlen=0) then
  954. exit(0);
  955. setlength(a,maxlen);
  956. move(s1^,a[1],maxlen);
  957. setlength(b,maxlen);
  958. move(s2^,b[1],maxlen);
  959. result:=AnsiCompareText(a,b);
  960. end;
  961. procedure ansi2pchar(const s: ansistring; const orgp: PAnsiChar; out p: PAnsiChar);
  962. var
  963. newlen: sizeint;
  964. begin
  965. newlen:=length(s);
  966. if newlen>strlen(orgp) then
  967. fpc_rangeerror;
  968. p:=orgp;
  969. if (newlen>0) then
  970. move(s[1],p[0],newlen);
  971. p[newlen]:=#0;
  972. end;
  973. function AnsiStrLower(Str: PAnsiChar): PAnsiChar;
  974. var
  975. temp: ansistring;
  976. begin
  977. temp:=loweransistring(str);
  978. ansi2pchar(temp,str,result);
  979. end;
  980. function AnsiStrUpper(Str: PAnsiChar): PAnsiChar;
  981. var
  982. temp: ansistring;
  983. begin
  984. temp:=upperansistring(str);
  985. ansi2pchar(temp,str,result);
  986. end;
  987. function envvarset(const varname: PAnsiChar): boolean;
  988. var
  989. varval: PAnsiChar;
  990. begin
  991. varval:=fpgetenv(varname);
  992. result:=
  993. assigned(varval) and
  994. (varval[0]<>#0);
  995. end;
  996. function GetStandardCodePage(const stdcp: TStandardCodePageEnum): TSystemCodePage;
  997. var
  998. langinfo: PAnsiChar;
  999. begin
  1000. {$ifdef FPCRTL_FILESYSTEM_UTF8}
  1001. if stdcp=scpFileSystemSingleByte then
  1002. begin
  1003. result:=CP_UTF8;
  1004. exit;
  1005. end;
  1006. {$endif}
  1007. { if none of the relevant LC_* environment variables are set, fall back to
  1008. UTF-8 (this happens under some versions of OS X for GUI applications, which
  1009. otherwise get CP_ASCII) }
  1010. if envvarset('LC_ALL') or
  1011. envvarset('LC_CTYPE') or
  1012. envvarset('LANG') then
  1013. begin
  1014. langinfo:=nl_langinfo(CODESET);
  1015. { there's a bug in the Mac OS X 10.5 libc (based on FreeBSD's)
  1016. that causes it to return an empty string of UTF-8 locales
  1017. -> patch up (and in general, UTF-8 is a good default on
  1018. Unix platforms) }
  1019. if not assigned(langinfo) or
  1020. (langinfo^=#0) then
  1021. langinfo:='UTF-8';
  1022. Result:=GetCodepageByName(ansistring(langinfo));
  1023. end
  1024. else
  1025. Result:={$IFDEF FPC_DOTTEDUNITS}UnixApi.CP{$ELSE}unixcp{$ENDIF}.GetSystemCodepage;
  1026. end;
  1027. {$ifdef FPC_HAS_CPSTRING}
  1028. procedure SetStdIOCodePage(var T: Text); inline;
  1029. begin
  1030. case TextRec(T).Mode of
  1031. fmInput:TextRec(T).CodePage:=GetStandardCodePage(scpConsoleInput);
  1032. fmOutput:TextRec(T).CodePage:=GetStandardCodePage(scpConsoleOutput);
  1033. end;
  1034. end;
  1035. procedure SetStdIOCodePages; inline;
  1036. begin
  1037. SetStdIOCodePage(Input);
  1038. SetStdIOCodePage(Output);
  1039. SetStdIOCodePage(ErrOutput);
  1040. SetStdIOCodePage(StdOut);
  1041. SetStdIOCodePage(StdErr);
  1042. end;
  1043. {$endif FPC_HAS_CPSTRING}
  1044. var
  1045. OrgWideStringManager: TUnicodeStringManager;
  1046. Procedure SetCWideStringManager;
  1047. Var
  1048. CWideStringManager : TUnicodeStringManager;
  1049. begin
  1050. GetUnicodeStringManager(OrgWideStringManager);
  1051. CWideStringManager:=OrgWideStringManager;
  1052. With CWideStringManager do
  1053. begin
  1054. Wide2AnsiMoveProc:=@Wide2AnsiMove;
  1055. Ansi2WideMoveProc:=@Ansi2WideMove;
  1056. UpperWideStringProc:=@UpperWideString;
  1057. LowerWideStringProc:=@LowerWideString;
  1058. CompareWideStringProc:=@CompareWideString;
  1059. // CompareTextWideStringProc:=@CompareTextWideString;
  1060. CharLengthPCharProc:=@CharLengthPChar;
  1061. CodePointLengthProc:=@CodePointLength;
  1062. UpperAnsiStringProc:=@UpperAnsiString;
  1063. LowerAnsiStringProc:=@LowerAnsiString;
  1064. CompareStrAnsiStringProc:=@CompareStrAnsiString;
  1065. CompareTextAnsiStringProc:=@AnsiCompareText;
  1066. StrCompAnsiStringProc:=@StrCompAnsi;
  1067. StrICompAnsiStringProc:=@AnsiStrIComp;
  1068. StrLCompAnsiStringProc:=@AnsiStrLComp;
  1069. StrLICompAnsiStringProc:=@AnsiStrLIComp;
  1070. StrLowerAnsiStringProc:=@AnsiStrLower;
  1071. StrUpperAnsiStringProc:=@AnsiStrUpper;
  1072. ThreadInitProc:=@InitThread;
  1073. ThreadFiniProc:=@FiniThread;
  1074. { Unicode }
  1075. Unicode2AnsiMoveProc:=@Wide2AnsiMove;
  1076. Ansi2UnicodeMoveProc:=@Ansi2WideMove;
  1077. UpperUnicodeStringProc:=@UpperWideString;
  1078. LowerUnicodeStringProc:=@LowerWideString;
  1079. CompareUnicodeStringProc:=@CompareWideString;
  1080. { CodePage }
  1081. GetStandardCodePageProc:=@GetStandardCodePage;
  1082. end;
  1083. SetUnicodeStringManager(CWideStringManager);
  1084. end;
  1085. var
  1086. iconvlib:TLibHandle;
  1087. initialization
  1088. SetCWideStringManager;
  1089. { you have to call setlocale(LC_ALL,'') to initialise the langinfo stuff }
  1090. { with the information from the environment variables according to POSIX }
  1091. { (some OSes do this automatically, but e.g. Darwin and Solaris don't) }
  1092. setlocale(LC_ALL,'');
  1093. { load iconv library and iconvctl function }
  1094. iconvlib:=LoadLibrary(libprefix+libiconvname+'.'+SharedSuffix);
  1095. if iconvlib=0 then
  1096. iconvlib:=LoadLibrary(libprefix+libiconvname+'.'+SharedSuffix+'.6');
  1097. if iconvlib<>0 then
  1098. pointer(iconvctl):=GetProcAddress(iconvlib,iconvctlname);
  1099. { set the DefaultSystemCodePage }
  1100. DefaultSystemCodePage:=GetStandardCodePage(scpAnsi);
  1101. DefaultFileSystemCodePage:=GetStandardCodePage(scpFileSystemSingleByte);
  1102. DefaultRTLFileSystemCodePage:=DefaultFileSystemCodePage;
  1103. {$ifdef FPC_HAS_CPSTRING}
  1104. SetStdIOCodePages;
  1105. {$endif FPC_HAS_CPSTRING}
  1106. { init conversion tables for main program }
  1107. InitThread;
  1108. finalization
  1109. { fini conversion tables for main program }
  1110. FiniThread;
  1111. { unload iconv library }
  1112. if iconvlib<>0 then
  1113. FreeLibrary(iconvlib);
  1114. { restore previous (probably default) widestring manager so that subsequent calls
  1115. into the widestring manager won't trigger the finalized functionality }
  1116. SetWideStringManager(OrgWideStringManager);
  1117. end.