Skip to content

Commit

Permalink
fix: ibex-windows cmd not support i18n charset (#1029)
Browse files Browse the repository at this point in the history
* fix: ibex-windows cmd not support i18n charset

- using native function, support ANSI CodePage from system

fix #1018

* fix: ibex-windows cmd not support i18n charset

- add: fake linux function
  • Loading branch information
zzmark authored Aug 17, 2024
1 parent d415d59 commit 5402d79
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 3 deletions.
10 changes: 10 additions & 0 deletions ibex/cmd_nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ func CmdStart(cmd *exec.Cmd) error {
func CmdKill(cmd *exec.Cmd) error {
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}

func ansiToUtf8(mbcs []byte) (string, error) {
// fake
return string(mbcs), nil
}

func utf8ToAnsi(utf8 string) (string, error) {
// fake
return utf8, nil
}
50 changes: 50 additions & 0 deletions ibex/cmd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,62 @@ package ibex

import (
"os/exec"
"unsafe"

"golang.org/x/sys/windows"
)

var kernel32 = windows.NewLazySystemDLL("kernel32")
var multiByteToWideChar = kernel32.NewProc("MultiByteToWideChar")
var wideCharToMultiByte = kernel32.NewProc("WideCharToMultiByte")

const CP_ACP = 0 // default to ANSI code page
const CP_OEMCP = 1 // default to OEM code page
const CP_THREAD_ACP = 3 // current thread's ANSI code page

func CmdStart(cmd *exec.Cmd) error {
return cmd.Start()
}

func CmdKill(cmd *exec.Cmd) error {
return cmd.Process.Kill()
}

func ansiToUtf8(mbcs []byte) (string, error) {
if mbcs == nil || len(mbcs) <= 0 {
return "", nil
}
// https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar#syntax
// ansi -> utf16
size, _, _ := multiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&mbcs[0])), uintptr(len(mbcs)), uintptr(0), 0)
if size <= 0 {
return "", windows.GetLastError()
}
utf16 := make([]uint16, size)
rc, _, _ := multiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&mbcs[0])), uintptr(len(mbcs)), uintptr(unsafe.Pointer(&utf16[0])), size)
if rc == 0 {
return "", windows.GetLastError()
}
return windows.UTF16ToString(utf16), nil
}

func utf8ToAnsi(utf8 string) (string, error) {
utf16, err := windows.UTF16FromString(utf8)
if err != nil {
return "", err
}
// https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
size, _, _ := wideCharToMultiByte.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&utf16[0])), uintptr(len(utf16)), uintptr(0), 0, uintptr(0), uintptr(0))
if size <= 0 {
return "", windows.GetLastError()
}
mbcs := make([]byte, size)
rc, _, _ := wideCharToMultiByte.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&utf16[0])), uintptr(len(utf16)), uintptr(unsafe.Pointer(&mbcs[0])), size, uintptr(0), uintptr(0))
if rc == 0 {
return "", windows.GetLastError()
}
if mbcs[size-1] == 0 {
mbcs = mbcs[:size-1]
}
return string(mbcs), nil
}
65 changes: 62 additions & 3 deletions ibex/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,49 @@ func (t *Task) SetAlive(pa bool) {

func (t *Task) GetStdout() string {
t.Lock()
out := t.Stdout.String()

buf := t.Stdout

var out string

switch runtime.GOOS {
// window exec out charset is ANSI, convert to utf-8. (pwsh and cmd same)
case "windows":
b := buf.Bytes()
decoded, err := ansiToUtf8(b)
if err != nil {
log.Printf("E! convert out to windows-ansi fail: %v", err)
out = string(b)
}
out = decoded
default:
out = buf.String()
}

t.Unlock()
return out
}

func (t *Task) GetStderr() string {
t.Lock()
out := t.Stderr.String()

buf := t.Stderr

var out string
switch runtime.GOOS {
// window exec out charset is ANSI, convert to utf-8. (pwsh and cmd same)
case "windows":
b := buf.Bytes()
decoded, err := ansiToUtf8(b)
if err != nil {
log.Printf("E! convert out to windows-ansi fail: %v", err)
out = string(b)
}
out = decoded
default:
out = buf.String()
}

t.Unlock()
return out
}
Expand Down Expand Up @@ -171,8 +206,32 @@ func (t *Task) prepare() error {

switch runtime.GOOS {
case "windows":
// window command(cmd) only support ANSI and CRLF
// if change to powershell , not convert script and stdin to ANSI and CRLF
encodedStdin, err := utf8ToAnsi(stdin)
if err != nil {
log.Printf("E! convert stdin[%s] to windows-ansi fail: %v", stdin, err)
return err
}
stdin = encodedStdin

encodedArgs, err := utf8ToAnsi(args)
if err != nil {
log.Printf("E! convert args[%s] to windows-ansi fail: %v", args, err)
return err
}
args = encodedArgs

script = strings.ReplaceAll(script, "\r", "")
script = strings.ReplaceAll(script, "\n", "\r\n")
encodedScript, err := utf8ToAnsi(script)
if err != nil {
log.Printf("E! convert script to windows-ansi fail: %v", err)
return err
}

scriptFile := filepath.Join(IdDir, "script.bat")
_, err = file.WriteString(scriptFile, fmt.Sprintf("@echo off\r\n%s", script))
_, err = file.WriteString(scriptFile, fmt.Sprintf("@echo off\r\n%s", encodedScript))
if err != nil {
log.Printf("E! write script to %s fail: %v", scriptFile, err)
return err
Expand Down

0 comments on commit 5402d79

Please sign in to comment.