view src/winclip.c @ 4444:ccecb03e5e8b v7.3.970

updated for version 7.3.970 Problem: Syntax highlighting can be slow. Solution: Include the NFA regexp engine. Add the 'regexpengine' option to select which one is used. (various authors, including Ken Takata, Andrei Aiordachioaie, Russ Cox, Xiaozhou Liua, Ian Young)
author Bram Moolenaar <bram@vim.org>
date Sun, 19 May 2013 19:40:29 +0200
parents ff193256398a
children 7b20dc804164
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * winclip.c
 *
 * Routines common to both Win16 and Win32 for clipboard handling.
 * Also used by Cygwin, using os_unix.c.
 */

#ifdef WIN16
# ifdef __BORLANDC__
#  pragma warn -par
#  pragma warn -ucp
#  pragma warn -use
#  pragma warn -aus
# endif
#endif

#include "vimio.h"
#include "vim.h"

/*
 * Compile only the clipboard handling features when compiling for cygwin
 * posix environment.
 */
#ifdef FEAT_CYGWIN_WIN32_CLIPBOARD
# define WIN3264
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include "winclip.pro"
#endif

/*
 * When generating prototypes for Win32 on Unix, these lines make the syntax
 * errors disappear.  They do not need to be correct.
 */
#ifdef PROTO
#define WINAPI
#define WINBASEAPI
typedef int DWORD;
typedef int LPBOOL;
typedef int LPCSTR;
typedef int LPCWSTR;
typedef int LPSTR;
typedef int LPWSTR;
typedef int UINT;
#endif

#if defined(FEAT_MBYTE) || defined(PROTO)
/*
 * Convert an UTF-8 string to UTF-16.
 * "instr[inlen]" is the input.  "inlen" is in bytes.
 * When "outstr" is NULL only return the number of UTF-16 words produced.
 * Otherwise "outstr" must be a buffer of sufficient size.
 * Returns the number of UTF-16 words produced.
 */
    int
utf8_to_utf16(char_u *instr, int inlen, short_u *outstr, int *unconvlenp)
{
    int		outlen = 0;
    char_u	*p = instr;
    int		todo = inlen;
    int		l;
    int		ch;

    while (todo > 0)
    {
	/* Only convert if we have a complete sequence. */
	l = utf_ptr2len_len(p, todo);
	if (l > todo)
	{
	    /* Return length of incomplete sequence. */
	    if (unconvlenp != NULL)
		*unconvlenp = todo;
	    break;
	}

	ch = utf_ptr2char(p);
	if (ch >= 0x10000)
	{
	    /* non-BMP character, encoding with surrogate pairs */
	    ++outlen;
	    if (outstr != NULL)
	    {
		*outstr++ = (0xD800 - (0x10000 >> 10)) + (ch >> 10);
		*outstr++ = 0xDC00 | (ch & 0x3FF);
	    }
	}
	else if (outstr != NULL)
	    *outstr++ = ch;
	++outlen;
	p += l;
	todo -= l;
    }

    return outlen;
}

/*
 * Convert an UTF-16 string to UTF-8.
 * The input is "instr[inlen]" with "inlen" in number of UTF-16 words.
 * When "outstr" is NULL only return the required number of bytes.
 * Otherwise "outstr" must be a buffer of sufficient size.
 * Return the number of bytes produced.
 */
    int
utf16_to_utf8(short_u *instr, int inlen, char_u *outstr)
{
    int		outlen = 0;
    int		todo = inlen;
    short_u	*p = instr;
    int		l;
    int		ch, ch2;

    while (todo > 0)
    {
	ch = *p;
	if (ch >= 0xD800 && ch <= 0xDBFF && todo > 1)
	{
	    /* surrogate pairs handling */
	    ch2 = p[1];
	    if (ch2 >= 0xDC00 && ch2 <= 0xDFFF)
	    {
		ch = ((ch - 0xD800) << 10) + (ch2 & 0x3FF) + 0x10000;
		++p;
		--todo;
	    }
	}
	if (outstr != NULL)
	{
	    l = utf_char2bytes(ch, outstr);
	    outstr += l;
	}
	else
	    l = utf_char2len(ch);
	++p;
	outlen += l;
	--todo;
    }

    return outlen;
}

/*
 * Call MultiByteToWideChar() and allocate memory for the result.
 * Returns the result in "*out[*outlen]" with an extra zero appended.
 * "outlen" is in words.
 */
    void
MultiByteToWideChar_alloc(UINT cp, DWORD flags,
	LPCSTR in, int inlen,
	LPWSTR *out, int *outlen)
{
    *outlen = MultiByteToWideChar(cp, flags, in, inlen, 0, 0);
    /* Add one one word to avoid a zero-length alloc(). */
    *out = (LPWSTR)alloc(sizeof(WCHAR) * (*outlen + 1));
    if (*out != NULL)
    {
	MultiByteToWideChar(cp, flags, in, inlen, *out, *outlen);
	(*out)[*outlen] = 0;
    }
}

/*
 * Call WideCharToMultiByte() and allocate memory for the result.
 * Returns the result in "*out[*outlen]" with an extra NUL appended.
 */
    void
WideCharToMultiByte_alloc(UINT cp, DWORD flags,
	LPCWSTR in, int inlen,
	LPSTR *out, int *outlen,
	LPCSTR def, LPBOOL useddef)
{
    *outlen = WideCharToMultiByte(cp, flags, in, inlen, NULL, 0, def, useddef);
    /* Add one one byte to avoid a zero-length alloc(). */
    *out = alloc((unsigned)*outlen + 1);
    if (*out != NULL)
    {
	WideCharToMultiByte(cp, flags, in, inlen, *out, *outlen, def, useddef);
	(*out)[*outlen] = 0;
    }
}

#endif /* FEAT_MBYTE */

#ifdef FEAT_CLIPBOARD
/*
 * Clipboard stuff, for cutting and pasting text to other windows.
 */

    void
win_clip_init(void)
{
    clip_init(TRUE);

    /*
     * Vim's own clipboard format recognises whether the text is char, line,
     * or rectangular block.  Only useful for copying between two Vims.
     * "VimClipboard" was used for previous versions, using the first
     * character to specify MCHAR, MLINE or MBLOCK.
     */
    clip_star.format = RegisterClipboardFormat("VimClipboard2");
    clip_star.format_raw = RegisterClipboardFormat("VimRawBytes");
}

/* Type used for the clipboard type of Vim's data. */
typedef struct
{
    int type;		/* MCHAR, MBLOCK or MLINE */
    int txtlen;		/* length of CF_TEXT in bytes */
    int ucslen;		/* length of CF_UNICODETEXT in words */
    int rawlen;		/* length of clip_star.format_raw, including encoding,
			   excluding terminating NUL */
} VimClipType_t;

/*
 * Make vim the owner of the current selection.  Return OK upon success.
 */
/*ARGSUSED*/
    int
clip_mch_own_selection(VimClipboard *cbd)
{
    /*
     * Never actually own the clipboard.  If another application sets the
     * clipboard, we don't want to think that we still own it.
     */
    return FAIL;
}

/*
 * Make vim NOT the owner of the current selection.
 */
/*ARGSUSED*/
    void
clip_mch_lose_selection(VimClipboard *cbd)
{
    /* Nothing needs to be done here */
}

/*
 * Copy "str[*size]" into allocated memory, changing CR-NL to NL.
 * Return the allocated result and the size in "*size".
 * Returns NULL when out of memory.
 */
    static char_u *
crnl_to_nl(const char_u *str, int *size)
{
    int		pos = 0;
    int		str_len = *size;
    char_u	*ret;
    char_u	*retp;

    /* Avoid allocating zero bytes, it generates an error message. */
    ret = lalloc((long_u)(str_len == 0 ? 1 : str_len), TRUE);
    if (ret != NULL)
    {
	retp = ret;
	for (pos = 0; pos < str_len; ++pos)
	{
	    if (str[pos] == '\r' && str[pos + 1] == '\n')
	    {
		++pos;
		--(*size);
	    }
	    *retp++ = str[pos];
	}
    }

    return ret;
}

/*
 * Wait for another process to Close the Clipboard.
 * Returns TRUE for success.
 */
    static int
vim_open_clipboard(void)
{
    int delay = 10;

    while (!OpenClipboard(NULL))
    {
	if (delay > 500)
	    return FALSE;  /* waited too long, give up */
	Sleep(delay);
	delay *= 2;	/* wait for 10, 20, 40, 80, etc. msec */
    }
    return TRUE;
}

/*
 * Get the current selection and put it in the clipboard register.
 *
 * NOTE: Must use GlobalLock/Unlock here to ensure Win32s compatibility.
 * On NT/W95 the clipboard data is a fixed global memory object and
 * so its handle = its pointer.
 * On Win32s, however, co-operation with the Win16 system means that
 * the clipboard data is moveable and its handle is not a pointer at all,
 * so we can't just cast the return value of GetClipboardData to (char_u*).
 * <VN>
 */
    void
clip_mch_request_selection(VimClipboard *cbd)
{
    VimClipType_t	metadata = { -1, -1, -1, -1 };
    HGLOBAL		hMem = NULL;
    char_u		*str = NULL;
#if defined(FEAT_MBYTE) && defined(WIN3264)
    char_u		*to_free = NULL;
#endif
#ifdef FEAT_MBYTE
    HGLOBAL		rawh = NULL;
#endif
    int			str_size = 0;
    int			maxlen;
    size_t		n;

    /*
     * Don't pass GetActiveWindow() as an argument to OpenClipboard() because
     * then we can't paste back into the same window for some reason - webb.
     */
    if (!vim_open_clipboard())
	return;

    /* Check for vim's own clipboard format first.  This only gets the type of
     * the data, still need to use CF_UNICODETEXT or CF_TEXT for the text. */
    if (IsClipboardFormatAvailable(cbd->format))
    {
	VimClipType_t	*meta_p;
	HGLOBAL		meta_h;

	/* We have metadata on the clipboard; try to get it. */
	if ((meta_h = GetClipboardData(cbd->format)) != NULL
		&& (meta_p = (VimClipType_t *)GlobalLock(meta_h)) != NULL)
	{
	    /* The size of "VimClipType_t" changed, "rawlen" was added later.
	     * Only copy what is available for backwards compatibility. */
	    n = sizeof(VimClipType_t);
	    if (GlobalSize(meta_h) < n)
		n = GlobalSize(meta_h);
	    memcpy(&metadata, meta_p, n);
	    GlobalUnlock(meta_h);
	}
    }

#ifdef FEAT_MBYTE
    /* Check for Vim's raw clipboard format first.  This is used without
     * conversion, but only if 'encoding' matches. */
    if (IsClipboardFormatAvailable(cbd->format_raw)
				      && metadata.rawlen > (int)STRLEN(p_enc))
    {
	/* We have raw data on the clipboard; try to get it. */
	if ((rawh = GetClipboardData(cbd->format_raw)) != NULL)
	{
	    char_u	*rawp;

	    rawp = (char_u *)GlobalLock(rawh);
	    if (rawp != NULL && STRCMP(p_enc, rawp) == 0)
	    {
		n = STRLEN(p_enc) + 1;
		str = rawp + n;
		str_size = (int)(metadata.rawlen - n);
	    }
	    else
	    {
		GlobalUnlock(rawh);
		rawh = NULL;
	    }
	}
    }
    if (str == NULL)
    {
#endif

#if defined(FEAT_MBYTE) && defined(WIN3264)
    /* Try to get the clipboard in Unicode if it's not an empty string. */
    if (IsClipboardFormatAvailable(CF_UNICODETEXT) && metadata.ucslen != 0)
    {
	HGLOBAL hMemW;

	if ((hMemW = GetClipboardData(CF_UNICODETEXT)) != NULL)
	{
	    WCHAR *hMemWstr = (WCHAR *)GlobalLock(hMemW);

	    /* Use the length of our metadata if possible, but limit it to the
	     * GlobalSize() for safety. */
	    maxlen = (int)(GlobalSize(hMemW) / sizeof(WCHAR));
	    if (metadata.ucslen >= 0)
	    {
		if (metadata.ucslen > maxlen)
		    str_size = maxlen;
		else
		    str_size = metadata.ucslen;
	    }
	    else
	    {
		for (str_size = 0; str_size < maxlen; ++str_size)
		    if (hMemWstr[str_size] == NUL)
			break;
	    }
	    to_free = str = utf16_to_enc((short_u *)hMemWstr, &str_size);
	    GlobalUnlock(hMemW);
	}
    }
    else
#endif
    /* Get the clipboard in the Active codepage. */
    if (IsClipboardFormatAvailable(CF_TEXT))
    {
	if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
	{
	    str = (char_u *)GlobalLock(hMem);

	    /* The length is either what our metadata says or the strlen().
	     * But limit it to the GlobalSize() for safety. */
	    maxlen = (int)GlobalSize(hMem);
	    if (metadata.txtlen >= 0)
	    {
		if (metadata.txtlen > maxlen)
		    str_size = maxlen;
		else
		    str_size = metadata.txtlen;
	    }
	    else
	    {
		for (str_size = 0; str_size < maxlen; ++str_size)
		    if (str[str_size] == NUL)
			break;
	    }

# if defined(FEAT_MBYTE) && defined(WIN3264)
	    /* The text is in the active codepage.  Convert to 'encoding',
	     * going through UTF-16. */
	    acp_to_enc(str, str_size, &to_free, &maxlen);
	    if (to_free != NULL)
	    {
		str_size = maxlen;
		str = to_free;
	    }
# endif
	}
    }
#ifdef FEAT_MBYTE
    }
#endif

    if (str != NULL && *str != NUL)
    {
	char_u *temp_clipboard;

	/* If the type is not known detect it. */
	if (metadata.type == -1)
	    metadata.type = MAUTO;

	/* Translate <CR><NL> into <NL>. */
	temp_clipboard = crnl_to_nl(str, &str_size);
	if (temp_clipboard != NULL)
	{
	    clip_yank_selection(metadata.type, temp_clipboard, str_size, cbd);
	    vim_free(temp_clipboard);
	}
    }

    /* unlock the global object */
    if (hMem != NULL)
	GlobalUnlock(hMem);
#ifdef FEAT_MBYTE
    if (rawh != NULL)
	GlobalUnlock(rawh);
#endif
    CloseClipboard();
#if defined(FEAT_MBYTE) && defined(WIN3264)
    vim_free(to_free);
#endif
}

/*
 * Send the current selection to the clipboard.
 */
    void
clip_mch_set_selection(VimClipboard *cbd)
{
    char_u		*str = NULL;
    VimClipType_t	metadata;
    long_u		txtlen;
    HGLOBAL		hMemRaw = NULL;
    HGLOBAL		hMem = NULL;
    HGLOBAL		hMemVim = NULL;
# if defined(FEAT_MBYTE) && defined(WIN3264)
    HGLOBAL		hMemW = NULL;
# endif

    /* If the '*' register isn't already filled in, fill it in now */
    cbd->owned = TRUE;
    clip_get_selection(cbd);
    cbd->owned = FALSE;

    /* Get the text to be put on the clipboard, with CR-LF. */
    metadata.type = clip_convert_selection(&str, &txtlen, cbd);
    if (metadata.type < 0)
	return;
    metadata.txtlen = (int)txtlen;
    metadata.ucslen = 0;
    metadata.rawlen = 0;

#ifdef FEAT_MBYTE
    /* Always set the raw bytes: 'encoding', NUL and the text.  This is used
     * when copy/paste from/to Vim with the same 'encoding', so that illegal
     * bytes can also be copied and no conversion is needed. */
    {
	LPSTR lpszMemRaw;

	metadata.rawlen = (int)(txtlen + STRLEN(p_enc) + 1);
	hMemRaw = (LPSTR)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
							 metadata.rawlen + 1);
	lpszMemRaw = (LPSTR)GlobalLock(hMemRaw);
	if (lpszMemRaw != NULL)
	{
	    STRCPY(lpszMemRaw, p_enc);
	    memcpy(lpszMemRaw + STRLEN(p_enc) + 1, str, txtlen + 1);
	    GlobalUnlock(hMemRaw);
	}
	else
	    metadata.rawlen = 0;
    }
#endif

# if defined(FEAT_MBYTE) && defined(WIN3264)
    {
	WCHAR		*out;
	int		len = metadata.txtlen;

	/* Convert the text to UTF-16. This is put on the clipboard as
	 * CF_UNICODETEXT. */
	out = (WCHAR *)enc_to_utf16(str, &len);
	if (out != NULL)
	{
	    WCHAR *lpszMemW;

	    /* Convert the text for CF_TEXT to Active codepage. Otherwise it's
	     * p_enc, which has no relation to the Active codepage. */
	    metadata.txtlen = WideCharToMultiByte(GetACP(), 0, out, len,
							       NULL, 0, 0, 0);
	    vim_free(str);
	    str = (char_u *)alloc((unsigned)(metadata.txtlen == 0 ? 1
							  : metadata.txtlen));
	    if (str == NULL)
	    {
		vim_free(out);
		return;		/* out of memory */
	    }
	    WideCharToMultiByte(GetACP(), 0, out, len,
						  str, metadata.txtlen, 0, 0);

	    /* Allocate memory for the UTF-16 text, add one NUL word to
	     * terminate the string. */
	    hMemW = (LPSTR)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
						   (len + 1) * sizeof(WCHAR));
	    lpszMemW = (WCHAR *)GlobalLock(hMemW);
	    if (lpszMemW != NULL)
	    {
		memcpy(lpszMemW, out, len * sizeof(WCHAR));
		lpszMemW[len] = NUL;
		GlobalUnlock(hMemW);
	    }
	    vim_free(out);
	    metadata.ucslen = len;
	}
    }
# endif

    /* Allocate memory for the text, add one NUL byte to terminate the string.
     */
    hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, metadata.txtlen + 1);
    {
	LPSTR lpszMem = (LPSTR)GlobalLock(hMem);

	if (lpszMem)
	{
	    vim_strncpy(lpszMem, str, metadata.txtlen);
	    GlobalUnlock(hMem);
	}
    }

    /* Set up metadata: */
    {
	VimClipType_t *lpszMemVim = NULL;

	hMemVim = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,
						       sizeof(VimClipType_t));
	lpszMemVim = (VimClipType_t *)GlobalLock(hMemVim);
	memcpy(lpszMemVim, &metadata, sizeof(metadata));
	GlobalUnlock(hMemVim);
    }

    /*
     * Open the clipboard, clear it and put our text on it.
     * Always set our Vim format.  Put Unicode and plain text on it.
     *
     * Don't pass GetActiveWindow() as an argument to OpenClipboard()
     * because then we can't paste back into the same window for some
     * reason - webb.
     */
    if (vim_open_clipboard())
    {
	if (EmptyClipboard())
	{
	    SetClipboardData(cbd->format, hMemVim);
	    hMemVim = 0;
# if defined(FEAT_MBYTE) && defined(WIN3264)
	    if (hMemW != NULL)
	    {
		if (SetClipboardData(CF_UNICODETEXT, hMemW) != NULL)
		    hMemW = NULL;
	    }
# endif
	    /* Always use CF_TEXT.  On Win98 Notepad won't obtain the
	     * CF_UNICODETEXT text, only CF_TEXT. */
	    SetClipboardData(CF_TEXT, hMem);
	    hMem = 0;
	}
	CloseClipboard();
    }

    vim_free(str);
    /* Free any allocations we didn't give to the clipboard: */
    if (hMemRaw)
	GlobalFree(hMemRaw);
    if (hMem)
	GlobalFree(hMem);
# if defined(FEAT_MBYTE) && defined(WIN3264)
    if (hMemW)
	GlobalFree(hMemW);
# endif
    if (hMemVim)
	GlobalFree(hMemVim);
}

#endif /* FEAT_CLIPBOARD */

#if defined(FEAT_MBYTE) || defined(PROTO)
/*
 * Note: the following two functions are only guaranteed to work when using
 * valid MS-Windows codepages or when iconv() is available.
 */

/*
 * Convert "str" from 'encoding' to UTF-16.
 * Input in "str" with length "*lenp".  When "lenp" is NULL, use strlen().
 * Output is returned as an allocated string.  "*lenp" is set to the length of
 * the result.  A trailing NUL is always added.
 * Returns NULL when out of memory.
 */
    short_u *
enc_to_utf16(char_u *str, int *lenp)
{
    vimconv_T	conv;
    WCHAR	*ret;
    char_u	*allocbuf = NULL;
    int		len_loc;
    int		length;

    if (lenp == NULL)
    {
	len_loc = (int)STRLEN(str) + 1;
	lenp = &len_loc;
    }

    if (enc_codepage > 0)
    {
	/* We can do any CP### -> UTF-16 in one pass, and we can do it
	 * without iconv() (convert_* may need iconv). */
	MultiByteToWideChar_alloc(enc_codepage, 0, str, *lenp, &ret, &length);
    }
    else
    {
	/* Use "latin1" by default, we might be called before we have p_enc
	 * set up.  Convert to utf-8 first, works better with iconv().  Does
	 * nothing if 'encoding' is "utf-8". */
	conv.vc_type = CONV_NONE;
	if (convert_setup(&conv, p_enc ? p_enc : (char_u *)"latin1",
						   (char_u *)"utf-8") == FAIL)
	    return NULL;
	if (conv.vc_type != CONV_NONE)
	{
	    str = allocbuf = string_convert(&conv, str, lenp);
	    if (str == NULL)
		return NULL;
	}
	convert_setup(&conv, NULL, NULL);

	length = utf8_to_utf16(str, *lenp, NULL, NULL);
	ret = (WCHAR *)alloc((unsigned)((length + 1) * sizeof(WCHAR)));
	if (ret != NULL)
	{
	    utf8_to_utf16(str, *lenp, (short_u *)ret, NULL);
	    ret[length] = 0;
	}

	vim_free(allocbuf);
    }

    *lenp = length;
    return (short_u *)ret;
}

/*
 * Convert an UTF-16 string to 'encoding'.
 * Input in "str" with length (counted in wide characters) "*lenp".  When
 * "lenp" is NULL, use wcslen().
 * Output is returned as an allocated string.  If "*lenp" is not NULL it is
 * set to the length of the result.
 * Returns NULL when out of memory.
 */
    char_u *
utf16_to_enc(short_u *str, int *lenp)
{
    vimconv_T	conv;
    char_u	*utf8_str = NULL, *enc_str = NULL;
    int		len_loc;

    if (lenp == NULL)
    {
	len_loc = (int)wcslen(str) + 1;
	lenp = &len_loc;
    }

    if (enc_codepage > 0)
    {
	/* We can do any UTF-16 -> CP### in one pass. */
	int length;

	WideCharToMultiByte_alloc(enc_codepage, 0, str, *lenp,
					    (LPSTR *)&enc_str, &length, 0, 0);
	*lenp = length;
	return enc_str;
    }

    /* Avoid allocating zero bytes, it generates an error message. */
    utf8_str = alloc(utf16_to_utf8(str, *lenp == 0 ? 1 : *lenp, NULL));
    if (utf8_str != NULL)
    {
	*lenp = utf16_to_utf8(str, *lenp, utf8_str);

	/* We might be called before we have p_enc set up. */
	conv.vc_type = CONV_NONE;
	convert_setup(&conv, (char_u *)"utf-8",
					    p_enc? p_enc: (char_u *)"latin1");
	if (conv.vc_type == CONV_NONE)
	{
	    /* p_enc is utf-8, so we're done. */
	    enc_str = utf8_str;
	}
	else
	{
	    enc_str = string_convert(&conv, utf8_str, lenp);
	    vim_free(utf8_str);
	}

	convert_setup(&conv, NULL, NULL);
    }

    return enc_str;
}
#endif /* FEAT_MBYTE */

#if (defined(FEAT_MBYTE) && defined(WIN3264)) || defined(PROTO)
/*
 * Convert from the active codepage to 'encoding'.
 * Input is "str[str_size]".
 * The result is in allocated memory: "out[outlen]".  With terminating NUL.
 */
    void
acp_to_enc(str, str_size, out, outlen)
    char_u	*str;
    int		str_size;
    char_u	**out;
    int		*outlen;

{
    LPWSTR	widestr;

    MultiByteToWideChar_alloc(GetACP(), 0, str, str_size, &widestr, outlen);
    if (widestr != NULL)
    {
	++*outlen;	/* Include the 0 after the string */
	*out = utf16_to_enc((short_u *)widestr, outlen);
	vim_free(widestr);
    }
}
#endif