codepage.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. #include "ci_string.h"
  2. #include "wide.h"
  3. #include "recyr.hh"
  4. #include "codepage.h"
  5. #include <util/string/cast.h>
  6. #include <util/string/subst.h>
  7. #include <util/string/util.h>
  8. #include <util/system/hi_lo.h>
  9. #include <util/system/yassert.h>
  10. #include <util/generic/hash.h>
  11. #include <util/generic/string.h>
  12. #include <util/generic/vector.h>
  13. #include <util/generic/hash_set.h>
  14. #include <util/generic/singleton.h>
  15. #include <util/generic/yexception.h>
  16. #include <util/memory/pool.h>
  17. #include <cstring>
  18. #include <ctype.h>
  19. using namespace NCodepagePrivate;
  20. void Recoder::Create(const CodePage& source, const CodePage& target) {
  21. const Encoder* wideTarget = &EncoderByCharset(target.CPEnum);
  22. Create(source, wideTarget);
  23. }
  24. void Recoder::Create(const CodePage& page, wchar32 (*mapfunc)(wchar32)) {
  25. const Encoder* widePage = &EncoderByCharset(page.CPEnum);
  26. Create(page, widePage, mapfunc);
  27. }
  28. template <class T, class T1>
  29. static inline T1 Apply(T b, T e, T1 to, const Recoder& mapper) {
  30. while (b != e) {
  31. *to++ = mapper.Table[(unsigned char)*b++];
  32. }
  33. return to;
  34. }
  35. template <class T, class T1>
  36. static inline T1 Apply(T b, T1 to, const Recoder& mapper) {
  37. while (*b != 0) {
  38. *to++ = mapper.Table[(unsigned char)*b++];
  39. }
  40. return to;
  41. }
  42. char* CodePage::ToLower(const char* b, const char* e, char* to) const {
  43. return Apply(b, e, to, TCodePageData::rcdr_to_lower[CPEnum]);
  44. }
  45. char* CodePage::ToLower(const char* b, char* to) const {
  46. return Apply(b, to, TCodePageData::rcdr_to_lower[CPEnum]);
  47. }
  48. char* CodePage::ToUpper(const char* b, const char* e, char* to) const {
  49. return Apply(b, e, to, TCodePageData::rcdr_to_upper[CPEnum]);
  50. }
  51. char* CodePage::ToUpper(const char* b, char* to) const {
  52. return Apply(b, to, TCodePageData::rcdr_to_upper[CPEnum]);
  53. }
  54. int CodePage::stricmp(const char* dst, const char* src) const {
  55. unsigned char f, l;
  56. do {
  57. f = ToLower(*dst++);
  58. l = ToLower(*src++);
  59. } while (f && (f == l));
  60. return f - l;
  61. }
  62. int CodePage::strnicmp(const char* dst, const char* src, size_t len) const {
  63. unsigned char f, l;
  64. if (len) {
  65. do {
  66. f = ToLower(*dst++);
  67. l = ToLower(*src++);
  68. } while (--len && f && (f == l));
  69. return f - l;
  70. }
  71. return 0;
  72. }
  73. static const CodePage UNSUPPORTED_CODEPAGE = {
  74. CODES_UNSUPPORTED,
  75. {
  76. "unsupported",
  77. },
  78. {},
  79. nullptr,
  80. };
  81. static const CodePage UNKNOWN_CODEPAGE = {
  82. CODES_UNKNOWN,
  83. {
  84. "unknown",
  85. },
  86. {},
  87. nullptr,
  88. };
  89. void NCodepagePrivate::TCodepagesMap::SetData(const CodePage* cp) {
  90. Y_ASSERT(cp);
  91. int code = static_cast<int>(cp->CPEnum) + DataShift;
  92. Y_ASSERT(code >= 0 && code < DataSize);
  93. Y_ASSERT(Data[code] == nullptr);
  94. Data[code] = cp;
  95. }
  96. NCodepagePrivate::TCodepagesMap::TCodepagesMap() {
  97. memset(Data, 0, sizeof(const CodePage*) * DataSize);
  98. SetData(&UNSUPPORTED_CODEPAGE);
  99. SetData(&UNKNOWN_CODEPAGE);
  100. for (size_t i = 0; i != CODES_MAX; ++i) {
  101. SetData(TCodePageData::AllCodePages[i]);
  102. }
  103. }
  104. const NCodepagePrivate::TCodepagesMap& NCodepagePrivate::TCodepagesMap::Instance() {
  105. return *Singleton<NCodepagePrivate::TCodepagesMap>();
  106. }
  107. class TCodePageHash {
  108. private:
  109. using TData = THashMap<TStringBuf, ECharset, ci_hash, ci_equal_to>;
  110. TData Data;
  111. TMemoryPool Pool;
  112. private:
  113. inline void AddNameWithCheck(const TString& name, ECharset code) {
  114. if (Data.find(name.c_str()) == Data.end()) {
  115. Data.insert(TData::value_type(Pool.Append(name.data(), name.size() + 1), code));
  116. } else {
  117. Y_ASSERT(Data.find(name.c_str())->second == code);
  118. }
  119. }
  120. inline void AddName(const TString& name, ECharset code) {
  121. AddNameWithCheck(name, code);
  122. TString temp = name;
  123. RemoveAll(temp, '-');
  124. RemoveAll(temp, '_');
  125. AddNameWithCheck(temp, code);
  126. temp = name;
  127. SubstGlobal(temp, '-', '_');
  128. AddNameWithCheck(temp, code);
  129. temp = name;
  130. SubstGlobal(temp, '_', '-');
  131. AddNameWithCheck(temp, code);
  132. }
  133. public:
  134. inline TCodePageHash()
  135. : Pool(20 * 1024) /* Currently used: 17KB. */
  136. {
  137. TString xPrefix = "x-";
  138. const char* name;
  139. for (size_t i = 0; i != CODES_MAX; ++i) {
  140. ECharset e = static_cast<ECharset>(i);
  141. const CodePage* page = Singleton<NCodepagePrivate::TCodepagesMap>()->GetPrivate(e);
  142. AddName(ToString(static_cast<int>(i)), e);
  143. for (size_t j = 0; (name = page->Names[j]) != nullptr && name[0]; ++j) {
  144. AddName(name, e);
  145. AddName(xPrefix + name, e);
  146. }
  147. }
  148. }
  149. inline ECharset CharsetByName(TStringBuf name) {
  150. if (!name)
  151. return CODES_UNKNOWN;
  152. TData::const_iterator it = Data.find(name);
  153. if (it == Data.end())
  154. return CODES_UNKNOWN;
  155. return it->second;
  156. }
  157. };
  158. ECharset CharsetByName(TStringBuf name) {
  159. return Singleton<TCodePageHash>()->CharsetByName(name);
  160. }
  161. ECharset CharsetByNameOrDie(TStringBuf name) {
  162. ECharset result = CharsetByName(name);
  163. if (result == CODES_UNKNOWN)
  164. ythrow yexception() << "CharsetByNameOrDie: unknown charset '" << name << "'";
  165. return result;
  166. }
  167. template <typename TxChar>
  168. static inline RECODE_RESULT utf8_read_rune_from_unknown_plane(TxChar& rune, size_t& rune_len, const TxChar* s, const TxChar* end) {
  169. if ((*s & 0xFF00) != 0xF000) {
  170. rune_len = 1;
  171. rune = *s;
  172. return RECODE_OK;
  173. }
  174. rune_len = 0;
  175. size_t _len = UTF8RuneLen((unsigned char)(*s));
  176. if (s + _len > end)
  177. return RECODE_EOINPUT; //[EOINPUT]
  178. if (_len == 0)
  179. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in first byte
  180. wchar32 _rune = (ui8)(*s++); //[00000000 0XXXXXXX]
  181. if (_len > 1) {
  182. _rune &= UTF8LeadByteMask(_len);
  183. wchar32 ch = *s++;
  184. if ((ch & 0xFFC0) != 0xF080)
  185. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in second byte
  186. _rune <<= 6;
  187. _rune |= ch & 0x3F; //[00000XXX XXYYYYYY]
  188. if (_len > 2) {
  189. ch = *s++;
  190. if ((ch & 0xFFC0) != 0xF080)
  191. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in third byte
  192. _rune <<= 6;
  193. _rune |= ch & 0x3F; //[XXXXYYYY YYZZZZZZ]
  194. if (_len > 3) {
  195. ch = *s;
  196. if ((ch & 0xFFC0) != 0xF080)
  197. return RECODE_BROKENSYMBOL; //[BROKENSYMBOL] in fourth byte
  198. _rune <<= 6;
  199. _rune |= ch & 0x3F; //[XXXYY YYYYZZZZ ZZQQQQQQ]
  200. }
  201. }
  202. }
  203. rune_len = _len;
  204. if (_rune > Max<TxChar>())
  205. rune = ' '; // maybe put sequence
  206. else
  207. rune = TxChar(_rune);
  208. return RECODE_OK;
  209. }
  210. template <typename TxChar>
  211. void DoDecodeUnknownPlane(TxChar* str, TxChar*& ee, const ECharset enc) {
  212. TxChar* e = ee;
  213. if (SingleByteCodepage(enc)) {
  214. const CodePage* cp = CodePageByCharset(enc);
  215. for (TxChar* s = str; s < e; s++) {
  216. if (Hi8(Lo16(*s)) == 0xF0)
  217. *s = (TxChar)cp->unicode[Lo8(Lo16(*s))]; // NOT mb compliant
  218. }
  219. } else if (enc == CODES_UTF8) {
  220. TxChar* s;
  221. TxChar* d;
  222. for (s = d = str; s < e;) {
  223. size_t l = 0;
  224. if (utf8_read_rune_from_unknown_plane(*d, l, s, e) == RECODE_OK) {
  225. d++, s += l;
  226. } else {
  227. *d++ = BROKEN_RUNE;
  228. ++s;
  229. }
  230. }
  231. e = d;
  232. } else if (enc == CODES_UNKNOWN) {
  233. for (TxChar* s = str; s < e; s++) {
  234. if (Hi8(Lo16(*s)) == 0xF0)
  235. *s = Lo8(Lo16(*s));
  236. }
  237. } else {
  238. Y_ASSERT(!SingleByteCodepage(enc));
  239. TxChar* s = str;
  240. TxChar* d = str;
  241. TVector<char> buf;
  242. size_t read = 0;
  243. size_t written = 0;
  244. for (; s < e; ++s) {
  245. if (Hi8(Lo16(*s)) == 0xF0) {
  246. buf.push_back(Lo8(Lo16(*s)));
  247. } else {
  248. if (!buf.empty()) {
  249. if (RecodeToUnicode(enc, buf.data(), d, buf.size(), e - d, read, written) == RECODE_OK) {
  250. Y_ASSERT(read == buf.size());
  251. d += written;
  252. } else { // just copying broken symbols
  253. Y_ASSERT(buf.size() <= static_cast<size_t>(e - d));
  254. Copy(buf.data(), buf.size(), d);
  255. d += buf.size();
  256. }
  257. buf.clear();
  258. }
  259. *d++ = *s;
  260. }
  261. }
  262. }
  263. ee = e;
  264. }
  265. void DecodeUnknownPlane(wchar16* str, wchar16*& ee, const ECharset enc) {
  266. DoDecodeUnknownPlane(str, ee, enc);
  267. }
  268. void DecodeUnknownPlane(wchar32* str, wchar32*& ee, const ECharset enc) {
  269. DoDecodeUnknownPlane(str, ee, enc);
  270. }
  271. namespace {
  272. class THashSetType: public THashSet<TString> {
  273. public:
  274. inline void Add(const TString& s) {
  275. insert(s);
  276. }
  277. inline bool Has(const TString& s) const noexcept {
  278. return find(s) != end();
  279. }
  280. };
  281. }
  282. class TWindowsPrefixesHashSet: public THashSetType {
  283. public:
  284. inline TWindowsPrefixesHashSet() {
  285. Add("win");
  286. Add("wincp");
  287. Add("window");
  288. Add("windowcp");
  289. Add("windows");
  290. Add("windowscp");
  291. Add("ansi");
  292. Add("ansicp");
  293. }
  294. };
  295. class TCpPrefixesHashSet: public THashSetType {
  296. public:
  297. inline TCpPrefixesHashSet() {
  298. Add("microsoft");
  299. Add("microsoftcp");
  300. Add("cp");
  301. }
  302. };
  303. class TIsoPrefixesHashSet: public THashSetType {
  304. public:
  305. inline TIsoPrefixesHashSet() {
  306. Add("iso");
  307. Add("isolatin");
  308. Add("latin");
  309. }
  310. };
  311. class TLatinToIsoHash: public THashMap<const char*, TString, ci_hash, ci_equal_to> {
  312. public:
  313. inline TLatinToIsoHash() {
  314. insert(value_type("latin1", "iso-8859-1"));
  315. insert(value_type("latin2", "iso-8859-2"));
  316. insert(value_type("latin3", "iso-8859-3"));
  317. insert(value_type("latin4", "iso-8859-4"));
  318. insert(value_type("latin5", "iso-8859-9"));
  319. insert(value_type("latin6", "iso-8859-10"));
  320. insert(value_type("latin7", "iso-8859-13"));
  321. insert(value_type("latin8", "iso-8859-14"));
  322. insert(value_type("latin9", "iso-8859-15"));
  323. insert(value_type("latin10", "iso-8859-16"));
  324. }
  325. };
  326. static inline void NormalizeEncodingPrefixes(TString& enc) {
  327. size_t preflen = enc.find_first_of("0123456789");
  328. if (preflen == TString::npos)
  329. return;
  330. TString prefix = enc.substr(0, preflen);
  331. for (size_t i = 0; i < prefix.length(); ++i) {
  332. if (prefix[i] == '-') {
  333. prefix.remove(i--);
  334. }
  335. }
  336. if (Singleton<TWindowsPrefixesHashSet>()->Has(prefix)) {
  337. enc.remove(0, preflen);
  338. enc.prepend("windows-");
  339. return;
  340. }
  341. if (Singleton<TCpPrefixesHashSet>()->Has(prefix)) {
  342. if (enc.length() > preflen + 3 && !strncmp(enc.c_str() + preflen, "125", 3) && isdigit(enc[preflen + 3])) {
  343. enc.remove(0, preflen);
  344. enc.prepend("windows-");
  345. return;
  346. }
  347. enc.remove(0, preflen);
  348. enc.prepend("cp");
  349. return;
  350. }
  351. if (Singleton<TIsoPrefixesHashSet>()->Has(prefix)) {
  352. if (enc.length() == preflen + 1 || enc.length() == preflen + 2) {
  353. TString enccopy = enc.substr(preflen);
  354. enccopy.prepend("latin");
  355. const TLatinToIsoHash* latinhash = Singleton<TLatinToIsoHash>();
  356. TLatinToIsoHash::const_iterator it = latinhash->find(enccopy.data());
  357. if (it != latinhash->end())
  358. enc.assign(it->second);
  359. return;
  360. } else if (enc.length() > preflen + 5 && enc[preflen] == '8') {
  361. enc.remove(0, preflen);
  362. enc.prepend("iso-");
  363. return;
  364. }
  365. }
  366. }
  367. class TEncodingNamesHashSet: public THashSetType {
  368. public:
  369. TEncodingNamesHashSet() {
  370. Add("iso-8859-1");
  371. Add("iso-8859-2");
  372. Add("iso-8859-3");
  373. Add("iso-8859-4");
  374. Add("iso-8859-5");
  375. Add("iso-8859-6");
  376. Add("iso-8859-7");
  377. Add("iso-8859-8");
  378. Add("iso-8859-8-i");
  379. Add("iso-8859-9");
  380. Add("iso-8859-10");
  381. Add("iso-8859-11");
  382. Add("iso-8859-12");
  383. Add("iso-8859-13");
  384. Add("iso-8859-14");
  385. Add("iso-8859-15");
  386. Add("windows-1250");
  387. Add("windows-1251");
  388. Add("windows-1252");
  389. Add("windows-1253");
  390. Add("windows-1254");
  391. Add("windows-1255");
  392. Add("windows-1256");
  393. Add("windows-1257");
  394. Add("windows-1258");
  395. Add("windows-874");
  396. Add("iso-2022-jp");
  397. Add("euc-jp");
  398. Add("shift-jis");
  399. Add("shiftjis");
  400. Add("iso-2022-kr");
  401. Add("euc-kr");
  402. Add("gb-2312");
  403. Add("gb2312");
  404. Add("gb-18030");
  405. Add("gb18030");
  406. Add("gbk");
  407. Add("big5");
  408. Add("tis-620");
  409. Add("tis620");
  410. }
  411. };
  412. ECharset EncodingHintByName(const char* encname) {
  413. if (!encname)
  414. return CODES_UNKNOWN; // safety check
  415. // Common trouble: spurious "charset=" in the encoding name
  416. if (!strnicmp(encname, "charset=", 8)) {
  417. encname += 8;
  418. }
  419. // Strip everything up to the first alphanumeric, and after the last one
  420. while (*encname && !isalnum(*encname))
  421. ++encname;
  422. if (!*encname)
  423. return CODES_UNKNOWN;
  424. const char* lastpos = encname + strlen(encname) - 1;
  425. while (lastpos > encname && !isalnum(*lastpos))
  426. --lastpos;
  427. // Do some normalization
  428. TString enc(encname, lastpos - encname + 1);
  429. enc.to_lower();
  430. for (char* p = enc.begin(); p != enc.end(); ++p) {
  431. if (*p == ' ' || *p == '=' || *p == '_')
  432. *p = '-';
  433. }
  434. NormalizeEncodingPrefixes(enc);
  435. ECharset hint = CharsetByName(enc.c_str());
  436. if (hint != CODES_UNKNOWN)
  437. return hint;
  438. if (Singleton<TEncodingNamesHashSet>()->Has(enc))
  439. return CODES_UNSUPPORTED;
  440. return CODES_UNKNOWN;
  441. }