Skip to content

Commit 3a7c731

Browse files
committed
feat: support utf8
1 parent 10b4410 commit 3a7c731

File tree

2 files changed

+99
-25
lines changed

2 files changed

+99
-25
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ project(sshpass)
55
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
66

77
add_executable(${PROJECT_NAME} main.c argparse.c)
8-
8+
add_compile_options(/W4 /utf8)
99
set_property(TARGET ${PROJECT_NAME} PROPERTY
1010
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
1111

main.c

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#include <Windows.h>
2+
#include <wchar.h>
23
#include <process.h>
34
#include <stdint.h>
45
#include <stdio.h>
56
#include <stdlib.h>
67
#include <string.h>
78
#include <strsafe.h>
9+
#include <locale.h>
810

911
#include "argparse.h"
1012

@@ -16,15 +18,15 @@ static const char* const usages[] = {
1618
typedef struct {
1719
enum { PWT_STDIN, PWT_FILE, PWT_FD, PWT_PASS } pwtype;
1820
union {
19-
const char* filename;
21+
const wchar_t* filename;
2022
int64_t fd;
2123
const char* password;
2224
} pwsrc;
2325

2426
const char* passPrompt;
2527
int verbose;
2628

27-
char* cmd;
29+
wchar_t* cmd;
2830
} Args;
2931

3032
typedef struct {
@@ -38,19 +40,87 @@ typedef struct {
3840
HANDLE events[2];
3941
} Context;
4042

41-
static void ParseArgs(int argc, const char* argv[], Context* ctx);
43+
static void ParseArgs(int argc, const wchar_t** wargv, char** argv, Context* ctx);
4244
static void WritePass(Context* ctx);
4345
static HRESULT CreatePseudoConsoleAndPipes(HPCON* hpcon, Context* ctx);
44-
static HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEXA* startupInfo,
46+
static HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEXW* startupInfo,
4547
HPCON hpcon);
4648
static void __cdecl PipeListener(LPVOID);
4749
static void __cdecl InputHandlerThread(LPVOID);
4850

49-
int main(int argc, const char* argv[]) {
51+
static wchar_t* ToUtf16(const char* utf8) {
52+
if (utf8 == NULL) {
53+
return NULL;
54+
}
55+
wchar_t* utf16 = NULL;
56+
int buf_size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
57+
if (buf_size == 0) {
58+
return NULL;
59+
}
60+
utf16 = malloc(sizeof(wchar_t) * (buf_size + 1)); // free when process exit
61+
buf_size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, buf_size);
62+
if (buf_size == 0) {
63+
free(utf16);
64+
return NULL;
65+
}
66+
utf16[buf_size] = L'\0';
67+
return utf16;
68+
}
69+
70+
static char* ToUtf8(const wchar_t* wstr) {
71+
if (wstr == NULL) {
72+
return NULL;
73+
}
74+
75+
int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
76+
if (len == 0) {
77+
return NULL;
78+
}
79+
80+
char* utf8 = (char*)malloc(len); // free when the process exit
81+
if (utf8 == NULL) {
82+
return NULL;
83+
}
84+
85+
if (WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8, len, NULL, NULL) == 0) {
86+
free(utf8);
87+
return NULL;
88+
}
89+
90+
return utf8;
91+
}
92+
93+
static char** ConvertArgcToMultiByte(int argc, const wchar_t* argv[])
94+
{
95+
char** ret = malloc(argc * sizeof(char*)); // free when the process exit
96+
if (ret == NULL) {
97+
return NULL;
98+
}
99+
for (int i = 0; i < argc; i++) {
100+
ret[i] = ToUtf8(argv[i]);
101+
}
102+
return ret;
103+
}
104+
105+
static void SetupConsole(void) {
106+
SetConsoleOutputCP(CP_UTF8);
107+
SetConsoleCP(CP_UTF8);
108+
setlocale(LC_ALL, ".UTF-8");
109+
}
110+
111+
112+
int wmain(int argc, const wchar_t* argv[]) {
50113
Context ctx;
51-
uint32_t childExitCode = 0;
114+
DWORD childExitCode = 0;
115+
116+
SetupConsole();
52117

53-
ParseArgs(argc, argv, &ctx);
118+
char** argvUtf8 = ConvertArgcToMultiByte(argc, argv);
119+
if(argvUtf8 == NULL) {
120+
return EXIT_FAILURE;
121+
}
122+
123+
ParseArgs(argc, argv, argvUtf8, &ctx);
54124

55125
HRESULT hr = E_UNEXPECTED;
56126

@@ -72,10 +142,10 @@ int main(int argc, const char* argv[]) {
72142
if (S_OK == hr) {
73143
HANDLE pipeListener = (HANDLE) _beginthread(PipeListener, 0, &ctx);
74144

75-
STARTUPINFOEXA startupInfo = {0};
145+
STARTUPINFOEXW startupInfo = {0};
76146
if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hpcon)) {
77147
PROCESS_INFORMATION cmdProc;
78-
hr = CreateProcessA(NULL, (char*) ctx.args.cmd, NULL, NULL, FALSE,
148+
hr = CreateProcessW(NULL, ctx.args.cmd, NULL, NULL, FALSE,
79149
EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
80150
&startupInfo.StartupInfo, &cmdProc)
81151
? S_OK
@@ -109,18 +179,19 @@ int main(int argc, const char* argv[]) {
109179
}
110180

111181
CloseHandle(ctx.events[0]);
112-
}
182+
}
113183
return S_OK == hr ? childExitCode : EXIT_FAILURE;
114184
}
115185

116-
static void ParseArgs(int argc, const char* argv[], Context* ctx) {
186+
static void ParseArgs(int argc, const wchar_t* wargv[], char** argv, Context* ctx) {
117187
const char* filename = NULL;
118188
int64_t number = 0;
119189
const char* strpass = NULL;
120190
int envPass = 0;
121191

122192
const char* passPrompt = NULL;
123193
int verbose = 0;
194+
int totalArgc = argc;
124195

125196
struct argparse_option options[] = {
126197
OPT_HELP(),
@@ -142,7 +213,7 @@ static void ParseArgs(int argc, const char* argv[], Context* ctx) {
142213

143214
struct argparse argparse;
144215
argparse_init(&argparse, options, usages, ARGPARSE_STOP_AT_NON_OPTION);
145-
argc = argparse_parse(&argparse, argc, argv);
216+
argc = argparse_parse(&argparse, argc, (const char**)argv);
146217
if (argc == 0) {
147218
argparse_usage(&argparse);
148219
exit(EXIT_FAILURE);
@@ -151,7 +222,7 @@ static void ParseArgs(int argc, const char* argv[], Context* ctx) {
151222
ctx->args.verbose = verbose;
152223
if (filename != NULL) {
153224
ctx->args.pwtype = PWT_FILE;
154-
ctx->args.pwsrc.filename = filename;
225+
ctx->args.pwsrc.filename = ToUtf16(filename);
155226
} else if (number != 0) {
156227
ctx->args.pwtype = PWT_FD;
157228
ctx->args.pwsrc.fd = number;
@@ -172,19 +243,22 @@ static void ParseArgs(int argc, const char* argv[], Context* ctx) {
172243
}
173244

174245
int cmdLen = 0;
246+
int parsedArgc = totalArgc - argc;
175247
for (int i = 0; i < argc; i++) {
176-
cmdLen += strlen(argv[i]) + 2;
248+
cmdLen += wcslen(wargv[i + parsedArgc]) + 2;
177249
}
178250

179-
ctx->args.cmd = malloc(sizeof(char) * cmdLen);
180-
memset(ctx->args.cmd, 0, sizeof(char) * cmdLen);
251+
ctx->args.cmd = malloc(sizeof(wchar_t) * cmdLen);
252+
memset(ctx->args.cmd, 0, sizeof(wchar_t) * cmdLen);
181253
for (int i = 0; i < argc; i++) {
182-
StringCchCatA(ctx->args.cmd, sizeof(char) * cmdLen, argv[i]);
183-
StringCchCatA(ctx->args.cmd, sizeof(char) * cmdLen, " ");
254+
StringCchCatW(ctx->args.cmd, sizeof(wchar_t) * cmdLen, wargv[i + parsedArgc]);
255+
StringCchCatW(ctx->args.cmd, sizeof(wchar_t) * cmdLen, L" ");
184256
}
185257

186258
if (ctx->args.verbose) {
187-
fprintf(stdout, "cmd: %s\n", ctx->args.cmd);
259+
char* cmd = ToUtf8(ctx->args.cmd);
260+
fprintf(stdout, "cmd: %s\n", cmd);
261+
free(cmd);
188262
}
189263
}
190264

@@ -219,15 +293,15 @@ static HRESULT CreatePseudoConsoleAndPipes(HPCON* hpcon, Context* ctx) {
219293
return hr;
220294
}
221295

222-
static HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEXA* startupInfo,
296+
static HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEXW* startupInfo,
223297
HPCON hpcon) {
224298
HRESULT hr = E_UNEXPECTED;
225299
if (startupInfo == NULL) {
226300
return hr;
227301
}
228302

229303
size_t attrListSize;
230-
startupInfo->StartupInfo.cb = sizeof(STARTUPINFOEXA);
304+
startupInfo->StartupInfo.cb = sizeof(STARTUPINFOEXW);
231305

232306
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
233307

@@ -290,7 +364,7 @@ static State ProcessOutput(Context* ctx, const char* buffer, DWORD len, State st
290364
return nextState;
291365
}
292366

293-
#define BUFFER_SIZE 1024
367+
#define BUFFER_SIZE 8192
294368

295369
static void __cdecl PipeListener(LPVOID arg) {
296370
Context* ctx = arg;
@@ -347,7 +421,7 @@ static void WritePass(Context* ctx) {
347421
WritePassHandle(ctx, (HANDLE) ctx->args.pwsrc.fd);
348422
break;
349423
case PWT_FILE: {
350-
HANDLE file = CreateFileA(ctx->args.pwsrc.filename, GENERIC_READ, FILE_SHARE_READ, NULL,
424+
HANDLE file = CreateFileW(ctx->args.pwsrc.filename, GENERIC_READ, FILE_SHARE_READ, NULL,
351425
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
352426
if (file != INVALID_HANDLE_VALUE) {
353427
WritePassHandle(ctx, file);
@@ -385,4 +459,4 @@ static void __cdecl InputHandlerThread(LPVOID arg) {
385459
}
386460

387461
SetConsoleMode(hStdin, mode);
388-
}
462+
}

0 commit comments

Comments
 (0)