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