#include "Python.h"
#include "pycore_fileutils.h"
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LANGINFO_H
#include <langinfo.h>
#endif
#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_WCHAR_H
#include <wchar.h>
#endif
#if defined(MS_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
PyDoc_STRVAR(locale__doc__, "Support for POSIX locales.");
typedef struct _locale_state {
PyObject *Error;
} _locale_state;
static inline _locale_state*
get_locale_state(PyObject *m)
{
void *state = PyModule_GetState(m);
assert(state != NULL);
return (_locale_state *)state;
}
#include "clinic/_localemodule.c.h"
static PyObject*
copy_grouping(const char* s)
{
int i;
PyObject *result, *val = NULL;
if (s[0] == '\0') {
return PyList_New(0);
}
for (i = 0; s[i] != '\0' && s[i] != CHAR_MAX; i++)
;
result = PyList_New(i+1);
if (!result)
return NULL;
i = -1;
do {
i++;
val = PyLong_FromLong(s[i]);
if (val == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, i, val);
} while (s[i] != '\0' && s[i] != CHAR_MAX);
return result;
}
static PyObject *
_locale_setlocale_impl(PyObject *module, int category, const char *locale)
{
char *result;
PyObject *result_object;
#if defined(MS_WINDOWS)
if (category < LC_MIN || category > LC_MAX)
{
PyErr_SetString(get_locale_state(module)->Error,
"invalid locale category");
return NULL;
}
#endif
if (locale) {
result = setlocale(category, locale);
if (!result) {
PyErr_SetString(get_locale_state(module)->Error,
"unsupported locale setting");
return NULL;
}
result_object = PyUnicode_DecodeLocale(result, NULL);
if (!result_object)
return NULL;
} else {
result = setlocale(category, NULL);
if (!result) {
PyErr_SetString(get_locale_state(module)->Error,
"locale query failed");
return NULL;
}
result_object = PyUnicode_DecodeLocale(result, NULL);
}
return result_object;
}
static int
locale_is_ascii(const char *str)
{
return (strlen(str) == 1 && ((unsigned char)str[0]) <= 127);
}
static int
locale_decode_monetary(PyObject *dict, struct lconv *lc)
{
#ifndef MS_WINDOWS
int change_locale;
change_locale = (!locale_is_ascii(lc->int_curr_symbol)
|| !locale_is_ascii(lc->currency_symbol)
|| !locale_is_ascii(lc->mon_decimal_point)
|| !locale_is_ascii(lc->mon_thousands_sep));
char *oldloc = NULL, *loc = NULL;
if (change_locale) {
oldloc = setlocale(LC_CTYPE, NULL);
if (!oldloc) {
PyErr_SetString(PyExc_RuntimeWarning,
"failed to get LC_CTYPE locale");
return -1;
}
oldloc = _PyMem_Strdup(oldloc);
if (!oldloc) {
PyErr_NoMemory();
return -1;
}
loc = setlocale(LC_MONETARY, NULL);
if (loc != NULL && strcmp(loc, oldloc) == 0) {
loc = NULL;
}
if (loc != NULL) {
setlocale(LC_CTYPE, loc);
}
}
#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
#else
#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
#endif
int res = -1;
#define RESULT_STRING(ATTR) \
do { \
PyObject *obj; \
obj = GET_LOCALE_STRING(ATTR); \
if (obj == NULL) { \
goto done; \
} \
if (PyDict_SetItemString(dict, Py_STRINGIFY(ATTR), obj) < 0) { \
Py_DECREF(obj); \
goto done; \
} \
Py_DECREF(obj); \
} while (0)
RESULT_STRING(int_curr_symbol);
RESULT_STRING(currency_symbol);
RESULT_STRING(mon_decimal_point);
RESULT_STRING(mon_thousands_sep);
#undef RESULT_STRING
#undef GET_LOCALE_STRING
res = 0;
done:
#ifndef MS_WINDOWS
if (loc != NULL) {
setlocale(LC_CTYPE, oldloc);
}
PyMem_Free(oldloc);
#endif
return res;
}
static PyObject *
_locale_localeconv_impl(PyObject *module)
{
PyObject* result;
struct lconv *lc;
PyObject *x;
result = PyDict_New();
if (!result) {
return NULL;
}
lc = localeconv();
#define RESULT(key, obj)\
do { \
if (obj == NULL) \
goto failed; \
if (PyDict_SetItemString(result, key, obj) < 0) { \
Py_DECREF(obj); \
goto failed; \
} \
Py_DECREF(obj); \
} while (0)
#ifdef MS_WINDOWS
#define GET_LOCALE_STRING(ATTR) PyUnicode_FromWideChar(lc->_W_ ## ATTR, -1)
#else
#define GET_LOCALE_STRING(ATTR) PyUnicode_DecodeLocale(lc->ATTR, NULL)
#endif
#define RESULT_STRING(s)\
do { \
x = GET_LOCALE_STRING(s); \
RESULT(#s, x); \
} while (0)
#define RESULT_INT(i)\
do { \
x = PyLong_FromLong(lc->i); \
RESULT(#i, x); \
} while (0)
if (locale_decode_monetary(result, lc) < 0) {
goto failed;
}
x = copy_grouping(lc->mon_grouping);
RESULT("mon_grouping", x);
RESULT_STRING(positive_sign);
RESULT_STRING(negative_sign);
RESULT_INT(int_frac_digits);
RESULT_INT(frac_digits);
RESULT_INT(p_cs_precedes);
RESULT_INT(p_sep_by_space);
RESULT_INT(n_cs_precedes);
RESULT_INT(n_sep_by_space);
RESULT_INT(p_sign_posn);
RESULT_INT(n_sign_posn);
PyObject *decimal_point = NULL, *thousands_sep = NULL;
if (_Py_GetLocaleconvNumeric(lc, &decimal_point, &thousands_sep) < 0) {
Py_XDECREF(decimal_point);
Py_XDECREF(thousands_sep);
goto failed;
}
if (PyDict_SetItemString(result, "decimal_point", decimal_point) < 0) {
Py_DECREF(decimal_point);
Py_DECREF(thousands_sep);
goto failed;
}
Py_DECREF(decimal_point);
if (PyDict_SetItemString(result, "thousands_sep", thousands_sep) < 0) {
Py_DECREF(thousands_sep);
goto failed;
}
Py_DECREF(thousands_sep);
x = copy_grouping(lc->grouping);
RESULT("grouping", x);
return result;
failed:
Py_DECREF(result);
return NULL;
#undef RESULT
#undef RESULT_STRING
#undef RESULT_INT
#undef GET_LOCALE_STRING
}
#if defined(HAVE_WCSCOLL)
static PyObject *
_locale_strcoll_impl(PyObject *module, PyObject *os1, PyObject *os2)
{
PyObject *result = NULL;
wchar_t *ws1 = NULL, *ws2 = NULL;
ws1 = PyUnicode_AsWideCharString(os1, NULL);
if (ws1 == NULL)
goto done;
ws2 = PyUnicode_AsWideCharString(os2, NULL);
if (ws2 == NULL)
goto done;
result = PyLong_FromLong(wcscoll(ws1, ws2));
done:
if (ws1) PyMem_Free(ws1);
if (ws2) PyMem_Free(ws2);
return result;
}
#endif
#ifdef HAVE_WCSXFRM
static PyObject *
_locale_strxfrm_impl(PyObject *module, PyObject *str)
{
Py_ssize_t n1;
wchar_t *s = NULL, *buf = NULL;
size_t n2;
PyObject *result = NULL;
s = PyUnicode_AsWideCharString(str, &n1);
if (s == NULL)
goto exit;
if (wcslen(s) != (size_t)n1) {
PyErr_SetString(PyExc_ValueError,
"embedded null character");
goto exit;
}
n1 = n1 + 1;
buf = PyMem_New(wchar_t, n1);
if (!buf) {
PyErr_NoMemory();
goto exit;
}
errno = 0;
n2 = wcsxfrm(buf, s, n1);
if (errno && errno != ERANGE) {
PyErr_SetFromErrno(PyExc_OSError);
goto exit;
}
if (n2 >= (size_t)n1) {
wchar_t * new_buf = PyMem_Realloc(buf, (n2+1)*sizeof(wchar_t));
if (!new_buf) {
PyErr_NoMemory();
goto exit;
}
buf = new_buf;
errno = 0;
n2 = wcsxfrm(buf, s, n2+1);
if (errno) {
PyErr_SetFromErrno(PyExc_OSError);
goto exit;
}
}
result = PyUnicode_FromWideChar(buf, n2);
exit:
PyMem_Free(buf);
PyMem_Free(s);
return result;
}
#endif
#if defined(MS_WINDOWS)
static PyObject *
_locale__getdefaultlocale_impl(PyObject *module)
{
char encoding[20];
char locale[100];
PyOS_snprintf(encoding, sizeof(encoding), "cp%u", GetACP());
if (GetLocaleInfoA(LOCALE_USER_DEFAULT,
LOCALE_SISO639LANGNAME,
locale, sizeof(locale))) {
Py_ssize_t i = strlen(locale);
locale[i++] = '_';
if (GetLocaleInfoA(LOCALE_USER_DEFAULT,
LOCALE_SISO3166CTRYNAME,
locale+i, (int)(sizeof(locale)-i)))
return Py_BuildValue("ss", locale, encoding);
}
locale[0] = '0';
locale[1] = 'x';
if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE,
locale+2, sizeof(locale)-2)) {
return Py_BuildValue("ss", locale, encoding);
}
Py_INCREF(Py_None);
return Py_BuildValue("Os", Py_None, encoding);
}
#endif
#ifdef HAVE_LANGINFO_H
#define LANGINFO(X) {#X, X}
static struct langinfo_constant{
char* name;
int value;
} langinfo_constants[] =
{
LANGINFO(DAY_1),
LANGINFO(DAY_2),
LANGINFO(DAY_3),
LANGINFO(DAY_4),
LANGINFO(DAY_5),
LANGINFO(DAY_6),
LANGINFO(DAY_7),
LANGINFO(ABDAY_1),
LANGINFO(ABDAY_2),
LANGINFO(ABDAY_3),
LANGINFO(ABDAY_4),
LANGINFO(ABDAY_5),
LANGINFO(ABDAY_6),
LANGINFO(ABDAY_7),
LANGINFO(MON_1),
LANGINFO(MON_2),
LANGINFO(MON_3),
LANGINFO(MON_4),
LANGINFO(MON_5),
LANGINFO(MON_6),
LANGINFO(MON_7),
LANGINFO(MON_8),
LANGINFO(MON_9),
LANGINFO(MON_10),
LANGINFO(MON_11),
LANGINFO(MON_12),
LANGINFO(ABMON_1),
LANGINFO(ABMON_2),
LANGINFO(ABMON_3),
LANGINFO(ABMON_4),
LANGINFO(ABMON_5),
LANGINFO(ABMON_6),
LANGINFO(ABMON_7),
LANGINFO(ABMON_8),
LANGINFO(ABMON_9),
LANGINFO(ABMON_10),
LANGINFO(ABMON_11),
LANGINFO(ABMON_12),
#ifdef RADIXCHAR
LANGINFO(RADIXCHAR),
LANGINFO(THOUSEP),
LANGINFO(CRNCYSTR),
#endif
LANGINFO(D_T_FMT),
LANGINFO(D_FMT),
LANGINFO(T_FMT),
LANGINFO(AM_STR),
LANGINFO(PM_STR),
#ifdef CODESET
LANGINFO(CODESET),
#endif
#ifdef T_FMT_AMPM
LANGINFO(T_FMT_AMPM),
#endif
#ifdef ERA
LANGINFO(ERA),
#endif
#ifdef ERA_D_FMT
LANGINFO(ERA_D_FMT),
#endif
#ifdef ERA_D_T_FMT
LANGINFO(ERA_D_T_FMT),
#endif
#ifdef ERA_T_FMT
LANGINFO(ERA_T_FMT),
#endif
#ifdef ALT_DIGITS
LANGINFO(ALT_DIGITS),
#endif
#ifdef YESEXPR
LANGINFO(YESEXPR),
#endif
#ifdef NOEXPR
LANGINFO(NOEXPR),
#endif
#ifdef _DATE_FMT
LANGINFO(_DATE_FMT),
#endif
{0, 0}
};
static PyObject *
_locale_nl_langinfo_impl(PyObject *module, int item)
{
int i;
for (i = 0; langinfo_constants[i].name; i++)
if (langinfo_constants[i].value == item) {
const char *result = nl_langinfo(item);
result = result != NULL ? result : "";
return PyUnicode_DecodeLocale(result, NULL);
}
PyErr_SetString(PyExc_ValueError, "unsupported langinfo constant");
return NULL;
}
#endif
#ifdef HAVE_LIBINTL_H
static PyObject *
_locale_gettext_impl(PyObject *module, const char *in)
{
return PyUnicode_DecodeLocale(gettext(in), NULL);
}
static PyObject *
_locale_dgettext_impl(PyObject *module, const char *domain, const char *in)
{
return PyUnicode_DecodeLocale(dgettext(domain, in), NULL);
}
static PyObject *
_locale_dcgettext_impl(PyObject *module, const char *domain,
const char *msgid, int category)
{
return PyUnicode_DecodeLocale(dcgettext(domain,msgid,category), NULL);
}
static PyObject *
_locale_textdomain_impl(PyObject *module, const char *domain)
{
domain = textdomain(domain);
if (!domain) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return PyUnicode_DecodeLocale(domain, NULL);
}
static PyObject *
_locale_bindtextdomain_impl(PyObject *module, const char *domain,
PyObject *dirname_obj)
{
const char *dirname, *current_dirname;
PyObject *dirname_bytes = NULL, *result;
if (!strlen(domain)) {
PyErr_SetString(get_locale_state(module)->Error,
"domain must be a non-empty string");
return 0;
}
if (dirname_obj != Py_None) {
if (!PyUnicode_FSConverter(dirname_obj, &dirname_bytes))
return NULL;
dirname = PyBytes_AsString(dirname_bytes);
} else {
dirname_bytes = NULL;
dirname = NULL;
}
current_dirname = bindtextdomain(domain, dirname);
if (current_dirname == NULL) {
Py_XDECREF(dirname_bytes);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
result = PyUnicode_DecodeLocale(current_dirname, NULL);
Py_XDECREF(dirname_bytes);
return result;
}
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
static PyObject *
_locale_bind_textdomain_codeset_impl(PyObject *module, const char *domain,
const char *codeset)
{
codeset = bind_textdomain_codeset(domain, codeset);
if (codeset) {
return PyUnicode_DecodeLocale(codeset, NULL);
}
Py_RETURN_NONE;
}
#endif
#endif
static PyObject *
_locale_getencoding_impl(PyObject *module)
{
return _Py_GetLocaleEncodingObject();
}
static struct PyMethodDef PyLocale_Methods[] = {
_LOCALE_SETLOCALE_METHODDEF
_LOCALE_LOCALECONV_METHODDEF
#ifdef HAVE_WCSCOLL
_LOCALE_STRCOLL_METHODDEF
#endif
#ifdef HAVE_WCSXFRM
_LOCALE_STRXFRM_METHODDEF
#endif
#if defined(MS_WINDOWS)
_LOCALE__GETDEFAULTLOCALE_METHODDEF
#endif
#ifdef HAVE_LANGINFO_H
_LOCALE_NL_LANGINFO_METHODDEF
#endif
#ifdef HAVE_LIBINTL_H
_LOCALE_GETTEXT_METHODDEF
_LOCALE_DGETTEXT_METHODDEF
_LOCALE_DCGETTEXT_METHODDEF
_LOCALE_TEXTDOMAIN_METHODDEF
_LOCALE_BINDTEXTDOMAIN_METHODDEF
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
_LOCALE_BIND_TEXTDOMAIN_CODESET_METHODDEF
#endif
#endif
_LOCALE_GETENCODING_METHODDEF
{NULL, NULL}
};
static int
_locale_exec(PyObject *module)
{
#ifdef HAVE_LANGINFO_H
int i;
#endif
#define ADD_INT(module, value) \
do { \
if (PyModule_AddIntConstant(module, #value, value) < 0) { \
return -1; \
} \
} while (0)
ADD_INT(module, LC_CTYPE);
ADD_INT(module, LC_TIME);
ADD_INT(module, LC_COLLATE);
ADD_INT(module, LC_MONETARY);
#ifdef LC_MESSAGES
ADD_INT(module, LC_MESSAGES);
#endif
ADD_INT(module, LC_NUMERIC);
ADD_INT(module, LC_ALL);
ADD_INT(module, CHAR_MAX);
_locale_state *state = get_locale_state(module);
state->Error = PyErr_NewException("locale.Error", NULL, NULL);
if (state->Error == NULL) {
return -1;
}
Py_INCREF(get_locale_state(module)->Error);
if (PyModule_AddObject(module, "Error", get_locale_state(module)->Error) < 0) {
Py_DECREF(get_locale_state(module)->Error);
return -1;
}
#ifdef HAVE_LANGINFO_H
for (i = 0; langinfo_constants[i].name; i++) {
if (PyModule_AddIntConstant(module,
langinfo_constants[i].name,
langinfo_constants[i].value) < 0) {
return -1;
}
}
#endif
if (PyErr_Occurred()) {
return -1;
}
return 0;
#undef ADD_INT
}
static struct PyModuleDef_Slot _locale_slots[] = {
{Py_mod_exec, _locale_exec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{0, NULL}
};
static int
locale_traverse(PyObject *module, visitproc visit, void *arg)
{
_locale_state *state = get_locale_state(module);
Py_VISIT(state->Error);
return 0;
}
static int
locale_clear(PyObject *module)
{
_locale_state *state = get_locale_state(module);
Py_CLEAR(state->Error);
return 0;
}
static void
locale_free(PyObject *module)
{
locale_clear(module);
}
static struct PyModuleDef _localemodule = {
PyModuleDef_HEAD_INIT,
"_locale",
locale__doc__,
sizeof(_locale_state),
PyLocale_Methods,
_locale_slots,
locale_traverse,
locale_clear,
(freefunc)locale_free,
};
PyMODINIT_FUNC
PyInit__locale(void)
{
return PyModuleDef_Init(&_localemodule);
}