Skip to content

i18n: fix non-ASCII mangling under default C locale (#203) - #205

Merged
got3nks merged 3 commits into
amule-org:masterfrom
got3nks:fix/init-locale
Jun 20, 2026
Merged

i18n: fix non-ASCII mangling under default C locale (#203)#205
got3nks merged 3 commits into
amule-org:masterfrom
got3nks:fix/init-locale

Conversation

@got3nks

@got3nks got3nks commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Fix #203: amuled and several smaller binaries depend on the process locale for UTF-8 handling, and mangle non-ASCII text under the default C/POSIX locale common in headless Docker / systemd / cron deployments where LANG/LC_ALL is unset.

  • New helper aMuleInitLocale() in src/libs/common/. Calls setlocale(LC_ALL, "") to pick the user's locale up from the environment, then forces LC_NUMERIC back to C so libc printf/scanf decimal handling stays portable (avoids the de_DE.UTF-8 trap where "%.2f" of 3.14 silently becomes "3,14").
  • Wired into the binaries that had no locale init at all: amuled (the reported bug), fileview, and cas (plain C — inline setlocale pair rather than the C++ helper).
  • Migrated amulecmd / amuleweb from the existing setlocale(LC_ALL, "") at ExternalConnector.cpp:681 (added in Changelog #626) to the new helper. Behaviour change called out: LC_NUMERIC is now always C for the connectors regardless of the user's environment.

Left alone: amule, amule-remote-gui, alc, alcc, wxcas — they all drive locale through wxLocale (m_locale.Init() / InitLocale()), which calls setlocale internally and is not affected by this class of bug.

Test plan

  • Configure + build everything on macOS APFS with -DBUILD_EVERYTHING=YES. Exit 0, zero new errors, zero new warnings, all touched targets (amuled, amulecmd, amuleweb, fileview, cas) build cleanly. amule, amulegui, alc, alcc, wxcas also build (unchanged behaviour confirmed).
  • CI matrix on Ubuntu Debug+Release, macOS Debug+Release, mingw-w64 Debug+Release, plus I18n CI.

Notes for reviewers

Three commits, each separately revertable:

  1. Introduce helper (pure addition, zero behaviour change).
  2. Wire amuled + fileview + cas (the actual bugfix).
  3. Migrate connectors from inline setlocale to the helper (small behaviour change: LC_NUMERIC=C for amulecmd/amuleweb).

got3nks added 3 commits June 20, 2026 18:08
Self-contained 2-line idiom that (a) picks LC_CTYPE etc. up from the
environment via setlocale(LC_ALL, "") so wxConvLibc emits real UTF-8
instead of mangling non-ASCII bytes under the default "C" locale, and
(b) forces LC_NUMERIC back to "C" so libc printf/scanf decimal handling
stays portable across user locales (e.g. de_DE.UTF-8 turning "%.2f" of
3.14 into "3,14" silently).

Pure addition — no behaviour change in this commit. Callers wired in
follow-ups.

Refs amule-org#203.
…ing under C locale (amule-project#203)

Three binaries that previously did no locale init at all stayed on the
default "C" locale for their whole lifetime, which made wxConvLibc
(and unicode2char() in StringFunctions.cpp) collapse every non-ASCII
codepoint into '?' or escape it. The classic symptom is a filename
like "La.mujer.danesa.1x02.Un.niño.destrozado" surfacing as
"La.mujer.danesa.1x02.Un.ni\361o" in amuled output, amulecmd, the web
UI, etc. Common deployment trigger: Docker / systemd / cron with
LANG/LC_ALL unset.

Three call-sites:
  * src/amuled.cpp -- aMuleInitLocale() at the top of
    CamuleDaemonApp::Initialize(), right after wxAppConsole::Initialize
    returns and before any wxString -> char* conversion fires.
  * src/utils/fileview/FileView.cpp -- aMuleInitLocale() at the top
    of OnInitCmdLine() (CFileView has no OnInit override, so this is
    the earliest hook wxApp calls).
  * src/utils/cas/cas.c -- inline setlocale(LC_ALL, "") + LC_NUMERIC=C
    at the top of main(). cas is plain C, so calling the C++ helper
    would require linkage shimming for no real win; the inline pair
    matches the helper's policy verbatim.

amule, amule-remote-gui, alc, alcc, and wxcas already drive locale
through wxLocale (m_locale.Init() / InitLocale()), so they're left
alone -- the wxLocale codepath calls setlocale internally and was
never broken in the C-locale sense.

amulecmd and amuleweb get migrated to the new helper in a follow-up
commit so this commit's diff is just "add calls, no replace".

Closes amule-org#203.
…RIC=C)

ExternalConnector.cpp has been calling setlocale(LC_ALL, "") for the
connector binaries since amule-project#626. That fixed unicode2char() output for
non-ASCII filenames but left LC_NUMERIC at whatever the user's locale
specified, which silently breaks any libc printf/scanf decimal path
under e.g. de_DE.UTF-8 ("%.2f" of 3.14 -> "3,14"). The aMuleInitLocale
helper added in the previous commits forces LC_NUMERIC back to "C"
after the LC_ALL grab, eliminating that trap.

Behaviour change: LC_NUMERIC is now always "C" for amulecmd and
amuleweb regardless of the user's environment. Any code that was
silently depending on the user-locale decimal separator in those
binaries now sees "C" decimal. None known to do so; flagging here so
a future bisect can find this line if something surfaces.

Refs amule-org#203.
@got3nks
got3nks merged commit 6ecde95 into amule-org:master Jun 20, 2026
10 checks passed
@got3nks
got3nks deleted the fix/init-locale branch June 20, 2026 16:41
got3nks added a commit that referenced this pull request Jun 20, 2026
UPnPBase.cpp has four call sites that rely on tolower() producing
ASCII semantics:

  * stdStringIsEqualCI / stdStringStartsWithCI -- the two
    case-insensitive std::string helpers used throughout the file.
  * CUPnPService::Execute -- direct comparison against 'i' and 'n' to
    validate the UPnP argument direction string ("in" / "out").
  * CUPnPControlPoint::Callback BYEBYE case -- transforms the
    discovered device-type URN to lowercase before matching against
    UPnP::Device::IGW.

Under tr_TR.UTF-8 (and any other Turkish-locale variant), libc
tolower('I') returns U+0131 (dotless i) instead of 'i'. This
silently breaks UPnP direction validation, device-type matching,
namespace lowercase comparisons, and HTTP-header lowercasing --
amuled's UPnP auto-portforward stops working for Turkish-locale
users.

Before #205 (i18n: fix non-ASCII mangling under default C locale),
amuled stayed on the C locale throughout its lifetime and these
sites were silently safe. The fix in #205 made amuled pick LC_CTYPE
up from the environment so unicode2char() emits real UTF-8 -- which
exposed the latent Turkish-i issue documented above.

Wrap each tolower() scope in CCtypeAsciiScope -- the project's
existing RAII helper that pins LC_CTYPE = "C" for the duration of
its scope and restores it on destruction. Same pattern already used
in CamuleFileConfig.h (#852) and MaxMindDBDatabase.cpp.

amule (GUI) was already exposed to this bug via wxLocale's
setlocale(LC_ALL) on Turkish-language installs; the wrap fixes it
there too as a free side effect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

amuled depends on the process locale for UTF-8 handling; mangles non-ASCII filenames under the default C locale

1 participant