static void echo_hidden_hook(const char *newval) { if (newval == NULL) pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; else if (strcmp(newval, "noexec") == 0) pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC; else if (pg_strcasecmp(newval, "off") == 0) pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; else pset.echo_hidden = PSQL_ECHO_HIDDEN_ON; }
static void verbosity_hook(const char *newval) { if (newval == NULL) pset.verbosity = PQERRORS_DEFAULT; else if (pg_strcasecmp(newval, "default") == 0) pset.verbosity = PQERRORS_DEFAULT; else if (pg_strcasecmp(newval, "terse") == 0) pset.verbosity = PQERRORS_TERSE; else if (pg_strcasecmp(newval, "verbose") == 0) pset.verbosity = PQERRORS_VERBOSE; else { psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n", newval, "VERBOSITY", "default"); pset.verbosity = PQERRORS_DEFAULT; } if (pset.db) PQsetErrorVerbosity(pset.db, pset.verbosity); }
static void on_error_rollback_hook(const char *newval) { if (newval == NULL) pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; else if (pg_strcasecmp(newval, "interactive") == 0) pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE; else if (ParseVariableBool(newval, "ON_ERROR_ROLLBACK")) pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON; else /* ParseVariableBool printed msg if needed */ pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; }
static void echo_hidden_hook(const char *newval) { if (newval == NULL) pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; else if (pg_strcasecmp(newval, "noexec") == 0) pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC; else if (ParseVariableBool(newval, "ECHO_HIDDEN")) pset.echo_hidden = PSQL_ECHO_HIDDEN_ON; else /* ParseVariableBool printed msg if needed */ pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; }
static void locate_stem_module(DictSnowball *d, char *lang) { const stemmer_module *m; /* * First, try to find exact match of stemmer module. Stemmer with * PG_SQL_ASCII encoding is treated as working with any server encoding */ for (m = stemmer_modules; m->name; m++) { if ((m->enc == PG_SQL_ASCII || m->enc == GetDatabaseEncoding()) && pg_strcasecmp(m->name, lang) == 0) { d->stem = m->stem; d->z = m->create(); d->needrecode = false; return; } } /* * Second, try to find stemmer for needed language for UTF8 encoding. */ for (m = stemmer_modules; m->name; m++) { if (m->enc == PG_UTF8 && pg_strcasecmp(m->name, lang) == 0) { d->stem = m->stem; d->z = m->create(); d->needrecode = true; return; } } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no Snowball stemmer available for language \"%s\" and encoding \"%s\"", lang, GetDatabaseEncodingName()))); }
Datum thesaurus_init(PG_FUNCTION_ARGS) { List *dictoptions = (List *) PG_GETARG_POINTER(0); DictThesaurus *d; char *subdictname = NULL; bool fileloaded = false; ListCell *l; d = (DictThesaurus *) palloc0(sizeof(DictThesaurus)); foreach(l, dictoptions) { DefElem *defel = (DefElem *) lfirst(l); if (pg_strcasecmp("DictFile", defel->defname) == 0) { if (fileloaded) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("multiple DictFile parameters"))); thesaurusRead(defGetString(defel), d); fileloaded = true; } else if (pg_strcasecmp("Dictionary", defel->defname) == 0) { if (subdictname) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("multiple Dictionary parameters"))); subdictname = pstrdup(defGetString(defel)); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized Thesaurus parameter: \"%s\"", defel->defname))); } }
/* * Extract a type length indicator (either absolute bytes, or * -1 for "variable") from a DefElem. */ int defGetTypeLength(DefElem *def) { if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a parameter", def->defname))); switch (nodeTag(def->arg)) { case T_Integer: return intVal(def->arg); case T_Float: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires an integer value", def->defname))); break; case T_String: if (pg_strcasecmp(strVal(def->arg), "variable") == 0) return -1; /* variable length */ break; case T_TypeName: /* cope if grammar chooses to believe "variable" is a typename */ if (pg_strcasecmp(TypeNameToString((TypeName *) def->arg), "variable") == 0) return -1; /* variable length */ break; case T_List: /* must be an operator name */ break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg)); } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid argument for %s: \"%s\"", def->defname, defGetString(def)))); return 0; /* keep compiler quiet */ }
/* * Try to interpret "value" as a boolean value, and if successful, * store it in *result. Otherwise don't clobber *result. * * Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique * prefixes thereof. * * "name" is the name of the variable we're assigning to, to use in error * report if any. Pass name == NULL to suppress the error report. * * Return true when "value" is syntactically valid, false otherwise. */ bool ParseVariableBool(const char *value, const char *name, bool *result) { size_t len; bool valid = true; /* Treat "unset" as an empty string, which will lead to error below */ if (value == NULL) value = ""; len = strlen(value); if (len > 0 && pg_strncasecmp(value, "true", len) == 0) *result = true; else if (len > 0 && pg_strncasecmp(value, "false", len) == 0) *result = false; else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0) *result = true; else if (len > 0 && pg_strncasecmp(value, "no", len) == 0) *result = false; /* 'o' is not unique enough */ else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) *result = true; else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) *result = false; else if (pg_strcasecmp(value, "1") == 0) *result = true; else if (pg_strcasecmp(value, "0") == 0) *result = false; else { /* string is not recognized; don't clobber *result */ if (name) psql_error("unrecognized value \"%s\" for \"%s\": boolean expected\n", value, name); valid = false; } return valid; }
/* * assign_regex_flavor - GUC hook to validate and set REGEX_FLAVOR */ const char * assign_regex_flavor(const char *value, bool doit, GucSource source) { if (pg_strcasecmp(value, "advanced") == 0) { if (doit) regex_flavor = REG_ADVANCED; } else if (pg_strcasecmp(value, "extended") == 0) { if (doit) regex_flavor = REG_EXTENDED; } else if (pg_strcasecmp(value, "basic") == 0) { if (doit) regex_flavor = REG_BASIC; } else return NULL; /* fail */ return value; /* OK */ }
/* * gpvars_assign_gp_resqueue_memory_policy * gpvars_show_gp_resqueue_memory_policy */ const char * gpvars_assign_gp_resqueue_memory_policy(const char *newval, bool doit, GucSource source __attribute__((unused)) ) { ResQueueMemoryPolicy newtype = RESQUEUE_MEMORY_POLICY_NONE; if (newval == NULL || newval[0] == 0 || !pg_strcasecmp("none", newval)) newtype = RESQUEUE_MEMORY_POLICY_NONE; else if (!pg_strcasecmp("auto", newval)) newtype = RESQUEUE_MEMORY_POLICY_AUTO; else if (!pg_strcasecmp("eager_free", newval)) newtype = RESQUEUE_MEMORY_POLICY_EAGER_FREE; else elog(ERROR, "unknown resource queue memory policy: current policy is '%s'", gpvars_show_gp_resqueue_memory_policy()); if (doit) { gp_resqueue_memory_policy = newtype; } return newval; }
/* * Convert Windows locale name to the ISO formatted one * if possible. * * This function returns NULL if conversion is impossible, * otherwise returns the pointer to a static area which * contains the iso formatted locale name. */ static char * IsoLocaleName(const char *winlocname) { #if (_MSC_VER >= 1400) /* VC8.0 or later */ static char iso_lc_messages[32]; _locale_t loct = NULL; if (pg_strcasecmp("c", winlocname) == 0 || pg_strcasecmp("posix", winlocname) == 0) { strcpy(iso_lc_messages, "C"); return iso_lc_messages; } loct = _create_locale(LC_CTYPE, winlocname); if (loct != NULL) { char isolang[32], isocrty[32]; LCID lcid; lcid = loct->locinfo->lc_handle[LC_CTYPE]; if (lcid == 0) lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); _free_locale(loct); if (!GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, isolang, sizeof(isolang))) return NULL; if (!GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty, sizeof(isocrty))) return NULL; snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s", isolang, isocrty); return iso_lc_messages; } return NULL; #else return NULL; /* Not supported on this version of msvc/mingw */ #endif /* _MSC_VER >= 1400 */ }
Datum dsnowball_init(PG_FUNCTION_ARGS) { List *dictoptions = (List *) PG_GETARG_POINTER(0); DictSnowball *d; bool stoploaded = false; ListCell *l; d = (DictSnowball *) palloc0(sizeof(DictSnowball)); foreach(l, dictoptions) { DefElem *defel = (DefElem *) lfirst(l); if (pg_strcasecmp("StopWords", defel->defname) == 0) { if (stoploaded) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("multiple StopWords parameters"))); readstoplist(defGetString(defel), &d->stoplist, lowerstr); stoploaded = true; } else if (pg_strcasecmp("Language", defel->defname) == 0) { if (d->stem) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("multiple Language parameters"))); locate_stem_module(d, defGetString(defel)); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized Snowball parameter: \"%s\"", defel->defname))); } }
/* * gpvars_assign_gp_interconnect_type * gpvars_show_gp_interconnect_type */ const char * gpvars_assign_gp_interconnect_type(const char *newval, bool doit, GucSource source __attribute__((unused)) ) { int newtype = 0; if (newval == NULL || newval[0] == 0 || !pg_strcasecmp("tcp", newval)) newtype = INTERCONNECT_TYPE_TCP; else if (!pg_strcasecmp("udp", newval)) newtype = INTERCONNECT_TYPE_UDP; else if (!pg_strcasecmp("nil", newval)) newtype = INTERCONNECT_TYPE_NIL; else elog(ERROR, "Unknown interconnect type. (current type is '%s')", gpvars_show_gp_interconnect_type()); if (doit) { if (newtype == INTERCONNECT_TYPE_NIL) { if (Gp_role == GP_ROLE_DISPATCH) elog(WARNING, "Nil-Interconnect diagnostic mode enabled (tuple will be dropped)."); else elog(LOG, "Nil-Interconnect diagnostic mode enabled (tuple will be dropped)."); } else if (Gp_interconnect_type == INTERCONNECT_TYPE_NIL) { if (Gp_role == GP_ROLE_DISPATCH) elog(WARNING, "Nil-Interconnect diagnostic mode disabled."); else elog(LOG, "Nil-Interconnect diagnostic mode disabled."); } Gp_interconnect_type = newtype; } return newval; } /* gpvars_assign_gp_log_interconnect */
/* * CREATE COLLATION */ ObjectAddress DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists) { char *collName; Oid collNamespace; AclResult aclresult; ListCell *pl; DefElem *fromEl = NULL; DefElem *localeEl = NULL; DefElem *lccollateEl = NULL; DefElem *lcctypeEl = NULL; DefElem *providerEl = NULL; DefElem *versionEl = NULL; char *collcollate = NULL; char *collctype = NULL; char *collproviderstr = NULL; int collencoding; char collprovider = 0; char *collversion = NULL; Oid newoid; ObjectAddress address; collNamespace = QualifiedNameGetCreationNamespace(names, &collName); aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(collNamespace)); foreach(pl, parameters) { DefElem *defel = lfirst_node(DefElem, pl); DefElem **defelp; if (pg_strcasecmp(defel->defname, "from") == 0) defelp = &fromEl; else if (pg_strcasecmp(defel->defname, "locale") == 0) defelp = &localeEl; else if (pg_strcasecmp(defel->defname, "lc_collate") == 0) defelp = &lccollateEl; else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0) defelp = &lcctypeEl; else if (pg_strcasecmp(defel->defname, "provider") == 0) defelp = &providerEl; else if (pg_strcasecmp(defel->defname, "version") == 0) defelp = &versionEl; else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("collation attribute \"%s\" not recognized", defel->defname), parser_errposition(pstate, defel->location))); break; } *defelp = defel; }
/* * Initialize timezone library * * This is called after initial loading of postgresql.conf. If no TimeZone * setting was found therein, we try to derive one from the environment. */ void pg_timezone_initialize(void) { /* Do we need to try to figure the timezone? */ if (pg_strcasecmp(GetConfigOption("timezone"), "UNKNOWN") == 0) { const char *def_tz; /* Select setting */ def_tz = select_default_timezone(); /* Tell GUC about the value. Will redundantly call pg_tzset() */ SetConfigOption("timezone", def_tz, PGC_POSTMASTER, PGC_S_ARGV); } }
static IndexAMProperty lookup_prop_name(const char *name) { int i; for (i = 0; i < lengthof(am_propnames); i++) { if (pg_strcasecmp(am_propnames[i].name, name) == 0) return am_propnames[i].prop; } /* We do not throw an error, so that AMs can define their own properties */ return AMPROP_UNKNOWN; }
/* * Bind gettext to the codeset equivalent with the database encoding. */ void pg_bind_textdomain_codeset(const char *domainname) { #if defined(ENABLE_NLS) int encoding = GetDatabaseEncoding(); int i; /* * gettext() uses the codeset specified by LC_CTYPE by default, so if that * matches the database encoding we don't need to do anything. In CREATE * DATABASE, we enforce or trust that the locale's codeset matches * database encoding, except for the C locale. In C locale, we bind * gettext() explicitly to the right codeset. * * On Windows, though, gettext() tends to get confused so we always bind * it. */ #ifndef WIN32 const char *ctype = setlocale(LC_CTYPE, NULL); if (pg_strcasecmp(ctype, "C") != 0 && pg_strcasecmp(ctype, "POSIX") != 0) return; #endif for (i = 0; pg_enc2gettext_tbl[i].name != NULL; i++) { if (pg_enc2gettext_tbl[i].encoding == encoding) { if (bind_textdomain_codeset(domainname, pg_enc2gettext_tbl[i].name) == NULL) elog(LOG, "bind_textdomain_codeset failed"); break; } } #endif }
Datum dregex_init(PG_FUNCTION_ARGS) { parser_str *parser = parser_create(); List *dictoptions = (List *) PG_GETARG_POINTER(0); ListCell *l; foreach(l, dictoptions){ DefElem *defel = (DefElem *) lfirst(l); if (pg_strcasecmp(defel->defname, "RULES") == 0) { parser_read_rules(parser, defGetString(defel)); } else { elog(ERROR,"Unknown option: %s => %s", defel->defname, defGetString(defel)); } }
size_t choice(const char *name, const char *key, const char *keys[], size_t nkeys) { size_t i; for (i = 0; i < nkeys; i++) { if (pg_strcasecmp(key, keys[i]) == 0) return i; } ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid %s \"%s\"", name, key))); return 0; /* keep compiler quiet */ }
/* * Try to interpret value as boolean value. Valid values are: true, * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof. */ bool ParseVariableBool(const char *value) { size_t len; if (value == NULL) return false; /* not set -> assume "off" */ len = strlen(value); if (pg_strncasecmp(value, "true", len) == 0) return true; else if (pg_strncasecmp(value, "false", len) == 0) return false; else if (pg_strncasecmp(value, "yes", len) == 0) return true; else if (pg_strncasecmp(value, "no", len) == 0) return false; /* 'o' is not unique enough */ else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) return true; else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) return false; else if (pg_strcasecmp(value, "1") == 0) return true; else if (pg_strcasecmp(value, "0") == 0) return false; else { /* NULL is treated as false, so a non-matching value is 'true' */ psql_error("unrecognized boolean value; assuming \"on\".\n"); return true; } /* suppress compiler warning */ return true; }
/** * @brief Parse int64 expression */ int64 ParseInt64(char *value, int64 minValue) { int64 i; if (pg_strcasecmp(value, "INFINITE") == 0) return INT64CONST(0x7FFFFFFFFFFFFFFF); i = DatumGetInt64(DirectFunctionCall1(int8in, CStringGetDatum(value))); if (i < minValue) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("value \"%s\" is out of range", value))); return i; }
/* * Get the set of functions implementing a compression algorithm. * * Intercept requests for "none", since that is not a real compression * implementation but a fake one to indicate no compression desired. */ PGFunction * get_funcs_for_compression(char *compresstype) { PGFunction *func = NULL; if (!compresstype) return func; if (pg_strcasecmp("none", compresstype) != 0) { func = GetCompressionImplementation(compresstype); Insist(PointerIsValid(func)); } return func; }
void ReaderInit(Reader *self) { /* * Set defaults to unspecified parameters. */ if (self->max_parse_errors < -1) self->max_parse_errors = DEFAULT_MAX_PARSE_ERRORS; /* * Use the client_encoding case of ENCODING is not specified and INPUT is * STDIN. */ if (self->checker.encoding == -1 && pg_strcasecmp(self->infile, "stdin") == 0) self->checker.encoding = pg_get_client_encoding(); }
/* * validate_exec() * * validate "path" as an executable file */ static void validate_exec(const char *dir, const char *cmdName) { char path[MAXPGPATH]; struct stat buf; snprintf(path, sizeof(path), "%s/%s", dir, cmdName); #ifdef WIN32 /* Windows requires a .exe suffix for stat() */ if (strlen(path) <= strlen(EXE_EXT) || pg_strcasecmp(path + strlen(path) - strlen(EXE_EXT), EXE_EXT) != 0) strlcat(path, EXE_EXT, sizeof(path)); #endif /* * Ensure that the file exists and is a regular file. */ if (stat(path, &buf) < 0) pg_log(PG_FATAL, "check for %s failed - %s\n", cmdName, getErrorText(errno)); if (!S_ISREG(buf.st_mode)) pg_log(PG_FATAL, "check for %s failed - not an executable file\n", cmdName); /* * Ensure that the file is both executable and readable (required for * dynamic loading). */ #ifndef WIN32 if (access(path, R_OK) != 0) #else if ((buf.st_mode & S_IRUSR) == 0) #endif pg_log(PG_FATAL, "check for %s failed - cannot read file (permission denied)\n", cmdName); #ifndef WIN32 if (access(path, X_OK) != 0) #else if ((buf.st_mode & S_IXUSR) == 0) #endif pg_log(PG_FATAL, "check for %s failed - cannot execute (permission denied)\n", cmdName); }
int pgp_get_cipher_code(const char *name) { const struct cipher_info *i; for (i = cipher_list; i->name; i++) { if (pg_strcasecmp(i->name, name) == 0) { if (fips_mode && !i->fips) return PXE_NOT_ALLOWED_FIPS; else return i->code; } } return PXE_PGP_UNSUPPORTED_CIPHER; }
/* * validate_exec -- validate "path" as an executable file * * returns 0 if the file is found and no error is encountered. * -1 if the regular file "path" does not exist or cannot be executed. * -2 if the file is otherwise valid but cannot be read. */ static int validate_exec(const char *path) { struct stat buf; int is_r; int is_x; #ifdef WIN32 char path_exe[MAXPGPATH + sizeof(".exe") - 1]; /* Win32 requires a .exe suffix for stat() */ if (strlen(path) >= strlen(".exe") && pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0) { strcpy(path_exe, path); strcat(path_exe, ".exe"); path = path_exe; } #endif /* * Ensure that the file exists and is a regular file. * * XXX if you have a broken system where stat() looks at the symlink * instead of the underlying file, you lose. */ if (stat(path, &buf) < 0) return -1; if (!S_ISREG(buf.st_mode)) return -1; /* * Ensure that the file is both executable and readable (required for * dynamic loading). */ #ifndef WIN32 is_r = (access(path, R_OK) == 0); is_x = (access(path, X_OK) == 0); #else is_r = buf.st_mode & S_IRUSR; is_x = buf.st_mode & S_IXUSR; #endif return is_x ? (is_r ? 0 : -2) : -1; }
int px_find_digest(const char *name, PX_MD **res) { const struct int_digest *p; PX_MD *h; for (p = int_digest_list; p->name; p++) if (pg_strcasecmp(p->name, name) == 0) { h = px_alloc(sizeof(*h)); p->init(h); *res = h; return 0; } return PXE_NO_HASH; }
/* * Given a Windows code page identifier, find the corresponding PostgreSQL * encoding. Issue a warning and return -1 if none found. */ int pg_codepage_to_encoding(UINT cp) { char sys[16]; int i; sprintf(sys, "CP%u", cp); /* Check the table */ for (i = 0; encoding_match_list[i].system_enc_name; i++) if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) return encoding_match_list[i].pg_enc_code; ereport(WARNING, (errmsg("could not determine encoding for codeset \"%s\"", sys))); return -1; }
/* * gpvars_assign_gp_interconnect_type * gpvars_show_gp_interconnect_type */ const char * gpvars_assign_gp_interconnect_type(const char *newval, bool doit, GucSource source __attribute__((unused)) ) { int newtype = 0; if (newval == NULL || newval[0] == 0) newtype = INTERCONNECT_TYPE_UDPIFC; else if (!pg_strcasecmp("udpifc", newval)) newtype = INTERCONNECT_TYPE_UDPIFC; else elog(ERROR, "Only support UDPIFC, (current type is '%s')", gpvars_show_gp_interconnect_type()); if (doit) { Gp_interconnect_type = newtype; } return newval; } /* gpvars_assign_gp_log_interconnect */
/* * equivalent_locale() * * Best effort locale-name comparison. Return false if we are not 100% sure * the locales are equivalent. * * Note: The encoding parts of the names are ignored. This function is * currently used to compare locale names stored in pg_database, and * pg_database contains a separate encoding field. That's compared directly * in check_locale_and_encoding(). */ static bool equivalent_locale(int category, const char *loca, const char *locb) { const char *chara; const char *charb; char *canona; char *canonb; int lena; int lenb; /* * If the names are equal, the locales are equivalent. Checking this first * avoids calling setlocale() in the common case that the names are equal. * That's a good thing, if setlocale() is buggy, for example. */ if (pg_strcasecmp(loca, locb) == 0) return true; /* * Not identical. Canonicalize both names, remove the encoding parts, and * try again. */ canona = get_canonical_locale_name(category, loca); chara = strrchr(canona, '.'); lena = chara ? (chara - canona) : strlen(canona); canonb = get_canonical_locale_name(category, locb); charb = strrchr(canonb, '.'); lenb = charb ? (charb - canonb) : strlen(canonb); if (lena == lenb && pg_strncasecmp(canona, canonb, lena) == 0) { pg_free(canona); pg_free(canonb); return true; } pg_free(canona); pg_free(canonb); return false; }