Wolfgang Thallergcc16 merge, part 27672bd75 months agoHistory
3590 lines · 105.5 KB · C++
RawDownload
/* CPP Library - charsets   Copyright (C) 1998-2026 Free Software Foundation, Inc.    Broken out of c-lex.cc Apr 2003, adding valid C99 UCN ranges. This program is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 3, or (at your option) anylater version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details. You should have received a copy of the GNU General Public Licensealong with this program; see the file COPYING3.  If not see<http://www.gnu.org/licenses/>.  */ #include "config.h"#include "system.h"#include "cpplib.h"#include "internal.h" /* Character set handling for C-family languages.    Terminological note: In what follows, "charset" or "character set"   will be taken to mean both an abstract set of characters and an   encoding for that set.    The C99 standard discusses two character sets: source and execution.   The source character set is used for internal processing in translation   phases 1 through 4; the execution character set is used thereafter.   Both are required by 5.2.1.2p1 to be multibyte encodings, not wide   character encodings (see 3.7.2, 3.7.3 for the standardese meanings   of these terms).  Furthermore, the "basic character set" (listed in   5.2.1p3) is to be encoded in each with values one byte wide, and is   to appear in the initial shift state.    It is not explicitly mentioned, but there is also a "wide execution   character set" used to encode wide character constants and wide   string literals; this is supposed to be the result of applying the   standard library function mbstowcs() to an equivalent narrow string   (6.4.5p5).  However, the behavior of hexadecimal and octal   \-escapes is at odds with this; they are supposed to be translated   directly to wchar_t values (6.4.4.4p5,6).    The source character set is not necessarily the character set used   to encode physical source files on disk; translation phase 1 converts   from whatever that encoding is to the source character set.    The presence of universal character names in C99 (6.4.3 et seq.)   forces the source character set to be isomorphic to ISO 10646,   that is, Unicode.  There is no such constraint on the execution   character set; note also that the conversion from source to   execution character set does not occur for identifiers (5.1.1.2p1#5).    For convenience of implementation, the source character set's   encoding of the basic character set should be identical to the   execution character set OF THE HOST SYSTEM's encoding of the basic   character set, and it should not be a state-dependent encoding.    cpplib uses UTF-8 or UTF-EBCDIC for the source character set,   depending on whether the host is based on ASCII or EBCDIC (see   respectively Unicode section 2.3/ISO10646 Amendment 2, and Unicode   Technical Report #16).  With limited exceptions, it relies on the   system library's iconv() primitive to do charset conversion   (specified in SUSv2).  */ #if !HAVE_ICONV/* Make certain that the uses of iconv(), iconv_open(), iconv_close()   below, which are guarded only by if statements with compile-time   constant conditions, do not cause link errors.  */#define iconv_open(x, y) (errno = EINVAL, (iconv_t)-1)#define iconv(a,b,c,d,e) (errno = EINVAL, (size_t)-1)#define iconv_close(x)   (void)0#define ICONV_CONST#endif #if HOST_CHARSET == HOST_CHARSET_ASCII#define SOURCE_CHARSET "UTF-8"#define LAST_POSSIBLY_BASIC_SOURCE_CHAR 0x7e#elif HOST_CHARSET == HOST_CHARSET_EBCDIC#define SOURCE_CHARSET "UTF-EBCDIC"#define LAST_POSSIBLY_BASIC_SOURCE_CHAR 0xFF#else#error "Unrecognized basic host character set"#endif #ifndef EILSEQ#define EILSEQ EINVAL#endif /* This structure is used for a resizable string buffer throughout.  *//* Don't call it strbuf, as that conflicts with unistd.h on systems   such as DYNIX/ptx where unistd.h includes stropts.h.  */struct _cpp_strbuf{  uchar *text;  size_t asize;  size_t len;}; /* This is enough to hold any string that fits on a single 80-column   line, even if iconv quadruples its size (e.g. conversion from   ASCII to UTF-32) rounded up to a power of two.  */#define OUTBUF_BLOCK_SIZE 256 /* Conversions between UTF-8 and UTF-16/32 are implemented by custom   logic.  This is because a depressing number of systems lack iconv,   or have have iconv libraries that do not do these conversions, so   we need a fallback implementation for them.  To ensure the fallback   doesn't break due to neglect, it is used on all systems.    UTF-32 encoding is nice and simple: a four-byte binary number,   constrained to the range 00000000-7FFFFFFF to avoid questions of   signedness.  We do have to cope with big- and little-endian   variants.    UTF-16 encoding uses two-byte binary numbers, again in big- and   little-endian variants, for all values in the 00000000-0000FFFF   range.  Values in the 00010000-0010FFFF range are encoded as pairs   of two-byte numbers, called "surrogate pairs": given a number S in   this range, it is mapped to a pair (H, L) as follows:      H = (S - 0x10000) / 0x400 + 0xD800     L = (S - 0x10000) % 0x400 + 0xDC00    Two-byte values in the D800...DFFF range are ill-formed except as a   component of a surrogate pair.  Even if the encoding within a   two-byte value is little-endian, the H member of the surrogate pair   comes first.    There is no way to encode values in the 00110000-7FFFFFFF range,   which is not currently a problem as there are no assigned code   points in that range; however, the author expects that it will   eventually become necessary to abandon UTF-16 due to this   limitation.  Note also that, because of these pairs, UTF-16 does   not meet the requirements of the C standard for a wide character   encoding (see 3.7.3 and 6.4.4.4p11).    UTF-8 encoding looks like this:    value range	       encoded as   00000000-0000007F   0xxxxxxx   00000080-000007FF   110xxxxx 10xxxxxx   00000800-0000FFFF   1110xxxx 10xxxxxx 10xxxxxx   00010000-001FFFFF   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   00200000-03FFFFFF   111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx   04000000-7FFFFFFF   1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx    Values in the 0000D800 ... 0000DFFF range (surrogates) are invalid,   which means that three-byte sequences ED xx yy, with A0 <= xx <= BF,   never occur.  Note also that any value that can be encoded by a   given row of the table can also be encoded by all successive rows,   but this is not done; only the shortest possible encoding for any   given value is valid.  For instance, the character 07C0 could be   encoded as any of DF 80, E0 9F 80, F0 80 9F 80, F8 80 80 9F 80, or   FC 80 80 80 9F 80.  Only the first is valid.    An implementation note: the transformation from UTF-16 to UTF-8, or   vice versa, is easiest done by using UTF-32 as an intermediary.  */ /* Internal primitives which go from an UTF-8 byte stream to native-endian   UTF-32 in a cppchar_t, or vice versa; this avoids an extra marshal/unmarshal   operation in several places below.  */static inline intone_utf8_to_cppchar (const uchar **inbufp, size_t *inbytesleftp,		     cppchar_t *cp){  static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };  static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };   cppchar_t c;  const uchar *inbuf = *inbufp;  size_t nbytes, i;   if (*inbytesleftp < 1)    return EINVAL;   c = *inbuf;  if (c < 0x80)    {      *cp = c;      *inbytesleftp -= 1;      *inbufp += 1;      return 0;    }   /* The number of leading 1-bits in the first byte indicates how many     bytes follow.  */  for (nbytes = 2; nbytes < 7; nbytes++)    if ((c & ~masks[nbytes-1]) == patns[nbytes-1])      goto found;  return EILSEQ; found:   if (*inbytesleftp < nbytes)    return EINVAL;   c = (c & masks[nbytes-1]);  inbuf++;  for (i = 1; i < nbytes; i++)    {      cppchar_t n = *inbuf++;      if ((n & 0xC0) != 0x80)	return EILSEQ;      c = ((c << 6) + (n & 0x3F));    }   /* Make sure the shortest possible encoding was used.  */  if (c <=      0x7F && nbytes > 1) return EILSEQ;  if (c <=     0x7FF && nbytes > 2) return EILSEQ;  if (c <=    0xFFFF && nbytes > 3) return EILSEQ;  if (c <=  0x1FFFFF && nbytes > 4) return EILSEQ;  if (c <= 0x3FFFFFF && nbytes > 5) return EILSEQ;   /* Make sure the character is valid.  */  if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF)) return EILSEQ;   *cp = c;  *inbufp = inbuf;  *inbytesleftp -= nbytes;  return 0;} static inline intone_cppchar_to_utf8 (cppchar_t c, uchar **outbufp, size_t *outbytesleftp){  static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };  static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };  size_t nbytes;  uchar buf[6], *p = &buf[6];  uchar *outbuf = *outbufp;   nbytes = 1;  if (c < 0x80)    *--p = c;  else    {      do	{	  *--p = ((c & 0x3F) | 0x80);	  c >>= 6;	  nbytes++;	}      while (c >= 0x3F || (c & limits[nbytes-1]));      *--p = (c | masks[nbytes-1]);    }   if (*outbytesleftp < nbytes)    return E2BIG;   while (p < &buf[6])    *outbuf++ = *p++;  *outbytesleftp -= nbytes;  *outbufp = outbuf;  return 0;} /* The following four functions transform one character between the two   encodings named in the function name.  All have the signature   int (*)(iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,           uchar **outbufp, size_t *outbytesleftp)    BIGEND must have the value 0 or 1, coerced to (iconv_t); it is   interpreted as a boolean indicating whether big-endian or   little-endian encoding is to be used for the member of the pair   that is not UTF-8.    INBUFP, INBYTESLEFTP, OUTBUFP, OUTBYTESLEFTP work exactly as they   do for iconv.    The return value is either 0 for success, or an errno value for   failure, which may be E2BIG (need more space), EILSEQ (ill-formed   input sequence), ir EINVAL (incomplete input sequence).  */ static inline intone_utf8_to_utf32 (iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,		   uchar **outbufp, size_t *outbytesleftp){  uchar *outbuf;  cppchar_t s = 0;  int rval;   /* Check for space first, since we know exactly how much we need.  */  if (*outbytesleftp < 4)    return E2BIG;   rval = one_utf8_to_cppchar (inbufp, inbytesleftp, &s);  if (rval)    return rval;   outbuf = *outbufp;  outbuf[bigend ? 3 : 0] = (s & 0x000000FF);  outbuf[bigend ? 2 : 1] = (s & 0x0000FF00) >> 8;  outbuf[bigend ? 1 : 2] = (s & 0x00FF0000) >> 16;  outbuf[bigend ? 0 : 3] = (s & 0xFF000000) >> 24;   *outbufp += 4;  *outbytesleftp -= 4;  return 0;} static inline intone_utf32_to_utf8 (iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,		   uchar **outbufp, size_t *outbytesleftp){  cppchar_t s;  int rval;  const uchar *inbuf;   if (*inbytesleftp < 4)    return EINVAL;   inbuf = *inbufp;   s  = inbuf[bigend ? 0 : 3] << 24;  s += inbuf[bigend ? 1 : 2] << 16;  s += inbuf[bigend ? 2 : 1] << 8;  s += inbuf[bigend ? 3 : 0];   if (s >= 0x7FFFFFFF || (s >= 0xD800 && s <= 0xDFFF))    return EILSEQ;   rval = one_cppchar_to_utf8 (s, outbufp, outbytesleftp);  if (rval)    return rval;   *inbufp += 4;  *inbytesleftp -= 4;  return 0;} static inline intone_utf8_to_utf16 (iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,		   uchar **outbufp, size_t *outbytesleftp){  int rval;  cppchar_t s = 0;  const uchar *save_inbuf = *inbufp;  size_t save_inbytesleft = *inbytesleftp;  uchar *outbuf = *outbufp;   rval = one_utf8_to_cppchar (inbufp, inbytesleftp, &s);  if (rval)    return rval;   if (s > 0x0010FFFF)    {      *inbufp = save_inbuf;      *inbytesleftp = save_inbytesleft;      return EILSEQ;    }   if (s <= 0xFFFF)    {      if (*outbytesleftp < 2)	{	  *inbufp = save_inbuf;	  *inbytesleftp = save_inbytesleft;	  return E2BIG;	}      outbuf[bigend ? 1 : 0] = (s & 0x00FF);      outbuf[bigend ? 0 : 1] = (s & 0xFF00) >> 8;       *outbufp += 2;      *outbytesleftp -= 2;      return 0;    }  else    {      cppchar_t hi, lo;       if (*outbytesleftp < 4)	{	  *inbufp = save_inbuf;	  *inbytesleftp = save_inbytesleft;	  return E2BIG;	}       hi = (s - 0x10000) / 0x400 + 0xD800;      lo = (s - 0x10000) % 0x400 + 0xDC00;       /* Even if we are little-endian, put the high surrogate first.	 ??? Matches practice?  */      outbuf[bigend ? 1 : 0] = (hi & 0x00FF);      outbuf[bigend ? 0 : 1] = (hi & 0xFF00) >> 8;      outbuf[bigend ? 3 : 2] = (lo & 0x00FF);      outbuf[bigend ? 2 : 3] = (lo & 0xFF00) >> 8;       *outbufp += 4;      *outbytesleftp -= 4;      return 0;    }} static inline intone_utf16_to_utf8 (iconv_t bigend, const uchar **inbufp, size_t *inbytesleftp,		   uchar **outbufp, size_t *outbytesleftp){  cppchar_t s;  const uchar *inbuf = *inbufp;  int rval;   if (*inbytesleftp < 2)    return EINVAL;  s  = inbuf[bigend ? 0 : 1] << 8;  s += inbuf[bigend ? 1 : 0];   /* Low surrogate without immediately preceding high surrogate is invalid.  */  if (s >= 0xDC00 && s <= 0xDFFF)    return EILSEQ;  /* High surrogate must have a following low surrogate.  */  else if (s >= 0xD800 && s <= 0xDBFF)    {      cppchar_t hi = s, lo;      if (*inbytesleftp < 4)	return EINVAL;       lo  = inbuf[bigend ? 2 : 3] << 8;      lo += inbuf[bigend ? 3 : 2];       if (lo < 0xDC00 || lo > 0xDFFF)	return EILSEQ;       s = (hi - 0xD800) * 0x400 + (lo - 0xDC00) + 0x10000;    }   rval = one_cppchar_to_utf8 (s, outbufp, outbytesleftp);  if (rval)    return rval;   /* Success - update the input pointers (one_cppchar_to_utf8 has done     the output pointers for us).  */  if (s <= 0xFFFF)    {      *inbufp += 2;      *inbytesleftp -= 2;    }  else    {      *inbufp += 4;      *inbytesleftp -= 4;    }  return 0;}  /* Special routine which just counts number of characters in the   string, what exactly is stored into the output doesn't matter   as long as it is one uchar per character.  */ static inline intone_count_chars (iconv_t, const uchar **inbufp, size_t *inbytesleftp,		 uchar **outbufp, size_t *outbytesleftp){  cppchar_t s = 0;  int rval;   /* Check for space first, since we know exactly how much we need.  */  if (*outbytesleftp < 1)    return E2BIG; #if HOST_CHARSET == HOST_CHARSET_ASCII  rval = one_utf8_to_cppchar (inbufp, inbytesleftp, &s);  if (rval)    return rval;#else  if (*inbytesleftp < 1)    return EINVAL;  static const uchar utf_ebcdic_map[256] = {    /* See table 4 in http://unicode.org/reports/tr16/tr16-7.2.html  */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1,    1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1, 1,    1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1,    9, 9, 9, 9, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,    2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,    2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,    2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2,    2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 1, 3, 3,    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3,    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 4,    1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 5, 5, 5,    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 6, 6, 7, 7, 0  };  rval = utf_ebcdic_map[**inbufp];  if (rval == 9)    return EILSEQ;  if (rval == 0)    rval = 1;  if (rval >= 2)    {      if (*inbytesleftp < rval)	return EINVAL;      for (int i = 1; i < rval; ++i)

Lines 1–500 of 3590.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t