Skip to content

Commit db0be66

Browse files
committed
Fix input locale detection on Windows
1 parent 924cb75 commit db0be66

4 files changed

Lines changed: 107 additions & 4 deletions

File tree

src/boxes.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ static void activateSystemEncoding()
498498
if (locale == NULL || setlocale(LC_ALL, locale) == NULL) {
499499
setlocale(LC_ALL, "");
500500
}
501-
UINT codepage = get_console_codepage(locale_charset());
501+
UINT codepage = get_console_codepage(get_default_encoding());
502502
SetConsoleOutputCP(codepage);
503503
SetConsoleCP(codepage);
504504
#else
@@ -547,12 +547,13 @@ int main(int argc, char *argv[])
547547

548548
/* Temporarily set the system encoding, for proper output of --help text etc. */
549549
activateSystemEncoding();
550-
encoding = locale_charset();
550+
const char *system_encoding = get_default_encoding();
551+
encoding = system_encoding;
551552

552553
handle_command_line(argc, argv);
553554

554555
/* Store system character encoding */
555-
encoding = check_encoding(opt.encoding, locale_charset());
556+
encoding = check_encoding(opt.encoding, system_encoding);
556557
log_debug(__FILE__, MAIN, "Character Encoding = %s\n", encoding);
557558

558559
color_output_enabled = check_color_support(opt.color);

src/cmdline.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "logging.h"
3939
#include "query.h"
4040
#include "tools.h"
41+
#include "unicode.h"
4142
#include "cmdline.h"
4243

4344

@@ -104,7 +105,7 @@ void usage_long(FILE *st)
104105
fprintf(st, " --no-kill-blank Retain leading/trailing blank lines on removal (like -k false)\n");
105106
fprintf(st, " -l, --list List available box designs w/ samples\n");
106107
fprintf(st, " -m, --mend Mend (repair) box\n");
107-
fprintf(st, " -n, --encoding <enc> Character encoding of input and output [default: %s]\n", locale_charset());
108+
fprintf(st, " -n, --encoding <enc> Character encoding of input and output [default: %s]\n", get_default_encoding());
108109
fprintf(st, " -p, --padding <fmt> Padding [default: none]\n");
109110
fprintf(st, " -q, --tag-query <qry> Query the list of designs by tag\n");
110111
fprintf(st, " -r, --remove Remove box\n");

src/unicode.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <stdio.h>
2424
#include <stdlib.h>
2525
#include <string.h>
26+
#include <strings.h>
2627

2728
#include <uniconv.h>
2829
#include <unictype.h>
@@ -57,6 +58,81 @@ const ucs4_t char_nul = 0x00000000;
5758

5859

5960

61+
#ifdef _WIN32
62+
static const char *get_locale_from_environment()
63+
{
64+
const char *locale = getenv("LC_ALL");
65+
if (locale == NULL || locale[0] == '\0') {
66+
locale = getenv("LC_CTYPE");
67+
}
68+
if (locale == NULL || locale[0] == '\0') {
69+
locale = getenv("LANG");
70+
}
71+
return locale != NULL && locale[0] != '\0' ? locale : NULL;
72+
}
73+
74+
75+
static int is_all_digits(const char *s)
76+
{
77+
if (s == NULL || s[0] == '\0') {
78+
return 0;
79+
}
80+
for (const char *p = s; *p != '\0'; p++) {
81+
if (*p < '0' || *p > '9') {
82+
return 0;
83+
}
84+
}
85+
return 1;
86+
}
87+
88+
89+
static const char *extract_locale_encoding(const char *locale)
90+
{
91+
static char encoding_buffer[32];
92+
if (locale == NULL) {
93+
return NULL;
94+
}
95+
96+
const char *dot = strrchr(locale, '.');
97+
if (dot == NULL || dot[1] == '\0') {
98+
return NULL;
99+
}
100+
101+
const char *start = dot + 1;
102+
const char *end = strchr(start, '@');
103+
size_t len = end == NULL ? strlen(start) : (size_t) (end - start);
104+
if (len == 0 || len >= sizeof(encoding_buffer)) {
105+
return NULL;
106+
}
107+
108+
memcpy(encoding_buffer, start, len);
109+
encoding_buffer[len] = '\0';
110+
if (strcasecmp(encoding_buffer, "UTF8") == 0
111+
|| strcasecmp(encoding_buffer, "UTF-8") == 0
112+
|| strcasecmp(encoding_buffer, "CP65001") == 0
113+
|| strcmp(encoding_buffer, "65001") == 0)
114+
{
115+
return "UTF-8";
116+
}
117+
if ((encoding_buffer[0] == 'C' || encoding_buffer[0] == 'c')
118+
&& (encoding_buffer[1] == 'P' || encoding_buffer[1] == 'p')
119+
&& is_all_digits(encoding_buffer + 2))
120+
{
121+
encoding_buffer[0] = 'C';
122+
encoding_buffer[1] = 'P';
123+
return encoding_buffer;
124+
}
125+
if (is_all_digits(encoding_buffer)) {
126+
static char codepage_buffer[32];
127+
snprintf(codepage_buffer, sizeof(codepage_buffer), "CP%s", encoding_buffer);
128+
return codepage_buffer;
129+
}
130+
return encoding_buffer;
131+
}
132+
#endif
133+
134+
135+
60136
int is_char_at(const uint32_t *text, const size_t idx, const ucs4_t expected_char)
61137
{
62138
return text != NULL && u32_cmp(text + idx, &expected_char, 1) == 0;
@@ -265,6 +341,22 @@ char *u32_strconv_to_arg(const uint32_t *src, const char *targetEncoding)
265341

266342

267343

344+
const char *get_default_encoding()
345+
{
346+
const char *system_encoding = locale_charset();
347+
348+
#ifdef _WIN32
349+
const char *env_encoding = extract_locale_encoding(get_locale_from_environment());
350+
if (env_encoding != NULL) {
351+
return check_encoding(env_encoding, system_encoding);
352+
}
353+
#endif
354+
355+
return system_encoding;
356+
}
357+
358+
359+
268360
const char *check_encoding(const char *manual_encoding, const char *system_encoding)
269361
{
270362
if (manual_encoding != NULL) {

src/unicode.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,15 @@ char *u32_strconv_to_output(const uint32_t *src);
195195
char *u32_strconv_to_arg(const uint32_t *src, const char *targetEncoding);
196196

197197

198+
/**
199+
* Determine the default character encoding for input and output text.
200+
* On Windows, this prefers an explicit encoding suffix from `LC_ALL`, `LC_CTYPE`,
201+
* or `LANG` when present; otherwise it falls back to the CRT locale.
202+
* @return the default encoding name
203+
*/
204+
const char *get_default_encoding();
205+
206+
198207
/**
199208
* Check if the given `manual_encoding` can be used to covert anything. This should reveal invalid encoding names that
200209
* have been specified on the command line. If no `manual_encoding` was specified, or if an invalid encoding is

0 commit comments

Comments
 (0)