Blame apps/win32_init.c

Packit c4476c
/*
Packit c4476c
 * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
Packit c4476c
 *
Packit c4476c
 * Licensed under the OpenSSL license (the "License").  You may not use
Packit c4476c
 * this file except in compliance with the License.  You can obtain a copy
Packit c4476c
 * in the file LICENSE in the source distribution or at
Packit c4476c
 * https://www.openssl.org/source/license.html
Packit c4476c
 */
Packit c4476c
Packit c4476c
#include <windows.h>
Packit c4476c
#include <stdlib.h>
Packit c4476c
#include <string.h>
Packit c4476c
#include <malloc.h>
Packit c4476c
Packit c4476c
#if defined(CP_UTF8)
Packit c4476c
Packit c4476c
static UINT saved_cp;
Packit c4476c
static int newargc;
Packit c4476c
static char **newargv;
Packit c4476c
Packit c4476c
static void cleanup(void)
Packit c4476c
{
Packit c4476c
    int i;
Packit c4476c
Packit c4476c
    SetConsoleOutputCP(saved_cp);
Packit c4476c
Packit c4476c
    for (i = 0; i < newargc; i++)
Packit c4476c
        free(newargv[i]);
Packit c4476c
Packit c4476c
    free(newargv);
Packit c4476c
}
Packit c4476c
Packit c4476c
/*
Packit c4476c
 * Incrementally [re]allocate newargv and keep it NULL-terminated.
Packit c4476c
 */
Packit c4476c
static int validate_argv(int argc)
Packit c4476c
{
Packit c4476c
    static int size = 0;
Packit c4476c
Packit c4476c
    if (argc >= size) {
Packit c4476c
        char **ptr;
Packit c4476c
Packit c4476c
        while (argc >= size)
Packit c4476c
            size += 64;
Packit c4476c
Packit c4476c
        ptr = realloc(newargv, size * sizeof(newargv[0]));
Packit c4476c
        if (ptr == NULL)
Packit c4476c
            return 0;
Packit c4476c
Packit c4476c
        (newargv = ptr)[argc] = NULL;
Packit c4476c
    } else {
Packit c4476c
        newargv[argc] = NULL;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    return 1;
Packit c4476c
}
Packit c4476c
Packit c4476c
static int process_glob(WCHAR *wstr, int wlen)
Packit c4476c
{
Packit c4476c
    int i, slash, udlen;
Packit c4476c
    WCHAR saved_char;
Packit c4476c
    WIN32_FIND_DATAW data;
Packit c4476c
    HANDLE h;
Packit c4476c
Packit c4476c
    /*
Packit c4476c
     * Note that we support wildcard characters only in filename part
Packit c4476c
     * of the path, and not in directories. Windows users are used to
Packit c4476c
     * this, that's why recursive glob processing is not implemented.
Packit c4476c
     */
Packit c4476c
    /*
Packit c4476c
     * Start by looking for last slash or backslash, ...
Packit c4476c
     */
Packit c4476c
    for (slash = 0, i = 0; i < wlen; i++)
Packit c4476c
        if (wstr[i] == L'/' || wstr[i] == L'\\')
Packit c4476c
            slash = i + 1;
Packit c4476c
    /*
Packit c4476c
     * ... then look for asterisk or question mark in the file name.
Packit c4476c
     */
Packit c4476c
    for (i = slash; i < wlen; i++)
Packit c4476c
        if (wstr[i] == L'*' || wstr[i] == L'?')
Packit c4476c
            break;
Packit c4476c
Packit c4476c
    if (i == wlen)
Packit c4476c
        return 0;   /* definitely not a glob */
Packit c4476c
Packit c4476c
    saved_char = wstr[wlen];
Packit c4476c
    wstr[wlen] = L'\0';
Packit c4476c
    h = FindFirstFileW(wstr, &data);
Packit c4476c
    wstr[wlen] = saved_char;
Packit c4476c
    if (h == INVALID_HANDLE_VALUE)
Packit c4476c
        return 0;   /* not a valid glob, just pass... */
Packit c4476c
Packit c4476c
    if (slash)
Packit c4476c
        udlen = WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
Packit c4476c
                                    NULL, 0, NULL, NULL);
Packit c4476c
    else
Packit c4476c
        udlen = 0;
Packit c4476c
Packit c4476c
    do {
Packit c4476c
        int uflen;
Packit c4476c
        char *arg;
Packit c4476c
Packit c4476c
        /*
Packit c4476c
         * skip over . and ..
Packit c4476c
         */
Packit c4476c
        if (data.cFileName[0] == L'.') {
Packit c4476c
            if ((data.cFileName[1] == L'\0') ||
Packit c4476c
                (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0'))
Packit c4476c
                continue;
Packit c4476c
        }
Packit c4476c
Packit c4476c
        if (!validate_argv(newargc + 1))
Packit c4476c
            break;
Packit c4476c
Packit c4476c
        /*
Packit c4476c
         * -1 below means "scan for trailing '\0' *and* count it",
Packit c4476c
         * so that |uflen| covers even trailing '\0'.
Packit c4476c
         */
Packit c4476c
        uflen = WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
Packit c4476c
                                    NULL, 0, NULL, NULL);
Packit c4476c
Packit c4476c
        arg = malloc(udlen + uflen);
Packit c4476c
        if (arg == NULL)
Packit c4476c
            break;
Packit c4476c
Packit c4476c
        if (udlen)
Packit c4476c
            WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
Packit c4476c
                                arg, udlen, NULL, NULL);
Packit c4476c
Packit c4476c
        WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
Packit c4476c
                            arg + udlen, uflen, NULL, NULL);
Packit c4476c
Packit c4476c
        newargv[newargc++] = arg;
Packit c4476c
    } while (FindNextFileW(h, &data));
Packit c4476c
Packit c4476c
    CloseHandle(h);
Packit c4476c
Packit c4476c
    return 1;
Packit c4476c
}
Packit c4476c
Packit c4476c
void win32_utf8argv(int *argc, char **argv[])
Packit c4476c
{
Packit c4476c
    const WCHAR *wcmdline;
Packit c4476c
    WCHAR *warg, *wend, *p;
Packit c4476c
    int wlen, ulen, valid = 1;
Packit c4476c
    char *arg;
Packit c4476c
Packit c4476c
    if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) == 0)
Packit c4476c
        return;
Packit c4476c
Packit c4476c
    newargc = 0;
Packit c4476c
    newargv = NULL;
Packit c4476c
    if (!validate_argv(newargc))
Packit c4476c
        return;
Packit c4476c
Packit c4476c
    wcmdline = GetCommandLineW();
Packit c4476c
    if (wcmdline == NULL) return;
Packit c4476c
Packit c4476c
    /*
Packit c4476c
     * make a copy of the command line, since we might have to modify it...
Packit c4476c
     */
Packit c4476c
    wlen = wcslen(wcmdline);
Packit c4476c
    p = _alloca((wlen + 1) * sizeof(WCHAR));
Packit c4476c
    wcscpy(p, wcmdline);
Packit c4476c
Packit c4476c
    while (*p != L'\0') {
Packit c4476c
        int in_quote = 0;
Packit c4476c
Packit c4476c
        if (*p == L' ' || *p == L'\t') {
Packit c4476c
            p++; /* skip over white spaces */
Packit c4476c
            continue;
Packit c4476c
        }
Packit c4476c
Packit c4476c
        /*
Packit c4476c
         * Note: because we may need to fiddle with the number of backslashes,
Packit c4476c
         * the argument string is copied into itself.  This is safe because
Packit c4476c
         * the number of characters will never expand.
Packit c4476c
         */
Packit c4476c
        warg = wend = p;
Packit c4476c
        while (*p != L'\0'
Packit c4476c
               && (in_quote || (*p != L' ' && *p != L'\t'))) {
Packit c4476c
            switch (*p) {
Packit c4476c
            case L'\\':
Packit c4476c
                /*
Packit c4476c
                 * Microsoft documentation on how backslashes are treated
Packit c4476c
                 * is:
Packit c4476c
                 *
Packit c4476c
                 * + Backslashes are interpreted literally, unless they
Packit c4476c
                 *   immediately precede a double quotation mark.
Packit c4476c
                 * + If an even number of backslashes is followed by a double
Packit c4476c
                 *   quotation mark, one backslash is placed in the argv array
Packit c4476c
                 *   for every pair of backslashes, and the double quotation
Packit c4476c
                 *   mark is interpreted as a string delimiter.
Packit c4476c
                 * + If an odd number of backslashes is followed by a double
Packit c4476c
                 *   quotation mark, one backslash is placed in the argv array
Packit c4476c
                 *   for every pair of backslashes, and the double quotation
Packit c4476c
                 *   mark is "escaped" by the remaining backslash, causing a
Packit c4476c
                 *   literal double quotation mark (") to be placed in argv.
Packit c4476c
                 *
Packit c4476c
                 * Ref: https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
Packit c4476c
                 *
Packit c4476c
                 * Though referred page doesn't mention it, multiple qouble
Packit c4476c
                 * quotes are also special. Pair of double quotes in quoted
Packit c4476c
                 * string is counted as single double quote.
Packit c4476c
                 */
Packit c4476c
                {
Packit c4476c
                    const WCHAR *q = p;
Packit c4476c
                    int i;
Packit c4476c
Packit c4476c
                    while (*p == L'\\')
Packit c4476c
                        p++;
Packit c4476c
Packit c4476c
                    if (*p == L'"') {
Packit c4476c
                        int i;
Packit c4476c
Packit c4476c
                        for (i = (p - q) / 2; i > 0; i--)
Packit c4476c
                            *wend++ = L'\\';
Packit c4476c
Packit c4476c
                        /*
Packit c4476c
                         * if odd amount of backslashes before the quote,
Packit c4476c
                         * said quote is part of the argument, not a delimiter
Packit c4476c
                         */
Packit c4476c
                        if ((p - q) % 2 == 1)
Packit c4476c
                            *wend++ = *p++;
Packit c4476c
                    } else {
Packit c4476c
                        for (i = p - q; i > 0; i--)
Packit c4476c
                            *wend++ = L'\\';
Packit c4476c
                    }
Packit c4476c
                }
Packit c4476c
                break;
Packit c4476c
            case L'"':
Packit c4476c
                /*
Packit c4476c
                 * Without the preceding backslash (or when preceded with an
Packit c4476c
                 * even number of backslashes), the double quote is a simple
Packit c4476c
                 * string delimiter and just slightly change the parsing state
Packit c4476c
                 */
Packit c4476c
                if (in_quote && p[1] == L'"')
Packit c4476c
                    *wend++ = *p++;
Packit c4476c
                else
Packit c4476c
                    in_quote = !in_quote;
Packit c4476c
                p++;
Packit c4476c
                break;
Packit c4476c
            default:
Packit c4476c
                /*
Packit c4476c
                 * Any other non-delimiter character is just taken verbatim
Packit c4476c
                 */
Packit c4476c
                *wend++ = *p++;
Packit c4476c
            }
Packit c4476c
        }
Packit c4476c
Packit c4476c
        wlen = wend - warg;
Packit c4476c
Packit c4476c
        if (wlen == 0 || !process_glob(warg, wlen)) {
Packit c4476c
            if (!validate_argv(newargc + 1)) {
Packit c4476c
                valid = 0;
Packit c4476c
                break;
Packit c4476c
            }
Packit c4476c
Packit c4476c
            ulen = 0;
Packit c4476c
            if (wlen > 0) {
Packit c4476c
                ulen = WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
Packit c4476c
                                           NULL, 0, NULL, NULL);
Packit c4476c
                if (ulen <= 0)
Packit c4476c
                    continue;
Packit c4476c
            }
Packit c4476c
Packit c4476c
            arg = malloc(ulen + 1);
Packit c4476c
            if (arg == NULL) {
Packit c4476c
                valid = 0;
Packit c4476c
                break;
Packit c4476c
            }
Packit c4476c
Packit c4476c
            if (wlen > 0)
Packit c4476c
                WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
Packit c4476c
                                    arg, ulen, NULL, NULL);
Packit c4476c
            arg[ulen] = '\0';
Packit c4476c
Packit c4476c
            newargv[newargc++] = arg;
Packit c4476c
        }
Packit c4476c
    }
Packit c4476c
Packit c4476c
    if (valid) {
Packit c4476c
        saved_cp = GetConsoleOutputCP();
Packit c4476c
        SetConsoleOutputCP(CP_UTF8);
Packit c4476c
Packit c4476c
        *argc = newargc;
Packit c4476c
        *argv = newargv;
Packit c4476c
Packit c4476c
        atexit(cleanup);
Packit c4476c
    } else if (newargv != NULL) {
Packit c4476c
        int i;
Packit c4476c
Packit c4476c
        for (i = 0; i < newargc; i++)
Packit c4476c
            free(newargv[i]);
Packit c4476c
Packit c4476c
        free(newargv);
Packit c4476c
Packit c4476c
        newargc = 0;
Packit c4476c
        newargv = NULL;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    return;
Packit c4476c
}
Packit c4476c
#else
Packit c4476c
void win32_utf8argv(int *argc, char **argv[])
Packit c4476c
{   return;   }
Packit c4476c
#endif