1*b077aed3SPierre Pronchery /*
2*b077aed3SPierre Pronchery * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
3*b077aed3SPierre Pronchery *
4*b077aed3SPierre Pronchery * Licensed under the Apache License 2.0 (the "License"). You may not use
5*b077aed3SPierre Pronchery * this file except in compliance with the License. You can obtain a copy
6*b077aed3SPierre Pronchery * in the file LICENSE in the source distribution or at
7*b077aed3SPierre Pronchery * https://www.openssl.org/source/license.html
8*b077aed3SPierre Pronchery */
9*b077aed3SPierre Pronchery
10*b077aed3SPierre Pronchery #include <windows.h>
11*b077aed3SPierre Pronchery #include <stdlib.h>
12*b077aed3SPierre Pronchery #include <string.h>
13*b077aed3SPierre Pronchery #include <malloc.h>
14*b077aed3SPierre Pronchery
15*b077aed3SPierre Pronchery #if defined(CP_UTF8)
16*b077aed3SPierre Pronchery
17*b077aed3SPierre Pronchery static UINT saved_cp;
18*b077aed3SPierre Pronchery static int newargc;
19*b077aed3SPierre Pronchery static char **newargv;
20*b077aed3SPierre Pronchery
cleanup(void)21*b077aed3SPierre Pronchery static void cleanup(void)
22*b077aed3SPierre Pronchery {
23*b077aed3SPierre Pronchery int i;
24*b077aed3SPierre Pronchery
25*b077aed3SPierre Pronchery SetConsoleOutputCP(saved_cp);
26*b077aed3SPierre Pronchery
27*b077aed3SPierre Pronchery for (i = 0; i < newargc; i++)
28*b077aed3SPierre Pronchery free(newargv[i]);
29*b077aed3SPierre Pronchery
30*b077aed3SPierre Pronchery free(newargv);
31*b077aed3SPierre Pronchery }
32*b077aed3SPierre Pronchery
33*b077aed3SPierre Pronchery /*
34*b077aed3SPierre Pronchery * Incrementally [re]allocate newargv and keep it NULL-terminated.
35*b077aed3SPierre Pronchery */
validate_argv(int argc)36*b077aed3SPierre Pronchery static int validate_argv(int argc)
37*b077aed3SPierre Pronchery {
38*b077aed3SPierre Pronchery static int size = 0;
39*b077aed3SPierre Pronchery
40*b077aed3SPierre Pronchery if (argc >= size) {
41*b077aed3SPierre Pronchery char **ptr;
42*b077aed3SPierre Pronchery
43*b077aed3SPierre Pronchery while (argc >= size)
44*b077aed3SPierre Pronchery size += 64;
45*b077aed3SPierre Pronchery
46*b077aed3SPierre Pronchery ptr = realloc(newargv, size * sizeof(newargv[0]));
47*b077aed3SPierre Pronchery if (ptr == NULL)
48*b077aed3SPierre Pronchery return 0;
49*b077aed3SPierre Pronchery
50*b077aed3SPierre Pronchery (newargv = ptr)[argc] = NULL;
51*b077aed3SPierre Pronchery } else {
52*b077aed3SPierre Pronchery newargv[argc] = NULL;
53*b077aed3SPierre Pronchery }
54*b077aed3SPierre Pronchery
55*b077aed3SPierre Pronchery return 1;
56*b077aed3SPierre Pronchery }
57*b077aed3SPierre Pronchery
process_glob(WCHAR * wstr,int wlen)58*b077aed3SPierre Pronchery static int process_glob(WCHAR *wstr, int wlen)
59*b077aed3SPierre Pronchery {
60*b077aed3SPierre Pronchery int i, slash, udlen;
61*b077aed3SPierre Pronchery WCHAR saved_char;
62*b077aed3SPierre Pronchery WIN32_FIND_DATAW data;
63*b077aed3SPierre Pronchery HANDLE h;
64*b077aed3SPierre Pronchery
65*b077aed3SPierre Pronchery /*
66*b077aed3SPierre Pronchery * Note that we support wildcard characters only in filename part
67*b077aed3SPierre Pronchery * of the path, and not in directories. Windows users are used to
68*b077aed3SPierre Pronchery * this, that's why recursive glob processing is not implemented.
69*b077aed3SPierre Pronchery */
70*b077aed3SPierre Pronchery /*
71*b077aed3SPierre Pronchery * Start by looking for last slash or backslash, ...
72*b077aed3SPierre Pronchery */
73*b077aed3SPierre Pronchery for (slash = 0, i = 0; i < wlen; i++)
74*b077aed3SPierre Pronchery if (wstr[i] == L'/' || wstr[i] == L'\\')
75*b077aed3SPierre Pronchery slash = i + 1;
76*b077aed3SPierre Pronchery /*
77*b077aed3SPierre Pronchery * ... then look for asterisk or question mark in the file name.
78*b077aed3SPierre Pronchery */
79*b077aed3SPierre Pronchery for (i = slash; i < wlen; i++)
80*b077aed3SPierre Pronchery if (wstr[i] == L'*' || wstr[i] == L'?')
81*b077aed3SPierre Pronchery break;
82*b077aed3SPierre Pronchery
83*b077aed3SPierre Pronchery if (i == wlen)
84*b077aed3SPierre Pronchery return 0; /* definitely not a glob */
85*b077aed3SPierre Pronchery
86*b077aed3SPierre Pronchery saved_char = wstr[wlen];
87*b077aed3SPierre Pronchery wstr[wlen] = L'\0';
88*b077aed3SPierre Pronchery h = FindFirstFileW(wstr, &data);
89*b077aed3SPierre Pronchery wstr[wlen] = saved_char;
90*b077aed3SPierre Pronchery if (h == INVALID_HANDLE_VALUE)
91*b077aed3SPierre Pronchery return 0; /* not a valid glob, just pass... */
92*b077aed3SPierre Pronchery
93*b077aed3SPierre Pronchery if (slash)
94*b077aed3SPierre Pronchery udlen = WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
95*b077aed3SPierre Pronchery NULL, 0, NULL, NULL);
96*b077aed3SPierre Pronchery else
97*b077aed3SPierre Pronchery udlen = 0;
98*b077aed3SPierre Pronchery
99*b077aed3SPierre Pronchery do {
100*b077aed3SPierre Pronchery int uflen;
101*b077aed3SPierre Pronchery char *arg;
102*b077aed3SPierre Pronchery
103*b077aed3SPierre Pronchery /*
104*b077aed3SPierre Pronchery * skip over . and ..
105*b077aed3SPierre Pronchery */
106*b077aed3SPierre Pronchery if (data.cFileName[0] == L'.') {
107*b077aed3SPierre Pronchery if ((data.cFileName[1] == L'\0') ||
108*b077aed3SPierre Pronchery (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0'))
109*b077aed3SPierre Pronchery continue;
110*b077aed3SPierre Pronchery }
111*b077aed3SPierre Pronchery
112*b077aed3SPierre Pronchery if (!validate_argv(newargc + 1))
113*b077aed3SPierre Pronchery break;
114*b077aed3SPierre Pronchery
115*b077aed3SPierre Pronchery /*
116*b077aed3SPierre Pronchery * -1 below means "scan for trailing '\0' *and* count it",
117*b077aed3SPierre Pronchery * so that |uflen| covers even trailing '\0'.
118*b077aed3SPierre Pronchery */
119*b077aed3SPierre Pronchery uflen = WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
120*b077aed3SPierre Pronchery NULL, 0, NULL, NULL);
121*b077aed3SPierre Pronchery
122*b077aed3SPierre Pronchery arg = malloc(udlen + uflen);
123*b077aed3SPierre Pronchery if (arg == NULL)
124*b077aed3SPierre Pronchery break;
125*b077aed3SPierre Pronchery
126*b077aed3SPierre Pronchery if (udlen)
127*b077aed3SPierre Pronchery WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
128*b077aed3SPierre Pronchery arg, udlen, NULL, NULL);
129*b077aed3SPierre Pronchery
130*b077aed3SPierre Pronchery WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
131*b077aed3SPierre Pronchery arg + udlen, uflen, NULL, NULL);
132*b077aed3SPierre Pronchery
133*b077aed3SPierre Pronchery newargv[newargc++] = arg;
134*b077aed3SPierre Pronchery } while (FindNextFileW(h, &data));
135*b077aed3SPierre Pronchery
136*b077aed3SPierre Pronchery CloseHandle(h);
137*b077aed3SPierre Pronchery
138*b077aed3SPierre Pronchery return 1;
139*b077aed3SPierre Pronchery }
140*b077aed3SPierre Pronchery
win32_utf8argv(int * argc,char ** argv[])141*b077aed3SPierre Pronchery void win32_utf8argv(int *argc, char **argv[])
142*b077aed3SPierre Pronchery {
143*b077aed3SPierre Pronchery const WCHAR *wcmdline;
144*b077aed3SPierre Pronchery WCHAR *warg, *wend, *p;
145*b077aed3SPierre Pronchery int wlen, ulen, valid = 1;
146*b077aed3SPierre Pronchery char *arg;
147*b077aed3SPierre Pronchery
148*b077aed3SPierre Pronchery if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) == 0)
149*b077aed3SPierre Pronchery return;
150*b077aed3SPierre Pronchery
151*b077aed3SPierre Pronchery newargc = 0;
152*b077aed3SPierre Pronchery newargv = NULL;
153*b077aed3SPierre Pronchery if (!validate_argv(newargc))
154*b077aed3SPierre Pronchery return;
155*b077aed3SPierre Pronchery
156*b077aed3SPierre Pronchery wcmdline = GetCommandLineW();
157*b077aed3SPierre Pronchery if (wcmdline == NULL) return;
158*b077aed3SPierre Pronchery
159*b077aed3SPierre Pronchery /*
160*b077aed3SPierre Pronchery * make a copy of the command line, since we might have to modify it...
161*b077aed3SPierre Pronchery */
162*b077aed3SPierre Pronchery wlen = wcslen(wcmdline);
163*b077aed3SPierre Pronchery p = _alloca((wlen + 1) * sizeof(WCHAR));
164*b077aed3SPierre Pronchery wcscpy(p, wcmdline);
165*b077aed3SPierre Pronchery
166*b077aed3SPierre Pronchery while (*p != L'\0') {
167*b077aed3SPierre Pronchery int in_quote = 0;
168*b077aed3SPierre Pronchery
169*b077aed3SPierre Pronchery if (*p == L' ' || *p == L'\t') {
170*b077aed3SPierre Pronchery p++; /* skip over whitespace */
171*b077aed3SPierre Pronchery continue;
172*b077aed3SPierre Pronchery }
173*b077aed3SPierre Pronchery
174*b077aed3SPierre Pronchery /*
175*b077aed3SPierre Pronchery * Note: because we may need to fiddle with the number of backslashes,
176*b077aed3SPierre Pronchery * the argument string is copied into itself. This is safe because
177*b077aed3SPierre Pronchery * the number of characters will never expand.
178*b077aed3SPierre Pronchery */
179*b077aed3SPierre Pronchery warg = wend = p;
180*b077aed3SPierre Pronchery while (*p != L'\0'
181*b077aed3SPierre Pronchery && (in_quote || (*p != L' ' && *p != L'\t'))) {
182*b077aed3SPierre Pronchery switch (*p) {
183*b077aed3SPierre Pronchery case L'\\':
184*b077aed3SPierre Pronchery /*
185*b077aed3SPierre Pronchery * Microsoft documentation on how backslashes are treated
186*b077aed3SPierre Pronchery * is:
187*b077aed3SPierre Pronchery *
188*b077aed3SPierre Pronchery * + Backslashes are interpreted literally, unless they
189*b077aed3SPierre Pronchery * immediately precede a double quotation mark.
190*b077aed3SPierre Pronchery * + If an even number of backslashes is followed by a double
191*b077aed3SPierre Pronchery * quotation mark, one backslash is placed in the argv array
192*b077aed3SPierre Pronchery * for every pair of backslashes, and the double quotation
193*b077aed3SPierre Pronchery * mark is interpreted as a string delimiter.
194*b077aed3SPierre Pronchery * + If an odd number of backslashes is followed by a double
195*b077aed3SPierre Pronchery * quotation mark, one backslash is placed in the argv array
196*b077aed3SPierre Pronchery * for every pair of backslashes, and the double quotation
197*b077aed3SPierre Pronchery * mark is "escaped" by the remaining backslash, causing a
198*b077aed3SPierre Pronchery * literal double quotation mark (") to be placed in argv.
199*b077aed3SPierre Pronchery *
200*b077aed3SPierre Pronchery * Ref: https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
201*b077aed3SPierre Pronchery *
202*b077aed3SPierre Pronchery * Though referred page doesn't mention it, multiple qouble
203*b077aed3SPierre Pronchery * quotes are also special. Pair of double quotes in quoted
204*b077aed3SPierre Pronchery * string is counted as single double quote.
205*b077aed3SPierre Pronchery */
206*b077aed3SPierre Pronchery {
207*b077aed3SPierre Pronchery const WCHAR *q = p;
208*b077aed3SPierre Pronchery int i;
209*b077aed3SPierre Pronchery
210*b077aed3SPierre Pronchery while (*p == L'\\')
211*b077aed3SPierre Pronchery p++;
212*b077aed3SPierre Pronchery
213*b077aed3SPierre Pronchery if (*p == L'"') {
214*b077aed3SPierre Pronchery int i;
215*b077aed3SPierre Pronchery
216*b077aed3SPierre Pronchery for (i = (p - q) / 2; i > 0; i--)
217*b077aed3SPierre Pronchery *wend++ = L'\\';
218*b077aed3SPierre Pronchery
219*b077aed3SPierre Pronchery /*
220*b077aed3SPierre Pronchery * if odd amount of backslashes before the quote,
221*b077aed3SPierre Pronchery * said quote is part of the argument, not a delimiter
222*b077aed3SPierre Pronchery */
223*b077aed3SPierre Pronchery if ((p - q) % 2 == 1)
224*b077aed3SPierre Pronchery *wend++ = *p++;
225*b077aed3SPierre Pronchery } else {
226*b077aed3SPierre Pronchery for (i = p - q; i > 0; i--)
227*b077aed3SPierre Pronchery *wend++ = L'\\';
228*b077aed3SPierre Pronchery }
229*b077aed3SPierre Pronchery }
230*b077aed3SPierre Pronchery break;
231*b077aed3SPierre Pronchery case L'"':
232*b077aed3SPierre Pronchery /*
233*b077aed3SPierre Pronchery * Without the preceding backslash (or when preceded with an
234*b077aed3SPierre Pronchery * even number of backslashes), the double quote is a simple
235*b077aed3SPierre Pronchery * string delimiter and just slightly change the parsing state
236*b077aed3SPierre Pronchery */
237*b077aed3SPierre Pronchery if (in_quote && p[1] == L'"')
238*b077aed3SPierre Pronchery *wend++ = *p++;
239*b077aed3SPierre Pronchery else
240*b077aed3SPierre Pronchery in_quote = !in_quote;
241*b077aed3SPierre Pronchery p++;
242*b077aed3SPierre Pronchery break;
243*b077aed3SPierre Pronchery default:
244*b077aed3SPierre Pronchery /*
245*b077aed3SPierre Pronchery * Any other non-delimiter character is just taken verbatim
246*b077aed3SPierre Pronchery */
247*b077aed3SPierre Pronchery *wend++ = *p++;
248*b077aed3SPierre Pronchery }
249*b077aed3SPierre Pronchery }
250*b077aed3SPierre Pronchery
251*b077aed3SPierre Pronchery wlen = wend - warg;
252*b077aed3SPierre Pronchery
253*b077aed3SPierre Pronchery if (wlen == 0 || !process_glob(warg, wlen)) {
254*b077aed3SPierre Pronchery if (!validate_argv(newargc + 1)) {
255*b077aed3SPierre Pronchery valid = 0;
256*b077aed3SPierre Pronchery break;
257*b077aed3SPierre Pronchery }
258*b077aed3SPierre Pronchery
259*b077aed3SPierre Pronchery ulen = 0;
260*b077aed3SPierre Pronchery if (wlen > 0) {
261*b077aed3SPierre Pronchery ulen = WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
262*b077aed3SPierre Pronchery NULL, 0, NULL, NULL);
263*b077aed3SPierre Pronchery if (ulen <= 0)
264*b077aed3SPierre Pronchery continue;
265*b077aed3SPierre Pronchery }
266*b077aed3SPierre Pronchery
267*b077aed3SPierre Pronchery arg = malloc(ulen + 1);
268*b077aed3SPierre Pronchery if (arg == NULL) {
269*b077aed3SPierre Pronchery valid = 0;
270*b077aed3SPierre Pronchery break;
271*b077aed3SPierre Pronchery }
272*b077aed3SPierre Pronchery
273*b077aed3SPierre Pronchery if (wlen > 0)
274*b077aed3SPierre Pronchery WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
275*b077aed3SPierre Pronchery arg, ulen, NULL, NULL);
276*b077aed3SPierre Pronchery arg[ulen] = '\0';
277*b077aed3SPierre Pronchery
278*b077aed3SPierre Pronchery newargv[newargc++] = arg;
279*b077aed3SPierre Pronchery }
280*b077aed3SPierre Pronchery }
281*b077aed3SPierre Pronchery
282*b077aed3SPierre Pronchery if (valid) {
283*b077aed3SPierre Pronchery saved_cp = GetConsoleOutputCP();
284*b077aed3SPierre Pronchery SetConsoleOutputCP(CP_UTF8);
285*b077aed3SPierre Pronchery
286*b077aed3SPierre Pronchery *argc = newargc;
287*b077aed3SPierre Pronchery *argv = newargv;
288*b077aed3SPierre Pronchery
289*b077aed3SPierre Pronchery atexit(cleanup);
290*b077aed3SPierre Pronchery } else if (newargv != NULL) {
291*b077aed3SPierre Pronchery int i;
292*b077aed3SPierre Pronchery
293*b077aed3SPierre Pronchery for (i = 0; i < newargc; i++)
294*b077aed3SPierre Pronchery free(newargv[i]);
295*b077aed3SPierre Pronchery
296*b077aed3SPierre Pronchery free(newargv);
297*b077aed3SPierre Pronchery
298*b077aed3SPierre Pronchery newargc = 0;
299*b077aed3SPierre Pronchery newargv = NULL;
300*b077aed3SPierre Pronchery }
301*b077aed3SPierre Pronchery
302*b077aed3SPierre Pronchery return;
303*b077aed3SPierre Pronchery }
304*b077aed3SPierre Pronchery #else
win32_utf8argv(int * argc,char ** argv[])305*b077aed3SPierre Pronchery void win32_utf8argv(int *argc, char **argv[])
306*b077aed3SPierre Pronchery { return; }
307*b077aed3SPierre Pronchery #endif
308