Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
charmap.cpp
Go to the documentation of this file.
1
29#include <rw/_defs.h>
30
31// On Compaq Tru64 UNIX if included after assert.h, the definition of
32// _XOPEN_SOURCE macro in assert.h selects a different declaration for
33// iconv than the one used in comp test.
34#ifndef _WIN32
35# ifndef _RWSTD_NO_ICONV
36# include <iconv.h>
37# endif
38# include _RWSTD_CERRNO
39#else
40# include <windows.h>
41#endif // _WIN32
42
43#include <cassert>
44#include <cctype>
45#include <cerrno> // for errno
46#include <climits>
47#include <clocale> // for LC_CTYPE, setlocale()
48#include <cstdio>
49#include <cstdlib>
50#include <cstring> // for strrchr(), strerror()
51
52#include <map>
53#include <string>
54
55#include <vector>
56#include <iostream>
57#include <fstream>
58
59#include "aliases.h"
60#include "scanner.h"
61#include "charmap.h"
62#include "loc_exception.h"
63#include "diagnostic.h"
64
65// This value specifies the largest allowed symbolic name length
66// If necessary this can be increased, but it is very doubtful that
67// that would ever be necessary
68#define MAX_SYM_NAME_LEN 256
69
70// this is the maximum size of a single byte of a character in the
71// charmap file. According to POSIX this cannot be larger then 5
72// because all bytes are in the format "\x%x", "\d%x" or "\%o" and
73// the numeric values cannot be greater then 3 digits long
74#define MAX_BYTE_LEN 5
75
76#ifndef _RWSTD_NO_ICONV
77
78static iconv_t
79my_iconv_open (const char *to_codeset, const char *from_codeset)
80{
81 typedef std::vector<std::string> StrVec;
82
83 StrVec aliases [2];
84
85 const bool to_utf8 = !std::strcmp (to_codeset, "UTF-8");
86 const bool from_utf8 = !to_utf8;
87
88// aliases [to_utf8].push_back (to_codeset);
89// aliases [from_utf8].push_back (from_codeset);
90
91 get_cname_aliases (to_codeset, aliases [to_utf8]);
92 get_cname_aliases (from_codeset, aliases [from_utf8]);
93
94 typedef StrVec::iterator VecIter;
95
96 std::string tried_names [2];
97
98 for (VecIter i = aliases [to_utf8].begin (); i != aliases [to_utf8].end ();
99 ++i) {
100
101 for (VecIter j = aliases [from_utf8].begin ();
102 j != aliases [from_utf8].end (); ++j) {
103
104 const char* const to_code = (*i).c_str ();
105 const char* const from_code = (*j).c_str ();
106
107 const iconv_t ret = iconv_open (to_code, from_code);
108
109 if (ret != iconv_t (-1))
110 return ret;
111
112 if (i == aliases [to_utf8].begin ()) {
113
114 if (tried_names [from_utf8].size ()) {
115 tried_names [from_utf8] += ',';
116 tried_names [from_utf8] += ' ';
117 }
118
119 tried_names [from_utf8] += '"';
120 tried_names [from_utf8] += *j;
121 tried_names [from_utf8] += '"';
122 }
123 }
124
125 if (tried_names [to_utf8].size ()) {
126 tried_names [to_utf8] += ',';
127 tried_names [to_utf8] += ' ';
128 }
129
130 tried_names [to_utf8] += '"';
131 tried_names [to_utf8] += *i;
132 tried_names [to_utf8] += '"';
133 }
134
135 assert (0 != aliases [0].size ());
136 assert (0 != aliases [1].size ());
137
138 issue_diag (W_ICONV, false, 0,
139 "iconv_open(\"%s\", \"%s\") failed; "
140 "tried { %s } and { %s }\n",
141 aliases [to_utf8][0].c_str (),
142 aliases [from_utf8][0].c_str (),
143 tried_names [to_utf8].c_str (),
144 tried_names [from_utf8].c_str ());
145
146 return iconv_t (-1);
147}
148
149// open an iconv file descriptor to convert from the codeset to utf8
150iconv_t Charmap::open_iconv_to_utf8 () const
151{
152 if (in_utf8_)
153 return 0;
154
155 return my_iconv_open ("UTF-8", code_set_name_.c_str ());
156}
157
158# ifndef _RWSTD_NO_ISO_10646_WCHAR_T
159
160iconv_t Charmap::open_iconv_to_ext ()
161{
162 return my_iconv_open (code_set_name_.c_str (), "UTF-8");
163}
164
165# endif // _RWSTD_NO_ISO_10646_WCHAR_T
166#endif // _RWSTD_NO_ICONV
167
168
169// utf8_decode translates the UTF-8 encoded character (specified
170// by the range [from, to) into an object of type wchar_t
171// algorithm derived from RFC2279
172static wchar_t utf8_decode (const char* from, const char* to)
173{
174 assert (from <= to);
175
176 const unsigned char* const ch =
177 _RWSTD_REINTERPRET_CAST (const unsigned char*, from);
178
179 const unsigned char* const ch_end =
180 _RWSTD_REINTERPRET_CAST (const unsigned char*, to);
181
182 size_t num_bytes = 0;
183
184 wchar_t ret = 0;
185
186 // if the first character is below 0x80 then the value of *ch is the
187 // actual value of the character so return that value as a wchar_t
188 if (*ch < 0x80)
189 return wchar_t (*ch);
190
191 // if *ch is between 0xc2 and 0xe0 there are 2 bytes in the multi-byte
192 // character
193 if (*ch >= 0xc2 && *ch < 0xe0) {
194 ret = (*ch & 0x1f);
195 num_bytes = 2;
196 }
197
198 // if *ch is between 0xe0 and 0xf0 there are 3 bytes in the multi-byte
199 // character
200 else if (*ch >= 0xe0 && *ch < 0xf0) {
201 ret = *ch & 0x0f;
202 num_bytes = 3;
203 }
204 else if (*ch >= 0xf0 && *ch < 0xf8) {
205 ret = *ch & 0x07;
206 num_bytes = 4;
207 }
208 else if (*ch >= 0xf8 && *ch < 0xfc) {
209 ret = *ch & 0x03;
210 num_bytes = 5;
211 }
212 else if (*ch >= 0xfc && *ch < 0xfe) {
213 ret = *ch & 0x01;
214 num_bytes = 6;
215 }
216 else {
217 issue_diag (E_MBCHAR, true, 0,
218 "illegal multibyte prefix '\\x%02x' in character "
219 "map file\n", *ch);
220 }
221
222 if (ch_end < ch + num_bytes - 1) {
223 // the input doesn't have enough characters
224 issue_diag (E_MBCHAR, true, 0,
225 "incomplete multibyte character in character "
226 "map file: expecting %u bytes, found %u\n",
227 num_bytes, ch_end - ch);
228 }
229
230 // for each byte in the character extract the useful data by shifting
231 // and bit or it into the wchar_t
232 for (size_t i = 1; i < num_bytes; ++i)
233 ret = (ret << 6) | (ch [i] & 0x3f);
234
235 return ret;
236}
237
238
239// count the number of bytes in a multibyte sequence denoted
240// by the argument by counting the number of escape characters
241std::size_t Charmap::mbcharlen (const std::string &str) const
242{
243 std::size_t count = 1;
244
245 const char escape = scanner_.escape_char ();
246
247 for (std::size_t idx = 0; ; ++idx, ++count) {
248 idx = str.find (escape, idx);
249
250 if (std::string::npos == idx)
251 break;
252 }
253
254 return count;
255}
256
257
258
260const char* const Charmap::
262 /* 0x00 */ "<NUL>",
263 /* 0x01 SOH */ 0,
264 /* 0x02 STX */ 0,
265 /* 0x03 ETX */ 0,
266 /* 0x04 EOT */ 0,
267 /* 0x05 ENQ */ 0,
268 /* 0x06 ACK */ 0,
269 /* 0x07 BEL */ "<alert>",
270 /* 0x08 */ "<backspace>",
271 /* 0x09 TAB */ "<tab>",
272 /* 0x0a */ "<newline>",
273 /* 0x0b */ "<vertical-tab>",
274 /* 0x0c */ "<form-feed>",
275 /* 0x0d */ "<carriage-return>",
276 /* 0x0e SO */ 0,
277 /* 0x0f SI */ 0,
278 /* 0x10 DLE */ 0,
279 /* 0x11 DC1 */ 0,
280 /* 0x12 DC2 */ 0,
281 /* 0x13 DC3 */ 0,
282 /* 0x14 DC4 */ 0,
283 /* 0x15 NAK */ 0,
284 /* 0x16 SYN */ 0,
285 /* 0x17 ETB */ 0,
286 /* 0x18 CAN */ 0,
287 /* 0x19 EM */ 0,
288 /* 0x1a SUB */ 0,
289 /* 0x1b ESC */ 0,
290 /* 0x1c IS4 */ 0,
291 /* 0x1d IS3 */ 0,
292 /* 0x1e IS2 */ 0,
293 /* 0x1f IS1 */ 0,
294 /* 0x20 SPC */ "<space>",
295 /* 0x21 ! */ "<exclamation-mark>",
296 /* 0x22 ' */ "<quotation-mark>",
297 /* 0x23 # */ "<number-sign>",
298 /* 0x24 $ */ "<dollar-sign>",
299 /* 0x25 % */ "<percent-sign>",
300 /* 0x26 & */ "<ampersand>",
301 /* 0x27 ' */ "<apostrophe>",
302 /* 0x28 ( */ "<left-parenthesis>",
303 /* 0x29 ) */ "<right-parenthesis>",
304 /* 0x2a * */ "<asterisk>",
305 /* 0x2b + */ "<plus-sign>",
306 /* 0x2c , */ "<comma>",
307 /* 0x2d - */ "<hyphen>", // "<hyphen-minus>",
308 /* 0x2e . */ "<period>", // "<full-stop>",
309 /* 0x2f / */ "<slash>", // "<solidus>",
310 /* 0x30 0 */ "<zero>",
311 /* 0x31 1 */ "<one>",
312 /* 0x32 2 */ "<two>",
313 /* 0x33 3 */ "<three>",
314 /* 0x34 4 */ "<four>",
315 /* 0x35 5 */ "<five>",
316 /* 0x36 6 */ "<six>",
317 /* 0x37 7 */ "<seven>",
318 /* 0x38 8 */ "<eight>",
319 /* 0x39 9 */ "<nine>",
320 /* 0x3a : */ "<colon>",
321 /* 0x3b ; */ "<semicolon>",
322 /* 0x3c < */ "<less-than-sign>",
323 /* 0x3d = */ "<equals-sign>",
324 /* 0x3e > */ "<greater-than-sign>",
325 /* 0x3f ? */ "<question-mark>",
326 /* 0x40 @ */ "<commercial-at>",
327 /* 0x41 A */ "<A>",
328 /* 0x42 B */ "<B>",
329 /* 0x43 C */ "<C>",
330 /* 0x44 D */ "<D>",
331 /* 0x45 E */ "<E>",
332 /* 0x46 F */ "<F>",
333 /* 0x47 G */ "<G>",
334 /* 0x48 H */ "<H>",
335 /* 0x49 I */ "<I>",
336 /* 0x4a J */ "<J>",
337 /* 0x4b K */ "<K>",
338 /* 0x4c L */ "<L>",
339 /* 0x4d M */ "<M>",
340 /* 0x4e N */ "<N>",
341 /* 0x4f O */ "<O>",
342 /* 0x50 P */ "<P>",
343 /* 0x51 Q */ "<Q>",
344 /* 0x52 R */ "<R>",
345 /* 0x53 S */ "<S>",
346 /* 0x54 T */ "<T>",
347 /* 0x55 U */ "<U>",
348 /* 0x56 V */ "<V>",
349 /* 0x57 W */ "<W>",
350 /* 0x58 X */ "<X>",
351 /* 0x59 Y */ "<Y>",
352 /* 0x5a Z */ "<Z>",
353 /* 0x5b [ */ "<left-square-bracket>",
354 /* 0x5c \ */ "<backslash>", // "<reverse-solidus>",
355 /* 0x5d ] */ "<right-square-bracket>",
356 /* 0x5e ^ */ "<circumflex>", // "<circumflex-accent>",
357 /* 0x5f _ */ "<underscore>", // "<low-line>",
358 /* 0x60 ` */ "<grave-accent>",
359 /* 0x61 a */ "<a>",
360 /* 0x62 b */ "<b>",
361 /* 0x63 c */ "<c>",
362 /* 0x64 d */ "<d>",
363 /* 0x65 e */ "<e>",
364 /* 0x66 f */ "<f>",
365 /* 0x67 g */ "<g>",
366 /* 0x68 h */ "<h>",
367 /* 0x69 i */ "<i>",
368 /* 0x6a j */ "<j>",
369 /* 0x6b k */ "<k>",
370 /* 0x6c l */ "<l>",
371 /* 0x6d m */ "<m>",
372 /* 0x6e n */ "<n>",
373 /* 0x6f o */ "<o>",
374 /* 0x70 p */ "<p>",
375 /* 0x71 q */ "<q>",
376 /* 0x72 r */ "<r>",
377 /* 0x73 s */ "<s>",
378 /* 0x74 t */ "<t>",
379 /* 0x75 u */ "<u>",
380 /* 0x76 v */ "<v>",
381 /* 0x77 w */ "<w>",
382 /* 0x78 x */ "<x>",
383 /* 0x79 y */ "<y>",
384 /* 0x7a z */ "<z>",
385 /* 0x7b { */ "<left-brace>", // "<left-curly-bracket>",
386 /* 0x7c | */ "<vertical-line>",
387 /* 0x7d } */ "<right-brace>", // "<right-curly-bracket>",
388 /* 0x7e ~ */ "<tilde>",
389 /* 0x7f */ 0
390};
391
392
393// convert a string of narrow character into a wchar_t
394bool Charmap::convert_to_wc (const std::string& sym_name,
395 const std::string& ext_enc, wchar_t& wc)
396{
397#ifndef _RWSTD_NO_ISO_10646_WCHAR_T
398
399 // the internal wchar_t representation for all characters
400 // in all locales is always ISO-10646 (UCS) on this system
401 return convert_to_ucs (sym_name, ext_enc, wc);
402
403#else // if defined _RWSTD_NO_ISO_10646_WCHAR_T
404
405 if (UCS4_internal_ || Clocale_.empty ()) {
406
407 // when using UCS as the internal encoding or for a locale
408 // that has no corresponding C library locale convert the
409 // character to ISO-10646 (UCS)
410 return convert_to_ucs (sym_name, ext_enc, wc);
411 }
412
413 // otherwise use libc to convert the multi-byte character
414 // to its wchar_t value
415 if (-1 == std::mbtowc (&wc, ext_enc.c_str (), ext_enc.size ())) {
416
417 const char* const locname = std::setlocale (LC_CTYPE, 0);
418 const char* const errtext = std::strerror (errno);
419
420 // diagnose the failure to convert the character as just
421 // a warning and (try to) convert it to ISO-10646 (UCS)
422 issue_diag (W_CALL, true, &next,
423 "mbtowc failed to convert character in locale "
424 "\"%s\": %s\n", locname, errtext);
425
426 return convert_to_ucs (sym_name, ext_enc, wc);
427 }
428
429 return true;
430
431#endif // _RWSTD_NO_ISO_10646_WCHAR_T
432
433}
434
435
436char* Charmap::convert_to_utf8 (const char *inbuf, size_t inbuf_s,
437 char *outbuf, size_t outbuf_s) const
438{
439#ifndef _RWSTD_NO_ICONV
440
441 if (ic_to_utf8_ == iconv_t (-1))
442 return 0;
443
444 char* outbufp = outbuf;
445
446# ifndef _RWSTD_NO_ICONV_CONST_CHAR
447 const char* inbufp = inbuf;
448# else
449 char* inbufp = _RWSTD_CONST_CAST(char*, inbuf);
450# endif // _RWSTD_NO_ICONV_CONST_CHAR
451
452 if (std::size_t (-1) ==
453 iconv (ic_to_utf8_, &inbufp, &inbuf_s, &outbufp, &outbuf_s)) {
454 const char* const errtext = std::strerror (errno);
455
456 issue_diag (W_ICONV, false, &next,
457 "iconv failed to convert \"%s\" "
458 "to UTF-8: %s\n", inbuf, errtext);
459
460 return 0;
461 }
462
463 return outbufp;
464
465#else // if defined (_RWSTD_NO_ICONV)
466
467 return 0;
468
469#endif // _RWSTD_NO_ICONV
470
471}
472
473
474
475std::string Charmap::get_charmap_name () const
476{
477 const std::string::size_type idx = charmap_name_.rfind (_RWSTD_PATH_SEP);
478
479 if (idx != std::string::npos)
480 return charmap_name_.substr (idx + 1);
481
482 return charmap_name_;
483}
484
485
486wchar_t Charmap::increment_wchar (wchar_t val) const
487{
488#ifndef _RWSTD_NO_ISO_10646_WCHAR_T
489
490 // to increment a wchar_t value and keep the encoding all we have
491 // to do is increment the val because the internal encoding is UCS
492 return val + 1;
493
494#else
495 // to increment a wchar_t value and keep the encoding we have to
496 // convert the wchar_t to the external encoding, increment that
497 // string value, and convert back to the internal representation
498 const rmb_cmap_iter it = rmb_cmap_.find (val);
499
500 if (it != rmb_cmap_.end ()) {
501
502 mb_cmap_iter ret;
503
504 // multibyte character corresponding to the wchar_t value
505 std::string encoding = it->second;
506
507 // continue incrementing the multi-byte value until we get a valid
508 // character. NOTE: this must be done for encodings such as SJIS where
509 // \x7f in the last byte of a multibyte string is not a valid character
510 // NOTE: this will not detect errors in the sequence, since the program
511 // will continue until it finds a valid character
512 do {
513 int last_elm = int (encoding.size ()) - 1;
514
515 while (last_elm >= 0) {
516
517 typedef unsigned char UChar;
518
519 const unsigned ic = UChar (encoding [last_elm]) + 1;
520
521 // if incrementing the last element caused it to exceed
522 // UCHAR_MAX increment the next higher byte if there is
523 // one
524 if (UCHAR_MAX < ic)
525 encoding [last_elm--] = '\0';
526 else {
527 encoding [last_elm] = char (ic);
528 break;
529 }
530 }
531
532 if (last_elm < 0)
533 return -1; // error
534
535 } while ((ret = mb_cmap_.find (encoding)) == mb_cmap_.end ());
536
537 return ret->second;
538 }
539
540 return -1; // error
541
542#endif // _RWSTD_NO_ISO_10646_WCHAR_T
543
544}
545
546
547bool Charmap::
548increment_encoding (std::string &encoding)
549{
550 // find the last escape character in the human readable representation
551 // of the encoding (i.e., in the multibyte character such as "/xf0/x80")
552 const std::string::size_type pos =
553 encoding.rfind (scanner_.escape_char ());
554
555 // the escape character must be there (guaranteed by the scanner)
556 assert (pos < encoding.size ());
557
558 const char* end = 0;
559
560 // convert the last character in the multibyte character to a numeric
561 // value representing the last byte of the sequence
562 unsigned last_byte =
563 unsigned (scanner_.convert_escape (encoding.c_str () + pos, &end));
564
565 // POSIX requires that the incremented value be non-NUL
566 if (UCHAR_MAX <= last_byte || *end)
567 return false;
568
569 // increment the last byte
570 ++last_byte;
571
572 // format the last byte in the same notation (octal, decimal,
573 // or hexadecimal escape sequence)
574 static const char xdigits[] = "0123456789ABCDEF";
575
576 char byte_str [5];
577 char *pdig = byte_str;
578
579 switch (encoding [pos + 1]) {
580 case 'd': { // decimal escape
581 const unsigned hundreds = last_byte / 100;
582 const unsigned tens = (last_byte - hundreds) / 10;
583 const unsigned units = last_byte % 10;
584
585 *pdig++ = 'd';
586
587 if (hundreds)
588 *pdig++ = xdigits [hundreds];
589
590 *pdig++ = xdigits [tens];
591 *pdig++ = xdigits [units];
592 *pdig = '\0';
593 break;
594 }
595
596 case 'x': { // hex escape
597 const unsigned hi = last_byte >> 4;
598 const unsigned lo = last_byte & 0xfU;
599
600 *pdig++ = 'x';
601 *pdig++ = xdigits [hi];
602 *pdig++ = xdigits [lo];
603 *pdig = '\0';
604 break;
605 }
606 default: { // octal escape
607 const unsigned hi = last_byte >> 6;
608 const unsigned mid = (last_byte >> 3) & 07U;
609 const unsigned lo = last_byte & 07U;
610
611 if (hi)
612 *pdig++ = xdigits [hi];
613
614 *pdig++ = xdigits [mid];
615 *pdig++ = xdigits [lo];
616 *pdig = '\0';
617 }
618 } // switch
619
620 // replace the last escape sequence with the new one
621 encoding.replace (pos + 1, std::string::npos, byte_str);
622
623 return true;
624}
625
626
627std::string Charmap::
628encoding_to_mbchar (const std::string &encoding) const
629{
630 std::string mbchar;
631
632 for (const char *pbyte = encoding.c_str (); pbyte && *pbyte; )
633 mbchar += char (scanner_.convert_escape (pbyte, &pbyte));
634
635 return mbchar;
636}
637
638
639// convert the locale's encoded character to UCS4 wchar_t
641convert_sym_to_ucs (const std::string &sym) const
642{
643 std::string::const_iterator it (sym.begin ());
644
645 if ( sym.size () < 4 || *it != '<' || *++it != 'U'
646 || !(std::isxdigit)(*++it)) {
647 issue_diag (E_UCS, true, 0,
648 "Unable to convert symbolic name %s to UCS.\n",
649 sym.c_str ());
650 }
651
652 const unsigned long val = std::strtoul (&*it, (char**)0, 16);
653
654 if (_RWSTD_WCHAR_MAX <= val)
655 issue_diag (E_UCS, true, 0,
656 "UCS value %lu of symbolic character %s out of range.\n",
657 val, sym.c_str ());
658
659 return wchar_t (val);
660}
661
662
663// convert the locale's encoded character to UCS4/UCS2 wchar_t
664bool Charmap::convert_to_ucs (const std::string &sym_name,
665 const std::string &encoding, wchar_t& wc)
666{
667#ifndef _WIN32
668
669 if (in_utf8_) {
670 wc = utf8_decode (encoding.c_str (), &*(encoding.end () - 1));
671 return true;
672 }
673
674 // allocate enough space for the longest possible UTF-8 character
675 char utf8_enc [8 + 1 /* NUL */];
676
677 const char* const ch_end =
678 convert_to_utf8 (encoding.c_str (), encoding.size (),
679 utf8_enc, sizeof utf8_enc);
680 if (ch_end)
681 // only if conversion to utf8 succeeded
682 wc = utf8_decode (utf8_enc, ch_end);
683 else
684 // if not, try to convert the symbolic name directly
685 wc = convert_sym_to_ucs (sym_name);
686
687 return true;
688
689#else
690
691 if (0 != codepage_) {
692 wchar_t ret[2] = {0};
693 const int res = MultiByteToWideChar (codepage_, 0,
694 encoding.c_str(), -1,
695 ret, 2);
696 if (!res && ERROR_INVALID_PARAMETER == GetLastError ()) {
697 // the required codepage conversion table is not installed
698 wc = convert_sym_to_ucs (sym_name);
699 return true;
700 }
701
702 if (!res || ret[1] != 0)
703 return false;
704
705 wc = ret[0];
706 return true;
707 }
708
709 wc = convert_sym_to_ucs (sym_name);
710 return true;
711
712#endif // _WIN32
713}
714
715
716void Charmap::add_to_cmaps (const std::string &sym_name,
717 const std::string &encoding,
718 bool is_mbchar /* = false */)
719{
720 // compute the external (multibyte) encoding of the character
721 // if necessary (i.e., unless already done by the caller)
722 const std::string mbchar =
723 is_mbchar ? encoding : encoding_to_mbchar (encoding);
724
725 symnames_list_.push_back (sym_name);
726
727 if (1 == mbchar.size ()) {
728 // strval is a single-byte character
729
730 const unsigned char ch = mbchar [0];
731
732 // add the wide character and its symbolic name to the narrow
733 // character maps
734 if (forward_maps) {
735 // the locale utility doesn't need reverse maps
736 n_cmap_.insert (std::make_pair (sym_name, ch));
737 }
738
739 if (reverse_maps)
740 rn_cmap_.insert (std::make_pair (ch, sym_name));
741
742 if (ch > largest_nchar_)
743 largest_nchar_ = ch;
744 }
745
746 // (try to) compute the wide character value of the character
747 wchar_t wch;
748
749 if (convert_to_wc (sym_name, mbchar, wch)) {
750
751 // add the wide character and its symbolic name to the wide
752 // character maps
753 if (forward_maps) {
754 // the locale utility doesn't need forward maps
755 w_cmap_.insert (std::make_pair (sym_name, wch));
756 }
757
758 if (reverse_maps)
759 rw_cmap_.insert (std::make_pair (wch, sym_name));
760
761 // add the corresponding multibyte character to the multibyte
762 // character maps
763 mb_cmap_.insert (std::make_pair (mbchar, wch));
764 rmb_cmap_.insert (std::make_pair (wch, mbchar));
765 }
766
767 // compute the UCS value of the character
768 wchar_t uch;
769
770 if (convert_to_ucs (sym_name, mbchar, uch)) {
771
772 // add UCS character and its symbolic name to the UCS
773 // character maps
774 ucs4_cmap_.insert (std::make_pair (sym_name, uch));
775 rucs4_cmap_.insert (std::make_pair (uch, sym_name));
776 }
777}
778
779
780// process the characters implicitly defined by using ellipsis between
781// two explicitly defined characters
782std::size_t Charmap::
783process_ellipsis (const Scanner::token_t &beg_tok, int num_ellipsis)
784{
785 // get the upper end of the range denoted by the ellipsis
786 const Scanner::token_t end_tok = scanner_.next_token ();
787
788 // get the human readabale encoding of the character
789 // denoted by the lower end of the ellipsis
790 const std::string encoding = scanner_.next_token ().name;
791
792 // convert the encoding to a multibyte character
793 std::string mbchar = encoding_to_mbchar (encoding);
794
795 // add the beg_tok symbol name to the maps
796 add_to_cmaps (beg_tok.name, mbchar, true);
797
798 // extract the numeric portion of the symbolic character name
799 // denoted by the lower end of the ellipsis
800 std::size_t idx = 0;
801
802 int base; // numeric base
803 const char *fmat; // sprintf() format specifier
804
805 const std::size_t beg_len = beg_tok.name.size ();
806
807 // determine the value of the beginning of the range
808 // denoted by the ellipsis
809 if (2 == num_ellipsis) {
810 base = 16;
811 fmat = "%.*s%0*lX>";
812
813 // advance to the first hex digit
814 while (idx < beg_len && !(std::isxdigit)(beg_tok.name [idx]))
815 ++idx;
816 }
817 else {
818 base = 10;
819 fmat = "%.*s%0*ld>";
820
821 // advance to the first decimal digit
822 while (idx < beg_len && !(std::isdigit)(beg_tok.name [idx]))
823 ++idx;
824 }
825
826 // length of non-numeric prefix of the symbolic character name
827 const std::size_t pfx_len = idx;
828
829 // get the character value plus one (since the first value
830 // has already been added to the map earlier)
831 char *num_end;
832 const unsigned long beg_val =
833 1 + std::strtoul (beg_tok.name.c_str () + pfx_len, &num_end, base);
834
835 // the length of the numeric portion
836 const std::size_t num_size =
837 num_end - (beg_tok.name.c_str () + pfx_len);
838
839 // find the end of the range denoted by the ellipsis
840 idx = 0;
841
842 const std::size_t end_len = end_tok.name.size ();
843
844 if (2 == num_ellipsis) {
845 // advance to the next hex digit
846 while (idx < end_len && !(std::isxdigit)(end_tok.name [idx]))
847 ++idx;
848 }
849 else {
850 // advance to the next dec digit
851 while (idx < end_len && !(std::isdigit)(end_tok.name [idx]))
852 ++idx;
853 }
854
855 const unsigned long end_val =
856 std::strtoul (end_tok.name.c_str () + idx, (char**)0, base);
857
858 // the ending numeric value must be greater than or equal
859 // to the beginning numeric value
860 if (end_val < beg_val)
861 issue_diag (E_RANGE, true, &end_tok,
862 "invalid range found in character map file\n");
863
864 char next_name [MAX_SYM_NAME_LEN];
865
866 std::size_t nchars = 0;
867
868 const char* const pfx = beg_tok.name.c_str ();
869
870 for (unsigned long val = beg_val; val <= end_val; ++val, ++nchars) {
871
872 std::sprintf (next_name, fmat, pfx_len, pfx, num_size, val);
873
874 // increment the last byte of the multibyte character
875 // and if the result is valid (i.e., doesn't contain
876 // an embedded NUL) add the generated name and the
877 // multibyte character to the maps
878 const unsigned char last_byte = mbchar [mbchar.size () - 1];
879 if (last_byte < UCHAR_MAX) {
880 mbchar [mbchar.size () - 1] = last_byte + 1;
881 add_to_cmaps (next_name, mbchar, true);
882 }
883 else {
884 // an ellipsis must not specify a range that includes
885 // an encoding with an embedded NUL
886 issue_diag (E_RANGE, true, &beg_tok,
887 "encoding of an element in range contains NUL\n");
888 }
889 }
890
891 // return the number of characters denoted by the ellipsis
892 return nchars;
893}
894
895
896// process all the characters in the character map file.
897void Charmap::process_chars()
898{
899 issue_diag (I_STAGE, false, 0, "processing CHARMAP section\n");
900
901 std::size_t ntokens = 0;
902 std::size_t nellips = 0;
903 std::size_t nchars = 0;
904
905 next = scanner_.next_token();
906 Scanner::token_t nextnext;
907
908 // loop until we find the closing charmap token
909 for ( ; next.token != Scanner::tok_charmap; ++ntokens) {
910
911 switch (next.token) {
912
913 case Scanner::tok_nl:
914 case Scanner::tok_end:
915 break;
916
918 // the next token may be either ellipsis if this line
919 // of the charmap is in the form:
920 // "%s...%s %s\n", <sym_name>, <sym_name>, <encoding>
921 // or an encoding if this line is in the format:
922 // "%s %s\n", <sym_name>, <encoding>
923 nextnext = scanner_.next_token ();
924 ntokens += 3;
925
926 switch (nextnext.token) {
927
929 // absolute ellipsis (see ISO/IEC TR 14652)
930 nchars += process_ellipsis (next, 3);
931 ++nellips;
932 break;
933
935 // hexadecimal symbolic ellipsis (see ISO/IEC TR 14652)
936 nchars += process_ellipsis (next, 2);
937 ++nellips;
938 break;
939
941 // character represented as a numeric constant
942 add_to_cmaps (next.name, nextnext.name);
943 ++nchars;
944 break;
945
946 default:
947 issue_diag (E_SYNTAX, true, &next,
948 "byte value expected following symbolic "
949 "name in character map file\n");
950 }
951
952 scanner_.ignore_line ();
953 break;
954
955 default:
956 issue_diag (E_SYNTAX, true, &next,
957 "symbolic name expected in character map file\n");
958 break;
959 }
960
961 next = scanner_.next_token();
962 }
963
964 issue_diag (I_STAGE, false, 0,
965 "done processing CHARMAP section (%lu tokens, "
966 "%lu ellipses, %lu characters)\n",
967 ntokens, nellips, nchars);
968
969 // make sure that all characters in the portable character set
970 // are in the charmap
971 if (forward_maps)
972 verify_portable_charset();
973}
974
975
976void Charmap::verify_portable_charset () const
977{
978 const std::size_t nchars =
979 sizeof portable_charset / sizeof *portable_charset;
980
981 for (std::size_t i = 0; i < nchars; ++i) {
982 if (0 == portable_charset [i])
983 continue;
984
985 if (n_cmap_.find (portable_charset [i]) == n_cmap_.end ())
986 issue_diag (W_NOPCS, false, 0,
987 "member of portable character set %s not found "
988 "in the character map\n", portable_charset [i]);
989 }
990}
991
992
993Charmap::Charmap(const char* Clocale,
994 const char* fname,
995 bool in_utf8, bool create_forward_maps,
996 bool create_reverse_maps, bool use_UCS4)
997 : mb_cur_max_(1),
998 charmap_name_ (fname),
999 Clocale_ (Clocale),
1000 largest_nchar_(0),
1001 in_utf8_(in_utf8),
1002 forward_maps (create_forward_maps),
1003 reverse_maps (create_reverse_maps),
1004 UCS4_internal_ (use_UCS4)
1005{
1006#ifndef _RWSTD_NO_ICONV
1007 ic_to_utf8_ = 0;
1008 ic_to_ext_ = 0;
1009#endif // _RWSTD_NO_ICONV
1010
1011 scanner_.open (fname, '#', '\\');
1012
1013 // set code_set_name to the name of the character set description
1014 // file by default, in case it's not explicitly specified
1015 const char* const slash = std::strrchr (fname, _RWSTD_PATH_SEP);
1016 code_set_name_ = slash ? slash + 1 : fname;
1017
1018 // loop until we reach the end of the file
1019 while ((next = scanner_.next_token()).token != Scanner::tok_end_tokens) {
1020
1021 switch (next.token) {
1022
1024 next = scanner_.next_token ();
1025
1026 if (next.token == Scanner::tok_string) {
1027 code_set_name_ = next.name.substr (1, next.name.size () - 2);
1028 }
1029 else if (next.token == Scanner::tok_ndef) {
1030 code_set_name_ = next.name;
1031 }
1032 else
1033 issue_diag (E_SYNTAX, true, &next,
1034 "string expected following <code_set_name>\n");
1035
1036 // we always need a iconv to utf8 so that we can create
1037 // the utf8_charmap unless we are on windows
1038#ifndef _RWSTD_NO_ICONV
1039 if (!in_utf8_) {
1040 ic_to_utf8_ = open_iconv_to_utf8 ();
1041# if !defined (_RWSTD_NO_ISO_10646_WCHAR_T)
1042 ic_to_ext_ = open_iconv_to_ext ();
1043# endif // _RWSTD_NO_ISO_10646_WCHAR_T
1044 }
1045
1046#else // if defined (_RWSTD_NO_ICONV)
1047
1048# ifdef _WIN32
1049 codepage_ = get_codepage (code_set_name_);
1050 if (codepage_ == 0) {
1051 issue_diag (W_ICONV, false, 0,
1052 "iconv_open (%s to UTF-8) failed\n",
1053 code_set_name_.c_str());
1054 }
1055
1056# endif // _WIN32
1057#endif // _RWSTD_NO_ICONV
1058
1059 scanner_.ignore_line ();
1060 break;
1061
1063 mb_cur_max_ = std::atoi (scanner_.next_token ().name.c_str ());
1064 scanner_.ignore_line ();
1065 break;
1066
1068 scanner_.ignore_line ();
1069 break;
1070
1072 scanner_.ignore_line ();
1073 process_chars();
1074 break;
1075 case Scanner::tok_width:
1076 // ignore the width section of the character map
1077 while ((next = scanner_.next_token ()).token != Scanner::tok_width);
1078 break;
1079
1080 case Scanner::tok_nl:
1081 break;
1082
1083 default:
1084 issue_diag (E_SYNTAX, false, &next,
1085 "unknown token %s in character map file\n",
1086 next.name.c_str ());
1087 }
1088 }
1089}
const char xdigits[]
void count(T, T, const U &,...)
#define i
Definition 17.names.cpp:126
#define nchars
Definition 17.names.cpp:502
#define res
Definition 17.names.cpp:531
#define j
Definition 17.names.cpp:127
#define val
Definition 17.names.cpp:570
#define pos
Definition 17.names.cpp:518
#define U
Definition 17.names.cpp:164
const int end
#define assert(expr)
Definition _cassert.h:40
#define LC_CTYPE
unsigned short wchar_t
#define _RWSTD_PATH_SEP
Definition _config.h:297
#define _RWSTD_REINTERPRET_CAST(T, x)
Definition _defs.h:535
#define _RWSTD_CONST_CAST(T, x)
Definition _defs.h:536
void get_cname_aliases(const std::string &name, StringVector &aliases)
Definition aliases.cpp:392
unsigned int get_codepage(const std::string &)
#define MAX_SYM_NAME_LEN
Definition charmap.cpp:68
bool convert_to_wc(const std::string &, const std::string &, wchar_t &)
Definition charmap.cpp:394
Charmap(const char *, const char *, bool, bool, bool, bool)
Definition charmap.cpp:993
static const char *const portable_charset[]
Definition charmap.h:261
std::string get_charmap_name() const
Definition charmap.cpp:475
bool convert_to_ucs(const std::string &, const std::string &, wchar_t &)
Definition charmap.cpp:664
wchar_t convert_sym_to_ucs(const std::string &) const
Definition charmap.cpp:641
std::size_t mbcharlen(const std::string &) const
Definition charmap.cpp:241
wchar_t increment_wchar(wchar_t) const
Definition charmap.cpp:486
void open(std::string, char='#', char='\\')
Definition scanner.cpp:142
char escape_char() const
Definition scanner.cpp:129
void ignore_line()
Definition scanner.cpp:135
unsigned long convert_escape(const char *, const char **=0, bool=false) const
Definition scanner.cpp:705
token_t next_token()
Definition scanner.cpp:352
@ tok_end
Definition scanner.h:66
@ tok_sym_name
Definition scanner.h:161
@ tok_string
Definition scanner.h:99
@ tok_mb_cur_min
Definition scanner.h:49
@ tok_code_set_name
Definition scanner.h:47
@ tok_ndef
Definition scanner.h:169
@ tok_mb_cur_max
Definition scanner.h:48
@ tok_abs_ellipsis
Definition scanner.h:103
@ tok_nl
Definition scanner.h:168
@ tok_char_value
Definition scanner.h:162
@ tok_end_tokens
Definition scanner.h:170
@ tok_charmap
Definition scanner.h:51
@ tok_width
Definition scanner.h:107
@ tok_hex_ellipsis
Definition scanner.h:104
bool issue_diag(int type, bool, const Scanner::token_t *token, const char *fmt,...)
@ W_ICONV
Definition diagnostic.h:68
@ E_MBCHAR
Definition diagnostic.h:45
@ E_RANGE
Definition diagnostic.h:48
@ E_UCS
Definition diagnostic.h:46
@ I_STAGE
Definition diagnostic.h:81
@ W_NOPCS
Definition diagnostic.h:64
@ E_SYNTAX
Definition diagnostic.h:49
@ W_CALL
Definition diagnostic.h:75
const char * str
Definition exec.cpp:170
#define UCHAR_MAX
Definition limits.h:60
unsigned char UChar
Definition collate.cpp:122
std::string name
Definition scanner.h:178
token_id token
Definition scanner.h:179