返回 DeepSeek-Reasonix
locale.go
根目录 / internal / localeenv / locale.go
1 // Package localeenv supplies a UTF-8 default for child processes without
2 // replacing explicitly configured locale semantics.
3 package localeenv
4
5 import (
6 "context"
7 "runtime"
8 "strings"
9 "sync"
10 "time"
11
12 "reasonix/internal/proc"
13 )
14
15 var hostDefault = sync.OnceValue(discoverUTF8)
16
17 // DefaultUTF8 returns a copy only when a default is needed. Empty locale
18 // variables have the same meaning as absent variables. Explicit C/POSIX and
19 // category overrides remain user-owned; this never sets LC_ALL.
20 func DefaultUTF8(env []string) []string {
21 if runtime.GOOS == "windows" || hasLocale(env) {
22 return env
23 }
24 return withDefault(env, hostDefault())
25 }
26
27 func hasLocale(env []string) bool {
28 for _, item := range env {
29 key, value, _ := strings.Cut(item, "=")
30 if (key == "LANG" || key == "LC_ALL" || key == "LC_CTYPE") && value != "" {
31 return true
32 }
33 }
34 return false
35 }
36
37 func withDefault(env []string, locale string) []string {
38 if locale == "" || hasLocale(env) {
39 return env
40 }
41 out := make([]string, 0, len(env)+1)
42 for _, item := range env {
43 if !strings.HasPrefix(item, "LANG=") {
44 out = append(out, item)
45 }
46 }
47 return append(out, "LANG="+locale)
48 }
49
50 func discoverUTF8() string {
51 // Probe once, without inheriting credentials or relying on the user's
52 // PATH. Do not invent a locale that a minimal Linux image lacks.
53 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
54 defer cancel()
55 cmd := proc.CommandContext(ctx, "/usr/bin/locale", "-a")
56 cmd.Env = []string{"LC_ALL=C", "PATH=/usr/bin:/bin"}
57 output, err := cmd.Output()
58 if err != nil {
59 return ""
60 }
61 return selectUTF8(string(output))
62 }
63
64 func selectUTF8(locales string) string {
65 available := strings.Fields(locales)
66 for _, preferred := range []string{"c.utf8", "en_us.utf8"} {
67 for _, locale := range available {
68 if strings.ReplaceAll(strings.ToLower(locale), "-", "") == preferred {
69 return locale
70 }
71 }
72 }
73 for _, locale := range available {
74 if strings.HasSuffix(strings.ReplaceAll(strings.ToLower(locale), "-", ""), ".utf8") {
75 return locale
76 }
77 }
78 return ""
79 }
80
80 lines GO