diff --git a/userspace/libraries/LibC/CMakeLists.txt b/userspace/libraries/LibC/CMakeLists.txt index 12ab129f..72ac0634 100644 --- a/userspace/libraries/LibC/CMakeLists.txt +++ b/userspace/libraries/LibC/CMakeLists.txt @@ -15,6 +15,7 @@ set(LIBC_SOURCES inttypes.cpp langinfo.cpp libgen.cpp + libintl.cpp locale.cpp malloc.cpp math.cpp diff --git a/userspace/libraries/LibC/libintl.cpp b/userspace/libraries/LibC/libintl.cpp new file mode 100644 index 00000000..c8534a68 --- /dev/null +++ b/userspace/libraries/LibC/libintl.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include + +struct text_domain_t +{ + char* domain_name; + char* codeset; + char* dir_name; +}; + +static text_domain_t* s_text_domains = nullptr; +static size_t s_text_domain_count = 0; +static char* s_current_domain = nullptr; + +static text_domain_t* find_text_domain(const char* domainname) +{ + for (size_t i = 0; i < s_text_domain_count; i++) + { + if (strcmp(s_text_domains[i].domain_name, domainname) != 0) + continue; + return &s_text_domains[i]; + } + + return nullptr; +} + +static text_domain_t* bind_new_textdomain(const char* domainname, const char* codeset, const char* dirname) +{ + auto* new_text_domains = static_cast(realloc(s_text_domains, (s_text_domain_count + 1) * sizeof(text_domain_t))); + if (new_text_domains == nullptr) + return nullptr; + s_text_domains = new_text_domains; + + auto& new_text_domain = new_text_domains[s_text_domain_count]; + + new_text_domain.domain_name = strdup(domainname); + new_text_domain.dir_name = strdup(dirname); + new_text_domain.codeset = strdup(codeset); + + if (!new_text_domain.domain_name || !new_text_domain.codeset || !new_text_domain.dir_name) + { + if (new_text_domain.domain_name) + free(new_text_domain.domain_name); + if (new_text_domain.codeset) + free(new_text_domain.codeset); + if (new_text_domain.dir_name) + free(new_text_domain.dir_name); + return nullptr; + } + + s_text_domain_count++; + + return &new_text_domain; +} + +char* bindtextdomain(const char* domainname, const char* dirname) +{ + if (domainname == nullptr || *domainname == '\0') + return nullptr; + + auto* text_domain = find_text_domain(domainname); + + if (dirname == nullptr) + { + if (text_domain != nullptr) + return text_domain->dir_name; + return const_cast(""); + } + + if (text_domain) + { + char* dirname_copy = strdup(dirname); + if (dirname_copy == nullptr) + return nullptr; + + if (text_domain->dir_name) + free(text_domain->dir_name); + + text_domain->dir_name = dirname_copy; + return text_domain->dir_name; + } + + auto* new_current_domain = bind_new_textdomain(domainname, nl_langinfo(CODESET), dirname); + if (new_current_domain == nullptr) + return nullptr; + return new_current_domain->domain_name; +} + +char* bind_textdomain_codeset(const char* domainname, const char* codeset) +{ + if (domainname == nullptr || *domainname == '\0') + return nullptr; + + auto* text_domain = find_text_domain(domainname); + + if (codeset == nullptr || *codeset == '\0') + { + if (text_domain == nullptr) + return nl_langinfo(CODESET); + return text_domain->codeset; + } + + if (text_domain) + { + char* codeset_copy = strdup(codeset); + if (codeset_copy == nullptr) + return nullptr; + + free(text_domain->codeset); + text_domain->codeset = codeset_copy; + return codeset_copy; + } + + auto* new_text_domain = bind_new_textdomain(domainname, codeset, ""); + if (new_text_domain == nullptr) + return nullptr; + return new_text_domain->codeset; +} + +char* textdomain(const char* domainname) +{ + if (domainname == nullptr || *domainname == '\0') + { + if (s_current_domain == nullptr) + s_current_domain = strdup("messages"); + return s_current_domain; + } + + char* new_current_domain = strdup(domainname); + if (new_current_domain == nullptr) + return nullptr; + + if (s_current_domain != nullptr) + free(s_current_domain); + + s_current_domain = new_current_domain; + return s_current_domain; +} + +char* dgettext(const char* domainname, const char* msgid) +{ + return dgettext_l(domainname, msgid, __getlocale(LC_MESSAGES)); +} + +char* dgettext_l(const char* domainname, const char* msgid, locale_t locale) +{ + return dngettext_l(domainname, msgid, nullptr, 1, locale); +} + +char* dcgettext(const char* domainname, const char* msgid, int category) +{ + return dcgettext_l(domainname, msgid, category, __getlocale(LC_MESSAGES)); +} + +char* dcgettext_l(const char* domainname, const char* msgid, int category, locale_t locale) +{ + return dcngettext_l(domainname, msgid, nullptr, 1, category, locale); +} + +char* dngettext(const char* domainname, const char* msgid, const char* msgid_plural, unsigned long int n) +{ + return dngettext_l(domainname, msgid, msgid_plural, n, __getlocale(LC_MESSAGES)); +} + +char* dngettext_l(const char* domainname, const char* msgid, const char* msgid_plural, unsigned long int n, locale_t locale) +{ + return dcngettext_l(domainname, msgid, msgid_plural, n, 0, locale); +} + +char* dcngettext(const char* domainname, const char* msgid, const char* msgid_plural, unsigned long int n, int category) +{ + return dcngettext_l(domainname, msgid, msgid_plural, n, category, __getlocale(LC_MESSAGES)); +} + +char* dcngettext_l(const char* domainname, const char* msgid, const char* msgid_plural, unsigned long int n, int category, locale_t locale) +{ + // TODO: do actual translations :D + // FIXME: handle domain codeset + + (void)locale; + (void)domainname; + (void)category; + return const_cast((n == 1) ? msgid : msgid_plural); +} + +char* gettext(const char* msgid) +{ + return gettext_l(msgid, __getlocale(LC_MESSAGES)); +} + +char* gettext_l(const char* msgid, locale_t locale) +{ + return dgettext_l(s_current_domain, msgid, locale); +} + +char* ngettext(const char* msgid, const char* msgid_plural, unsigned long int n) +{ + return ngettext_l(msgid, msgid_plural, n, __getlocale(LC_MESSAGES)); +} + +char* ngettext_l(const char* msgid, const char* msgid_plural, unsigned long int n, locale_t locale) +{ + return dngettext_l(s_current_domain, msgid, msgid_plural, n, locale); +}