Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ibex-windows cmd not support i18n charset #1029

Merged
merged 2 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading