Rationalize code placement between wchar.c, encnames.c, and mbutils.c.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Jan 2020 23:08:21 +0000 (18:08 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Jan 2020 23:08:21 +0000 (18:08 -0500)
Move all the backend-only code that'd crept into wchar.c and encnames.c
into mbutils.c.

To remove the last few #ifdef dependencies from wchar.c and encnames.c,
also make the following changes:

* Adjust get_encoding_name_for_icu to return NULL, not throw an error,
for unsupported encodings.  Its sole caller can perfectly well throw an
error instead.  (While at it, I also made this function and its sibling
is_encoding_supported_by_icu proof against out-of-range encoding IDs.)

* Remove the overlength-name error condition from pg_char_to_encoding.
It's completely silly not to treat that just like any other
the-name-is-not-in-the-table case.

Also, get rid of pg_mic_mblen --- there's no obvious reason why
conv.c shouldn't call pg_mule_mblen instead.

Other than that, this is just code movement and comment-polishing with
no functional changes.  Notably, I reordered declarations in pg_wchar.h
to show which functions are frontend-accessible and which are not.

Discussion: https://postgr.es/m/CA+TgmoYO8oq-iy8E02rD8eX25T-9SmyxKWqqks5OMHxKvGXpXQ@mail.gmail.com

src/backend/utils/adt/pg_locale.c
src/backend/utils/mb/conv.c
src/backend/utils/mb/mbutils.c
src/common/encnames.c
src/common/wchar.c
src/include/mb/pg_wchar.h

index 0eb915e62e9fb38bcea00c416f15ace2fabf252f..25fb7e2ebfa120c735b28bf14d01eeb310e96c30 100644 (file)
@@ -1555,9 +1555,14 @@ init_icu_converter(void)
        UConverter *conv;
 
        if (icu_converter)
-               return;
+               return;                                 /* already done */
 
        icu_encoding_name = get_encoding_name_for_icu(GetDatabaseEncoding());
+       if (!icu_encoding_name)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("encoding \"%s\" not supported by ICU",
+                                               pg_encoding_to_char(GetDatabaseEncoding()))));
 
        status = U_ZERO_ERROR;
        conv = ucnv_open(icu_encoding_name, &status);
index 11f17b337e6511bbff8d9e363f5f34d3a8570afd..54dcf71fb75625163fccb7d29c793999ebfc9b74 100644 (file)
@@ -115,7 +115,7 @@ mic2latin(const unsigned char *mic, unsigned char *p, int len,
                }
                else
                {
-                       int                     l = pg_mic_mblen(mic);
+                       int                     l = pg_mule_mblen(mic);
 
                        if (len < l)
                                report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic,
@@ -217,7 +217,7 @@ mic2latin_with_table(const unsigned char *mic,
                }
                else
                {
-                       int                     l = pg_mic_mblen(mic);
+                       int                     l = pg_mule_mblen(mic);
 
                        if (len < l)
                                report_invalid_encoding(PG_MULE_INTERNAL, (const char *) mic,
index 5d7cc74ad6c869e8011354d50fa7ba92a462ac62..86787bcb3190a0048bf7cff7eccaf6c3f1053732 100644 (file)
@@ -1066,6 +1066,23 @@ pg_client_encoding(PG_FUNCTION_ARGS)
        return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
 }
 
+Datum
+PG_char_to_encoding(PG_FUNCTION_ARGS)
+{
+       Name            s = PG_GETARG_NAME(0);
+
+       PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s)));
+}
+
+Datum
+PG_encoding_to_char(PG_FUNCTION_ARGS)
+{
+       int32           encoding = PG_GETARG_INT32(0);
+       const char *encoding_name = pg_encoding_to_char(encoding);
+
+       return DirectFunctionCall1(namein, CStringGetDatum(encoding_name));
+}
+
 /*
  * gettext() returns messages in this encoding.  This often matches the
  * database encoding, but it differs for SQL_ASCII databases, for processes
@@ -1078,6 +1095,438 @@ GetMessageEncoding(void)
        return MessageEncoding->encoding;
 }
 
+
+/*
+ * Generic character incrementer function.
+ *
+ * Not knowing anything about the properties of the encoding in use, we just
+ * keep incrementing the last byte until we get a validly-encoded result,
+ * or we run out of values to try.  We don't bother to try incrementing
+ * higher-order bytes, so there's no growth in runtime for wider characters.
+ * (If we did try to do that, we'd need to consider the likelihood that 255
+ * is not a valid final byte in the encoding.)
+ */
+static bool
+pg_generic_charinc(unsigned char *charptr, int len)
+{
+       unsigned char *lastbyte = charptr + len - 1;
+       mbverifier      mbverify;
+
+       /* We can just invoke the character verifier directly. */
+       mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverify;
+
+       while (*lastbyte < (unsigned char) 255)
+       {
+               (*lastbyte)++;
+               if ((*mbverify) (charptr, len) == len)
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * UTF-8 character incrementer function.
+ *
+ * For a one-byte character less than 0x7F, we just increment the byte.
+ *
+ * For a multibyte character, every byte but the first must fall between 0x80
+ * and 0xBF; and the first byte must be between 0xC0 and 0xF4.  We increment
+ * the last byte that's not already at its maximum value.  If we can't find a
+ * byte that's less than the maximum allowable value, we simply fail.  We also
+ * need some special-case logic to skip regions used for surrogate pair
+ * handling, as those should not occur in valid UTF-8.
+ *
+ * Note that we don't reset lower-order bytes back to their minimums, since
+ * we can't afford to make an exhaustive search (see make_greater_string).
+ */
+static bool
+pg_utf8_increment(unsigned char *charptr, int length)
+{
+       unsigned char a;
+       unsigned char limit;
+
+       switch (length)
+       {
+               default:
+                       /* reject lengths 5 and 6 for now */
+                       return false;
+               case 4:
+                       a = charptr[3];
+                       if (a < 0xBF)
+                       {
+                               charptr[3]++;
+                               break;
+                       }
+                       /* FALL THRU */
+               case 3:
+                       a = charptr[2];
+                       if (a < 0xBF)
+                       {
+                               charptr[2]++;
+                               break;
+                       }
+                       /* FALL THRU */
+               case 2:
+                       a = charptr[1];
+                       switch (*charptr)
+                       {
+                               case 0xED:
+                                       limit = 0x9F;
+                                       break;
+                               case 0xF4:
+                                       limit = 0x8F;
+                                       break;
+                               default:
+                                       limit = 0xBF;
+                                       break;
+                       }
+                       if (a < limit)
+                       {
+                               charptr[1]++;
+                               break;
+                       }
+                       /* FALL THRU */
+               case 1:
+                       a = *charptr;
+                       if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF4)
+                               return false;
+                       charptr[0]++;
+                       break;
+       }
+
+       return true;
+}
+
+/*
+ * EUC-JP character incrementer function.
+ *
+ * If the sequence starts with SS2 (0x8e), it must be a two-byte sequence
+ * representing JIS X 0201 characters with the second byte ranging between
+ * 0xa1 and 0xdf.  We just increment the last byte if it's less than 0xdf,
+ * and otherwise rewrite the whole sequence to 0xa1 0xa1.
+ *
+ * If the sequence starts with SS3 (0x8f), it must be a three-byte sequence
+ * in which the last two bytes range between 0xa1 and 0xfe.  The last byte
+ * is incremented if possible, otherwise the second-to-last byte.
+ *
+ * If the sequence starts with a value other than the above and its MSB
+ * is set, it must be a two-byte sequence representing JIS X 0208 characters
+ * with both bytes ranging between 0xa1 and 0xfe.  The last byte is
+ * incremented if possible, otherwise the second-to-last byte.
+ *
+ * Otherwise, the sequence is a single-byte ASCII character. It is
+ * incremented up to 0x7f.
+ */
+static bool
+pg_eucjp_increment(unsigned char *charptr, int length)
+{
+       unsigned char c1,
+                               c2;
+       int                     i;
+
+       c1 = *charptr;
+
+       switch (c1)
+       {
+               case SS2:                               /* JIS X 0201 */
+                       if (length != 2)
+                               return false;
+
+                       c2 = charptr[1];
+
+                       if (c2 >= 0xdf)
+                               charptr[0] = charptr[1] = 0xa1;
+                       else if (c2 < 0xa1)
+                               charptr[1] = 0xa1;
+                       else
+                               charptr[1]++;
+                       break;
+
+               case SS3:                               /* JIS X 0212 */
+                       if (length != 3)
+                               return false;
+
+                       for (i = 2; i > 0; i--)
+                       {
+                               c2 = charptr[i];
+                               if (c2 < 0xa1)
+                               {
+                                       charptr[i] = 0xa1;
+                                       return true;
+                               }
+                               else if (c2 < 0xfe)
+                               {
+                                       charptr[i]++;
+                                       return true;
+                               }
+                       }
+
+                       /* Out of 3-byte code region */
+                       return false;
+
+               default:
+                       if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
+                       {
+                               if (length != 2)
+                                       return false;
+
+                               for (i = 1; i >= 0; i--)
+                               {
+                                       c2 = charptr[i];
+                                       if (c2 < 0xa1)
+                                       {
+                                               charptr[i] = 0xa1;
+                                               return true;
+                                       }
+                                       else if (c2 < 0xfe)
+                                       {
+                                               charptr[i]++;
+                                               return true;
+                                       }
+                               }
+
+                               /* Out of 2 byte code region */
+                               return false;
+                       }
+                       else
+                       {                                       /* ASCII, single byte */
+                               if (c1 > 0x7e)
+                                       return false;
+                               (*charptr)++;
+                       }
+                       break;
+       }
+
+       return true;
+}
+
+/*
+ * get the character incrementer for the encoding for the current database
+ */
+mbcharacter_incrementer
+pg_database_encoding_character_incrementer(void)
+{
+       /*
+        * Eventually it might be best to add a field to pg_wchar_table[], but for
+        * now we just use a switch.
+        */
+       switch (GetDatabaseEncoding())
+       {
+               case PG_UTF8:
+                       return pg_utf8_increment;
+
+               case PG_EUC_JP:
+                       return pg_eucjp_increment;
+
+               default:
+                       return pg_generic_charinc;
+       }
+}
+
+/*
+ * fetch maximum length of the encoding for the current database
+ */
+int
+pg_database_encoding_max_length(void)
+{
+       return pg_wchar_table[GetDatabaseEncoding()].maxmblen;
+}
+
+/*
+ * Verify mbstr to make sure that it is validly encoded in the current
+ * database encoding.  Otherwise same as pg_verify_mbstr().
+ */
+bool
+pg_verifymbstr(const char *mbstr, int len, bool noError)
+{
+       return
+               pg_verify_mbstr_len(GetDatabaseEncoding(), mbstr, len, noError) >= 0;
+}
+
+/*
+ * Verify mbstr to make sure that it is validly encoded in the specified
+ * encoding.
+ */
+bool
+pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
+{
+       return pg_verify_mbstr_len(encoding, mbstr, len, noError) >= 0;
+}
+
+/*
+ * Verify mbstr to make sure that it is validly encoded in the specified
+ * encoding.
+ *
+ * mbstr is not necessarily zero terminated; length of mbstr is
+ * specified by len.
+ *
+ * If OK, return length of string in the encoding.
+ * If a problem is found, return -1 when noError is
+ * true; when noError is false, ereport() a descriptive message.
+ */
+int
+pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError)
+{
+       mbverifier      mbverify;
+       int                     mb_len;
+
+       Assert(PG_VALID_ENCODING(encoding));
+
+       /*
+        * In single-byte encodings, we need only reject nulls (\0).
+        */
+       if (pg_encoding_max_length(encoding) <= 1)
+       {
+               const char *nullpos = memchr(mbstr, 0, len);
+
+               if (nullpos == NULL)
+                       return len;
+               if (noError)
+                       return -1;
+               report_invalid_encoding(encoding, nullpos, 1);
+       }
+
+       /* fetch function pointer just once */
+       mbverify = pg_wchar_table[encoding].mbverify;
+
+       mb_len = 0;
+
+       while (len > 0)
+       {
+               int                     l;
+
+               /* fast path for ASCII-subset characters */
+               if (!IS_HIGHBIT_SET(*mbstr))
+               {
+                       if (*mbstr != '\0')
+                       {
+                               mb_len++;
+                               mbstr++;
+                               len--;
+                               continue;
+                       }
+                       if (noError)
+                               return -1;
+                       report_invalid_encoding(encoding, mbstr, len);
+               }
+
+               l = (*mbverify) ((const unsigned char *) mbstr, len);
+
+               if (l < 0)
+               {
+                       if (noError)
+                               return -1;
+                       report_invalid_encoding(encoding, mbstr, len);
+               }
+
+               mbstr += l;
+               len -= l;
+               mb_len++;
+       }
+       return mb_len;
+}
+
+/*
+ * check_encoding_conversion_args: check arguments of a conversion function
+ *
+ * "expected" arguments can be either an encoding ID or -1 to indicate that
+ * the caller will check whether it accepts the ID.
+ *
+ * Note: the errors here are not really user-facing, so elog instead of
+ * ereport seems sufficient.  Also, we trust that the "expected" encoding
+ * arguments are valid encoding IDs, but we don't trust the actuals.
+ */
+void
+check_encoding_conversion_args(int src_encoding,
+                                                          int dest_encoding,
+                                                          int len,
+                                                          int expected_src_encoding,
+                                                          int expected_dest_encoding)
+{
+       if (!PG_VALID_ENCODING(src_encoding))
+               elog(ERROR, "invalid source encoding ID: %d", src_encoding);
+       if (src_encoding != expected_src_encoding && expected_src_encoding >= 0)
+               elog(ERROR, "expected source encoding \"%s\", but got \"%s\"",
+                        pg_enc2name_tbl[expected_src_encoding].name,
+                        pg_enc2name_tbl[src_encoding].name);
+       if (!PG_VALID_ENCODING(dest_encoding))
+               elog(ERROR, "invalid destination encoding ID: %d", dest_encoding);
+       if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0)
+               elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"",
+                        pg_enc2name_tbl[expected_dest_encoding].name,
+                        pg_enc2name_tbl[dest_encoding].name);
+       if (len < 0)
+               elog(ERROR, "encoding conversion length must not be negative");
+}
+
+/*
+ * report_invalid_encoding: complain about invalid multibyte character
+ *
+ * note: len is remaining length of string, not length of character;
+ * len must be greater than zero, as we always examine the first byte.
+ */
+void
+report_invalid_encoding(int encoding, const char *mbstr, int len)
+{
+       int                     l = pg_encoding_mblen(encoding, mbstr);
+       char            buf[8 * 5 + 1];
+       char       *p = buf;
+       int                     j,
+                               jlimit;
+
+       jlimit = Min(l, len);
+       jlimit = Min(jlimit, 8);        /* prevent buffer overrun */
+
+       for (j = 0; j < jlimit; j++)
+       {
+               p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
+               if (j < jlimit - 1)
+                       p += sprintf(p, " ");
+       }
+
+       ereport(ERROR,
+                       (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
+                        errmsg("invalid byte sequence for encoding \"%s\": %s",
+                                       pg_enc2name_tbl[encoding].name,
+                                       buf)));
+}
+
+/*
+ * report_untranslatable_char: complain about untranslatable character
+ *
+ * note: len is remaining length of string, not length of character;
+ * len must be greater than zero, as we always examine the first byte.
+ */
+void
+report_untranslatable_char(int src_encoding, int dest_encoding,
+                                                  const char *mbstr, int len)
+{
+       int                     l = pg_encoding_mblen(src_encoding, mbstr);
+       char            buf[8 * 5 + 1];
+       char       *p = buf;
+       int                     j,
+                               jlimit;
+
+       jlimit = Min(l, len);
+       jlimit = Min(jlimit, 8);        /* prevent buffer overrun */
+
+       for (j = 0; j < jlimit; j++)
+       {
+               p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
+               if (j < jlimit - 1)
+                       p += sprintf(p, " ");
+       }
+
+       ereport(ERROR,
+                       (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
+                        errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"",
+                                       buf,
+                                       pg_enc2name_tbl[src_encoding].name,
+                                       pg_enc2name_tbl[dest_encoding].name)));
+}
+
+
 #ifdef WIN32
 /*
  * Convert from MessageEncoding to a palloc'ed, null-terminated utf16
@@ -1149,4 +1598,4 @@ pgwin32_message_to_UTF16(const char *str, int len, int *utf16len)
        return utf16;
 }
 
-#endif
+#endif                                                 /* WIN32 */
index 2086e007fc547b9be20ace86c76f0dc432a2ab0b..14cf1b39e9866fb2190b28b9b9a7fc071634ea25 100644 (file)
  *
  *-------------------------------------------------------------------------
  */
-#ifdef FRONTEND
-#include "postgres_fe.h"
-#else
-#include "postgres.h"
-#include "utils/builtins.h"
-#endif
+#include "c.h"
 
 #include <ctype.h>
 #include <unistd.h>
@@ -310,6 +305,7 @@ static const pg_encname pg_encname_tbl[] =
 #else
 #define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage }
 #endif
+
 const pg_enc2name pg_enc2name_tbl[] =
 {
        DEF_ENC2NAME(SQL_ASCII, 0),
@@ -409,10 +405,8 @@ const pg_enc2gettext pg_enc2gettext_tbl[] =
 };
 
 
-#ifndef FRONTEND
-
 /*
- * Table of encoding names for ICU
+ * Table of encoding names for ICU (currently covers backend encodings only)
  *
  * Reference: <https://ssl.icu-project.org/icu-bin/convexp>
  *
@@ -457,33 +451,32 @@ static const char *const pg_enc2icu_tbl[] =
        "KOI8-U",                                       /* PG_KOI8U */
 };
 
+
+/*
+ * Is this encoding supported by ICU?
+ */
 bool
 is_encoding_supported_by_icu(int encoding)
 {
+       if (!PG_VALID_BE_ENCODING(encoding))
+               return false;
        return (pg_enc2icu_tbl[encoding] != NULL);
 }
 
+/*
+ * Returns ICU's name for encoding, or NULL if not supported
+ */
 const char *
 get_encoding_name_for_icu(int encoding)
 {
-       const char *icu_encoding_name;
-
        StaticAssertStmt(lengthof(pg_enc2icu_tbl) == PG_ENCODING_BE_LAST + 1,
                                         "pg_enc2icu_tbl incomplete");
 
-       icu_encoding_name = pg_enc2icu_tbl[encoding];
-
-       if (!icu_encoding_name)
-               ereport(ERROR,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("encoding \"%s\" not supported by ICU",
-                                               pg_encoding_to_char(encoding))));
-
-       return icu_encoding_name;
+       if (!PG_VALID_BE_ENCODING(encoding))
+               return NULL;
+       return pg_enc2icu_tbl[encoding];
 }
 
-#endif                                                 /* not FRONTEND */
-
 
 /* ----------
  * Encoding checks, for error returns -1 else encoding id
@@ -523,9 +516,10 @@ pg_valid_server_encoding_id(int encoding)
        return PG_VALID_BE_ENCODING(encoding);
 }
 
-/* ----------
- * Remove irrelevant chars from encoding name
- * ----------
+/*
+ * Remove irrelevant chars from encoding name, store at *newkey
+ *
+ * (Caller's responsibility to provide a large enough buffer)
  */
 static char *
 clean_encoding_name(const char *key, char *newkey)
@@ -547,11 +541,10 @@ clean_encoding_name(const char *key, char *newkey)
        return newkey;
 }
 
-/* ----------
+/*
  * Search encoding by encoding name
  *
- * Returns encoding ID, or -1 for error
- * ----------
+ * Returns encoding ID, or -1 if not recognized
  */
 int
 pg_char_to_encoding(const char *name)
@@ -568,16 +561,8 @@ pg_char_to_encoding(const char *name)
                return -1;
 
        if (strlen(name) >= NAMEDATALEN)
-       {
-#ifdef FRONTEND
-               fprintf(stderr, "encoding name too long\n");
-               return -1;
-#else
-               ereport(ERROR,
-                               (errcode(ERRCODE_NAME_TOO_LONG),
-                                errmsg("encoding name too long")));
-#endif
-       }
+               return -1;                              /* it's certainly not in the table */
+
        key = clean_encoding_name(name, buff);
 
        while (last >= base)
@@ -599,16 +584,6 @@ pg_char_to_encoding(const char *name)
        return -1;
 }
 
-#ifndef FRONTEND
-Datum
-PG_char_to_encoding(PG_FUNCTION_ARGS)
-{
-       Name            s = PG_GETARG_NAME(0);
-
-       PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s)));
-}
-#endif
-
 const char *
 pg_encoding_to_char(int encoding)
 {
@@ -621,15 +596,3 @@ pg_encoding_to_char(int encoding)
        }
        return "";
 }
-
-#ifndef FRONTEND
-Datum
-PG_encoding_to_char(PG_FUNCTION_ARGS)
-{
-       int32           encoding = PG_GETARG_INT32(0);
-       const char *encoding_name = pg_encoding_to_char(encoding);
-
-       return DirectFunctionCall1(namein, CStringGetDatum(encoding_name));
-}
-
-#endif
index 74a88239a1dc08e06482faecb89c04336622aff5..efaf1c155bbdec453a603596e18b0b84729b97a1 100644 (file)
  *
  *-------------------------------------------------------------------------
  */
-#ifdef FRONTEND
-#include "postgres_fe.h"
-#else
-#include "postgres.h"
-#endif
+#include "c.h"
 
 #include "mb/pg_wchar.h"
 
@@ -838,6 +834,7 @@ pg_wchar2mule_with_len(const pg_wchar *from, unsigned char *to, int len)
        return cnt;
 }
 
+/* exported for direct use by conv.c */
 int
 pg_mule_mblen(const unsigned char *s)
 {
@@ -1498,214 +1495,6 @@ pg_utf8_islegal(const unsigned char *source, int length)
        return true;
 }
 
-#ifndef FRONTEND
-
-/*
- * Generic character incrementer function.
- *
- * Not knowing anything about the properties of the encoding in use, we just
- * keep incrementing the last byte until we get a validly-encoded result,
- * or we run out of values to try.  We don't bother to try incrementing
- * higher-order bytes, so there's no growth in runtime for wider characters.
- * (If we did try to do that, we'd need to consider the likelihood that 255
- * is not a valid final byte in the encoding.)
- */
-static bool
-pg_generic_charinc(unsigned char *charptr, int len)
-{
-       unsigned char *lastbyte = charptr + len - 1;
-       mbverifier      mbverify;
-
-       /* We can just invoke the character verifier directly. */
-       mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverify;
-
-       while (*lastbyte < (unsigned char) 255)
-       {
-               (*lastbyte)++;
-               if ((*mbverify) (charptr, len) == len)
-                       return true;
-       }
-
-       return false;
-}
-
-/*
- * UTF-8 character incrementer function.
- *
- * For a one-byte character less than 0x7F, we just increment the byte.
- *
- * For a multibyte character, every byte but the first must fall between 0x80
- * and 0xBF; and the first byte must be between 0xC0 and 0xF4.  We increment
- * the last byte that's not already at its maximum value.  If we can't find a
- * byte that's less than the maximum allowable value, we simply fail.  We also
- * need some special-case logic to skip regions used for surrogate pair
- * handling, as those should not occur in valid UTF-8.
- *
- * Note that we don't reset lower-order bytes back to their minimums, since
- * we can't afford to make an exhaustive search (see make_greater_string).
- */
-static bool
-pg_utf8_increment(unsigned char *charptr, int length)
-{
-       unsigned char a;
-       unsigned char limit;
-
-       switch (length)
-       {
-               default:
-                       /* reject lengths 5 and 6 for now */
-                       return false;
-               case 4:
-                       a = charptr[3];
-                       if (a < 0xBF)
-                       {
-                               charptr[3]++;
-                               break;
-                       }
-                       /* FALL THRU */
-               case 3:
-                       a = charptr[2];
-                       if (a < 0xBF)
-                       {
-                               charptr[2]++;
-                               break;
-                       }
-                       /* FALL THRU */
-               case 2:
-                       a = charptr[1];
-                       switch (*charptr)
-                       {
-                               case 0xED:
-                                       limit = 0x9F;
-                                       break;
-                               case 0xF4:
-                                       limit = 0x8F;
-                                       break;
-                               default:
-                                       limit = 0xBF;
-                                       break;
-                       }
-                       if (a < limit)
-                       {
-                               charptr[1]++;
-                               break;
-                       }
-                       /* FALL THRU */
-               case 1:
-                       a = *charptr;
-                       if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF4)
-                               return false;
-                       charptr[0]++;
-                       break;
-       }
-
-       return true;
-}
-
-/*
- * EUC-JP character incrementer function.
- *
- * If the sequence starts with SS2 (0x8e), it must be a two-byte sequence
- * representing JIS X 0201 characters with the second byte ranging between
- * 0xa1 and 0xdf.  We just increment the last byte if it's less than 0xdf,
- * and otherwise rewrite the whole sequence to 0xa1 0xa1.
- *
- * If the sequence starts with SS3 (0x8f), it must be a three-byte sequence
- * in which the last two bytes range between 0xa1 and 0xfe.  The last byte
- * is incremented if possible, otherwise the second-to-last byte.
- *
- * If the sequence starts with a value other than the above and its MSB
- * is set, it must be a two-byte sequence representing JIS X 0208 characters
- * with both bytes ranging between 0xa1 and 0xfe.  The last byte is
- * incremented if possible, otherwise the second-to-last byte.
- *
- * Otherwise, the sequence is a single-byte ASCII character. It is
- * incremented up to 0x7f.
- */
-static bool
-pg_eucjp_increment(unsigned char *charptr, int length)
-{
-       unsigned char c1,
-                               c2;
-       int                     i;
-
-       c1 = *charptr;
-
-       switch (c1)
-       {
-               case SS2:                               /* JIS X 0201 */
-                       if (length != 2)
-                               return false;
-
-                       c2 = charptr[1];
-
-                       if (c2 >= 0xdf)
-                               charptr[0] = charptr[1] = 0xa1;
-                       else if (c2 < 0xa1)
-                               charptr[1] = 0xa1;
-                       else
-                               charptr[1]++;
-                       break;
-
-               case SS3:                               /* JIS X 0212 */
-                       if (length != 3)
-                               return false;
-
-                       for (i = 2; i > 0; i--)
-                       {
-                               c2 = charptr[i];
-                               if (c2 < 0xa1)
-                               {
-                                       charptr[i] = 0xa1;
-                                       return true;
-                               }
-                               else if (c2 < 0xfe)
-                               {
-                                       charptr[i]++;
-                                       return true;
-                               }
-                       }
-
-                       /* Out of 3-byte code region */
-                       return false;
-
-               default:
-                       if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */
-                       {
-                               if (length != 2)
-                                       return false;
-
-                               for (i = 1; i >= 0; i--)
-                               {
-                                       c2 = charptr[i];
-                                       if (c2 < 0xa1)
-                                       {
-                                               charptr[i] = 0xa1;
-                                               return true;
-                                       }
-                                       else if (c2 < 0xfe)
-                                       {
-                                               charptr[i]++;
-                                               return true;
-                                       }
-                               }
-
-                               /* Out of 2 byte code region */
-                               return false;
-                       }
-                       else
-                       {                                       /* ASCII, single byte */
-                               if (c1 > 0x7e)
-                                       return false;
-                               (*charptr)++;
-                       }
-                       break;
-       }
-
-       return true;
-}
-#endif                                                 /* !FRONTEND */
-
 
 /*
  *-------------------------------------------------------------------
@@ -1758,13 +1547,6 @@ const pg_wchar_tbl pg_wchar_table[] = {
        {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}      /* PG_SHIFT_JIS_2004 */
 };
 
-/* returns the byte length of a word for mule internal code */
-int
-pg_mic_mblen(const unsigned char *mbstr)
-{
-       return pg_mule_mblen(mbstr);
-}
-
 /*
  * Returns the byte length of a multibyte character.
  */
@@ -1810,232 +1592,3 @@ pg_encoding_max_length(int encoding)
 
        return pg_wchar_table[encoding].maxmblen;
 }
-
-#ifndef FRONTEND
-
-/*
- * fetch maximum length of the encoding for the current database
- */
-int
-pg_database_encoding_max_length(void)
-{
-       return pg_wchar_table[GetDatabaseEncoding()].maxmblen;
-}
-
-/*
- * get the character incrementer for the encoding for the current database
- */
-mbcharacter_incrementer
-pg_database_encoding_character_incrementer(void)
-{
-       /*
-        * Eventually it might be best to add a field to pg_wchar_table[], but for
-        * now we just use a switch.
-        */
-       switch (GetDatabaseEncoding())
-       {
-               case PG_UTF8:
-                       return pg_utf8_increment;
-
-               case PG_EUC_JP:
-                       return pg_eucjp_increment;
-
-               default:
-                       return pg_generic_charinc;
-       }
-}
-
-/*
- * Verify mbstr to make sure that it is validly encoded in the current
- * database encoding.  Otherwise same as pg_verify_mbstr().
- */
-bool
-pg_verifymbstr(const char *mbstr, int len, bool noError)
-{
-       return
-               pg_verify_mbstr_len(GetDatabaseEncoding(), mbstr, len, noError) >= 0;
-}
-
-/*
- * Verify mbstr to make sure that it is validly encoded in the specified
- * encoding.
- */
-bool
-pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError)
-{
-       return pg_verify_mbstr_len(encoding, mbstr, len, noError) >= 0;
-}
-
-/*
- * Verify mbstr to make sure that it is validly encoded in the specified
- * encoding.
- *
- * mbstr is not necessarily zero terminated; length of mbstr is
- * specified by len.
- *
- * If OK, return length of string in the encoding.
- * If a problem is found, return -1 when noError is
- * true; when noError is false, ereport() a descriptive message.
- */
-int
-pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError)
-{
-       mbverifier      mbverify;
-       int                     mb_len;
-
-       Assert(PG_VALID_ENCODING(encoding));
-
-       /*
-        * In single-byte encodings, we need only reject nulls (\0).
-        */
-       if (pg_encoding_max_length(encoding) <= 1)
-       {
-               const char *nullpos = memchr(mbstr, 0, len);
-
-               if (nullpos == NULL)
-                       return len;
-               if (noError)
-                       return -1;
-               report_invalid_encoding(encoding, nullpos, 1);
-       }
-
-       /* fetch function pointer just once */
-       mbverify = pg_wchar_table[encoding].mbverify;
-
-       mb_len = 0;
-
-       while (len > 0)
-       {
-               int                     l;
-
-               /* fast path for ASCII-subset characters */
-               if (!IS_HIGHBIT_SET(*mbstr))
-               {
-                       if (*mbstr != '\0')
-                       {
-                               mb_len++;
-                               mbstr++;
-                               len--;
-                               continue;
-                       }
-                       if (noError)
-                               return -1;
-                       report_invalid_encoding(encoding, mbstr, len);
-               }
-
-               l = (*mbverify) ((const unsigned char *) mbstr, len);
-
-               if (l < 0)
-               {
-                       if (noError)
-                               return -1;
-                       report_invalid_encoding(encoding, mbstr, len);
-               }
-
-               mbstr += l;
-               len -= l;
-               mb_len++;
-       }
-       return mb_len;
-}
-
-/*
- * check_encoding_conversion_args: check arguments of a conversion function
- *
- * "expected" arguments can be either an encoding ID or -1 to indicate that
- * the caller will check whether it accepts the ID.
- *
- * Note: the errors here are not really user-facing, so elog instead of
- * ereport seems sufficient.  Also, we trust that the "expected" encoding
- * arguments are valid encoding IDs, but we don't trust the actuals.
- */
-void
-check_encoding_conversion_args(int src_encoding,
-                                                          int dest_encoding,
-                                                          int len,
-                                                          int expected_src_encoding,
-                                                          int expected_dest_encoding)
-{
-       if (!PG_VALID_ENCODING(src_encoding))
-               elog(ERROR, "invalid source encoding ID: %d", src_encoding);
-       if (src_encoding != expected_src_encoding && expected_src_encoding >= 0)
-               elog(ERROR, "expected source encoding \"%s\", but got \"%s\"",
-                        pg_enc2name_tbl[expected_src_encoding].name,
-                        pg_enc2name_tbl[src_encoding].name);
-       if (!PG_VALID_ENCODING(dest_encoding))
-               elog(ERROR, "invalid destination encoding ID: %d", dest_encoding);
-       if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0)
-               elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"",
-                        pg_enc2name_tbl[expected_dest_encoding].name,
-                        pg_enc2name_tbl[dest_encoding].name);
-       if (len < 0)
-               elog(ERROR, "encoding conversion length must not be negative");
-}
-
-/*
- * report_invalid_encoding: complain about invalid multibyte character
- *
- * note: len is remaining length of string, not length of character;
- * len must be greater than zero, as we always examine the first byte.
- */
-void
-report_invalid_encoding(int encoding, const char *mbstr, int len)
-{
-       int                     l = pg_encoding_mblen(encoding, mbstr);
-       char            buf[8 * 5 + 1];
-       char       *p = buf;
-       int                     j,
-                               jlimit;
-
-       jlimit = Min(l, len);
-       jlimit = Min(jlimit, 8);        /* prevent buffer overrun */
-
-       for (j = 0; j < jlimit; j++)
-       {
-               p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
-               if (j < jlimit - 1)
-                       p += sprintf(p, " ");
-       }
-
-       ereport(ERROR,
-                       (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
-                        errmsg("invalid byte sequence for encoding \"%s\": %s",
-                                       pg_enc2name_tbl[encoding].name,
-                                       buf)));
-}
-
-/*
- * report_untranslatable_char: complain about untranslatable character
- *
- * note: len is remaining length of string, not length of character;
- * len must be greater than zero, as we always examine the first byte.
- */
-void
-report_untranslatable_char(int src_encoding, int dest_encoding,
-                                                  const char *mbstr, int len)
-{
-       int                     l = pg_encoding_mblen(src_encoding, mbstr);
-       char            buf[8 * 5 + 1];
-       char       *p = buf;
-       int                     j,
-                               jlimit;
-
-       jlimit = Min(l, len);
-       jlimit = Min(jlimit, 8);        /* prevent buffer overrun */
-
-       for (j = 0; j < jlimit; j++)
-       {
-               p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]);
-               if (j < jlimit - 1)
-                       p += sprintf(p, " ");
-       }
-
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
-                        errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"",
-                                       buf,
-                                       pg_enc2name_tbl[src_encoding].name,
-                                       pg_enc2name_tbl[dest_encoding].name)));
-}
-
-#endif                                                 /* !FRONTEND */
index 026f64f90be40456365e9fc20fe50f26823e2460..b8892ef730e3f3384885c7bd8c795a372d103302 100644 (file)
@@ -9,7 +9,7 @@
  * src/include/mb/pg_wchar.h
  *
  *     NOTES
- *             This is used both by the backend and by libpq, but should not be
+ *             This is used both by the backend and by frontends, but should not be
  *             included by libpq client programs.  In particular, a libpq client
  *             should not assume that the encoding IDs used by the version of libpq
  *             it's linked to match up with the IDs declared here.
@@ -345,12 +345,6 @@ typedef struct pg_enc2gettext
 
 extern const pg_enc2gettext pg_enc2gettext_tbl[];
 
-/*
- * Encoding names for ICU
- */
-extern bool is_encoding_supported_by_icu(int encoding);
-extern const char *get_encoding_name_for_icu(int encoding);
-
 /*
  * pg_wchar stuff
  */
@@ -539,8 +533,27 @@ extern const char *pg_encoding_to_char(int encoding);
 extern int     pg_valid_server_encoding_id(int encoding);
 
 /*
- * Remaining functions are not considered part of libpq's API, though many
- * of them do exist inside libpq.
+ * These functions are available to frontend code that links with libpgcommon
+ * (in addition to the ones just above).  The constant tables declared
+ * earlier in this file are also available from libpgcommon.
+ */
+extern int     pg_encoding_mblen(int encoding, const char *mbstr);
+extern int     pg_encoding_dsplen(int encoding, const char *mbstr);
+extern int     pg_encoding_verifymb(int encoding, const char *mbstr, int len);
+extern int     pg_encoding_max_length(int encoding);
+extern int     pg_valid_client_encoding(const char *name);
+extern int     pg_valid_server_encoding(const char *name);
+extern bool is_encoding_supported_by_icu(int encoding);
+extern const char *get_encoding_name_for_icu(int encoding);
+
+extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string);
+extern pg_wchar utf8_to_unicode(const unsigned char *c);
+extern bool pg_utf8_islegal(const unsigned char *source, int length);
+extern int     pg_utf_mblen(const unsigned char *s);
+extern int     pg_mule_mblen(const unsigned char *s);
+
+/*
+ * The remaining functions are backend-only.
  */
 extern int     pg_mb2wchar(const char *from, pg_wchar *to);
 extern int     pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len);
@@ -556,18 +569,12 @@ extern int        pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t
 extern size_t pg_wchar_strlen(const pg_wchar *wstr);
 extern int     pg_mblen(const char *mbstr);
 extern int     pg_dsplen(const char *mbstr);
-extern int     pg_encoding_mblen(int encoding, const char *mbstr);
-extern int     pg_encoding_dsplen(int encoding, const char *mbstr);
-extern int     pg_encoding_verifymb(int encoding, const char *mbstr, int len);
-extern int     pg_mule_mblen(const unsigned char *mbstr);
-extern int     pg_mic_mblen(const unsigned char *mbstr);
 extern int     pg_mbstrlen(const char *mbstr);
 extern int     pg_mbstrlen_with_len(const char *mbstr, int len);
 extern int     pg_mbcliplen(const char *mbstr, int len, int limit);
 extern int     pg_encoding_mbcliplen(int encoding, const char *mbstr,
                                                                  int len, int limit);
 extern int     pg_mbcharcliplen(const char *mbstr, int len, int limit);
-extern int     pg_encoding_max_length(int encoding);
 extern int     pg_database_encoding_max_length(void);
 extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void);
 
@@ -587,12 +594,6 @@ extern int GetMessageEncoding(void);
 extern int     pg_bind_textdomain_codeset(const char *domainname);
 #endif
 
-extern int     pg_valid_client_encoding(const char *name);
-extern int     pg_valid_server_encoding(const char *name);
-
-extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string);
-extern pg_wchar utf8_to_unicode(const unsigned char *c);
-extern int     pg_utf_mblen(const unsigned char *);
 extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len,
                                                                                                int src_encoding,
                                                                                                int dest_encoding);
@@ -647,8 +648,6 @@ extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p,
                                                                 int len, int lc, int encoding,
                                                                 const unsigned char *tab);
 
-extern bool pg_utf8_islegal(const unsigned char *source, int length);
-
 #ifdef WIN32
 extern WCHAR *pgwin32_message_to_UTF16(const char *str, int len, int *utf16len);
 #endif