xref: /freebsd/crypto/openssl/apps/lib/win32_init.c (revision b077aed3)
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