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