xref: /vim-8.2.3635/src/os_mswin.c (revision 843ee41e)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * os_mswin.c
12  *
13  * Routines common to both Win16 and Win32.
14  */
15 
16 #ifdef WIN16
17 # ifdef __BORLANDC__
18 #  pragma warn -par
19 #  pragma warn -ucp
20 #  pragma warn -use
21 #  pragma warn -aus
22 # endif
23 #endif
24 
25 #include <io.h>
26 #include "vim.h"
27 
28 #ifdef HAVE_FCNTL_H
29 # include <fcntl.h>
30 #endif
31 #ifdef WIN16
32 # define SHORT_FNAME		/* always 8.3 file name */
33 # include <dos.h>
34 # include <string.h>
35 #endif
36 #include <sys/types.h>
37 #include <errno.h>
38 #include <signal.h>
39 #include <limits.h>
40 #include <process.h>
41 
42 #undef chdir
43 #ifdef __GNUC__
44 # ifndef __MINGW32__
45 #  include <dirent.h>
46 # endif
47 #else
48 # include <direct.h>
49 #endif
50 
51 #if defined(FEAT_TITLE) && !defined(FEAT_GUI_W32)
52 # include <shellapi.h>
53 #endif
54 
55 #if defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)
56 # include <dlgs.h>
57 # ifdef WIN3264
58 #  include <winspool.h>
59 # else
60 #  include <print.h>
61 # endif
62 # include <commdlg.h>
63 #endif
64 
65 #ifdef __MINGW32__
66 # ifndef FROM_LEFT_1ST_BUTTON_PRESSED
67 #  define FROM_LEFT_1ST_BUTTON_PRESSED    0x0001
68 # endif
69 # ifndef RIGHTMOST_BUTTON_PRESSED
70 #  define RIGHTMOST_BUTTON_PRESSED	  0x0002
71 # endif
72 # ifndef FROM_LEFT_2ND_BUTTON_PRESSED
73 #  define FROM_LEFT_2ND_BUTTON_PRESSED    0x0004
74 # endif
75 # ifndef FROM_LEFT_3RD_BUTTON_PRESSED
76 #  define FROM_LEFT_3RD_BUTTON_PRESSED    0x0008
77 # endif
78 # ifndef FROM_LEFT_4TH_BUTTON_PRESSED
79 #  define FROM_LEFT_4TH_BUTTON_PRESSED    0x0010
80 # endif
81 
82 /*
83  * EventFlags
84  */
85 # ifndef MOUSE_MOVED
86 #  define MOUSE_MOVED   0x0001
87 # endif
88 # ifndef DOUBLE_CLICK
89 #  define DOUBLE_CLICK  0x0002
90 # endif
91 #endif
92 
93 /*
94  * When generating prototypes for Win32 on Unix, these lines make the syntax
95  * errors disappear.  They do not need to be correct.
96  */
97 #ifdef PROTO
98 #define WINAPI
99 #define WINBASEAPI
100 typedef int BOOL;
101 typedef int CALLBACK;
102 typedef int COLORREF;
103 typedef int CONSOLE_CURSOR_INFO;
104 typedef int COORD;
105 typedef int DWORD;
106 typedef int ENUMLOGFONT;
107 typedef int HANDLE;
108 typedef int HDC;
109 typedef int HFONT;
110 typedef int HICON;
111 typedef int HWND;
112 typedef int INPUT_RECORD;
113 typedef int KEY_EVENT_RECORD;
114 typedef int LOGFONT;
115 typedef int LPARAM;
116 typedef int LPBOOL;
117 typedef int LPCSTR;
118 typedef int LPCWSTR;
119 typedef int LPSTR;
120 typedef int LPTSTR;
121 typedef int LPWSTR;
122 typedef int LRESULT;
123 typedef int MOUSE_EVENT_RECORD;
124 typedef int NEWTEXTMETRIC;
125 typedef int PACL;
126 typedef int PRINTDLG;
127 typedef int PSECURITY_DESCRIPTOR;
128 typedef int PSID;
129 typedef int SECURITY_INFORMATION;
130 typedef int SHORT;
131 typedef int SMALL_RECT;
132 typedef int TEXTMETRIC;
133 typedef int UINT;
134 typedef int WCHAR;
135 typedef int WORD;
136 typedef int WPARAM;
137 typedef void VOID;
138 #endif
139 
140 /* Record all output and all keyboard & mouse input */
141 /* #define MCH_WRITE_DUMP */
142 
143 #ifdef MCH_WRITE_DUMP
144 FILE* fdDump = NULL;
145 #endif
146 
147 #ifdef WIN3264
148 extern DWORD g_PlatformId;
149 #endif
150 
151 #ifndef FEAT_GUI_MSWIN
152 extern char g_szOrigTitle[];
153 #endif
154 
155 #ifdef FEAT_GUI
156 extern HWND s_hwnd;
157 #else
158 static HWND s_hwnd = 0;	    /* console window handle, set by GetConsoleHwnd() */
159 #endif
160 
161 extern int WSInitialized;
162 
163 /* Don't generate prototypes here, because some systems do have these
164  * functions. */
165 #if defined(__GNUC__) && !defined(PROTO)
166 # ifndef __MINGW32__
167 int _stricoll(char *a, char *b)
168 {
169     // the ANSI-ish correct way is to use strxfrm():
170     char a_buff[512], b_buff[512];  // file names, so this is enough on Win32
171     strxfrm(a_buff, a, 512);
172     strxfrm(b_buff, b, 512);
173     return strcoll(a_buff, b_buff);
174 }
175 
176 char * _fullpath(char *buf, char *fname, int len)
177 {
178     LPTSTR toss;
179 
180     return (char *)GetFullPathName(fname, len, buf, &toss);
181 }
182 # endif
183 
184 int _chdrive(int drive)
185 {
186     char temp [3] = "-:";
187     temp[0] = drive + 'A' - 1;
188     return !SetCurrentDirectory(temp);
189 }
190 #else
191 # ifdef __BORLANDC__
192 /* being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:
193  * but it does in BC 5.02! */
194 #  if __BORLANDC__ < 0x502
195 int _stricoll(char *a, char *b)
196 {
197 #   if 1
198     // this is fast but not correct:
199     return stricmp(a, b);
200 #   else
201     // the ANSI-ish correct way is to use strxfrm():
202     char a_buff[512], b_buff[512];  // file names, so this is enough on Win32
203     strxfrm(a_buff, a, 512);
204     strxfrm(b_buff, b, 512);
205     return strcoll(a_buff, b_buff);
206 #   endif
207 }
208 #  endif
209 # endif
210 #endif
211 
212 
213 #if defined(FEAT_GUI_MSWIN) || defined(PROTO)
214 /*
215  * GUI version of mch_exit().
216  * Shut down and exit with status `r'
217  * Careful: mch_exit() may be called before mch_init()!
218  */
219     void
220 mch_exit(int r)
221 {
222     display_errors();
223 
224     ml_close_all(TRUE);		/* remove all memfiles */
225 
226 # ifdef FEAT_OLE
227     UninitOLE();
228 # endif
229 # ifdef FEAT_NETBEANS_INTG
230     if (WSInitialized)
231     {
232 	WSInitialized = FALSE;
233 	WSACleanup();
234     }
235 # endif
236 #ifdef DYNAMIC_GETTEXT
237     dyn_libintl_end();
238 #endif
239 
240     if (gui.in_use)
241 	gui_exit(r);
242     exit(r);
243 }
244 
245 #endif /* FEAT_GUI_MSWIN */
246 
247 
248 /*
249  * Init the tables for toupper() and tolower().
250  */
251     void
252 mch_early_init(void)
253 {
254     int		i;
255 
256 #ifdef WIN3264
257     PlatformId();
258 #endif
259 
260     /* Init the tables for toupper() and tolower() */
261     for (i = 0; i < 256; ++i)
262 	toupper_tab[i] = tolower_tab[i] = i;
263 #ifdef WIN3264
264     CharUpperBuff(toupper_tab, 256);
265     CharLowerBuff(tolower_tab, 256);
266 #else
267     AnsiUpperBuff(toupper_tab, 256);
268     AnsiLowerBuff(tolower_tab, 256);
269 #endif
270 }
271 
272 
273 /*
274  * Return TRUE if the input comes from a terminal, FALSE otherwise.
275  */
276     int
277 mch_input_isatty()
278 {
279 #ifdef FEAT_GUI_MSWIN
280     return OK;	    /* GUI always has a tty */
281 #else
282     if (isatty(read_cmd_fd))
283 	return TRUE;
284     return FALSE;
285 #endif
286 }
287 
288 #ifdef FEAT_TITLE
289 /*
290  * mch_settitle(): set titlebar of our window
291  */
292     void
293 mch_settitle(
294     char_u *title,
295     char_u *icon)
296 {
297 # ifdef FEAT_GUI_MSWIN
298     gui_mch_settitle(title, icon);
299 # else
300     if (title != NULL)
301 	SetConsoleTitle(title);
302 # endif
303 }
304 
305 
306 /*
307  * Restore the window/icon title.
308  * which is one of:
309  *  1: Just restore title
310  *  2: Just restore icon (which we don't have)
311  *  3: Restore title and icon (which we don't have)
312  */
313     void
314 mch_restore_title(
315     int which)
316 {
317 #ifndef FEAT_GUI_MSWIN
318     mch_settitle((which & 1) ? g_szOrigTitle : NULL, NULL);
319 #endif
320 }
321 
322 
323 /*
324  * Return TRUE if we can restore the title (we can)
325  */
326     int
327 mch_can_restore_title()
328 {
329     return TRUE;
330 }
331 
332 
333 /*
334  * Return TRUE if we can restore the icon title (we can't)
335  */
336     int
337 mch_can_restore_icon()
338 {
339     return FALSE;
340 }
341 #endif /* FEAT_TITLE */
342 
343 
344 /*
345  * Get absolute file name into buffer 'buf' of length 'len' bytes,
346  * turning all '/'s into '\\'s and getting the correct case of each
347  * component of the file name.  Append a backslash to a directory name.
348  * When 'shellslash' set do it the other way around.
349  * Return OK or FAIL.
350  */
351     int
352 mch_FullName(
353     char_u	*fname,
354     char_u	*buf,
355     int		len,
356     int		force)
357 {
358     int		nResult = FAIL;
359 
360 #ifdef __BORLANDC__
361     if (*fname == NUL) /* Borland behaves badly here - make it consistent */
362 	nResult = mch_dirname(buf, len);
363     else
364 #endif
365 	if (_fullpath(buf, fname, len - 1) == NULL)
366     {
367 	STRNCPY(buf, fname, len);   /* failed, use the relative path name */
368 	buf[len - 1] = NUL;
369 #ifndef USE_FNAME_CASE
370 	slash_adjust(buf);
371 #endif
372     }
373     else
374 	nResult = OK;
375 
376 #ifdef USE_FNAME_CASE
377     fname_case(buf, len);
378 #endif
379 
380     return nResult;
381 }
382 
383 
384 /*
385  * Return TRUE if "fname" does not depend on the current directory.
386  */
387     int
388 mch_isFullName(char_u *fname)
389 {
390     char szName[_MAX_PATH + 1];
391 
392     /* A name like "d:/foo" and "//server/share" is absolute */
393     if ((fname[0] && fname[1] == ':' && (fname[2] == '/' || fname[2] == '\\'))
394 	    || (fname[0] == fname[1] && (fname[0] == '/' || fname[0] == '\\')))
395 	return TRUE;
396 
397     /* A name that can't be made absolute probably isn't absolute. */
398     if (mch_FullName(fname, szName, _MAX_PATH, FALSE) == FAIL)
399 	return FALSE;
400 
401     return pathcmp(fname, szName) == 0;
402 }
403 
404 /*
405  * Replace all slashes by backslashes.
406  * This used to be the other way around, but MS-DOS sometimes has problems
407  * with slashes (e.g. in a command name).  We can't have mixed slashes and
408  * backslashes, because comparing file names will not work correctly.  The
409  * commands that use a file name should try to avoid the need to type a
410  * backslash twice.
411  * When 'shellslash' set do it the other way around.
412  */
413     void
414 slash_adjust(p)
415     char_u  *p;
416 {
417     if (p != NULL)
418 	while (*p)
419 	{
420 	    if (*p == psepcN)
421 		*p = psepc;
422 #ifdef FEAT_MBYTE
423 	    if (has_mbyte)
424 		p += (*mb_ptr2len_check)(p);
425 	    else
426 #endif
427 		++p;
428 	}
429 }
430 
431 
432 /*
433  * stat() can't handle a trailing '/' or '\', remove it first.
434  */
435     int
436 vim_stat(const char *name, struct stat *stp)
437 {
438     char	buf[_MAX_PATH + 1];
439     char	*p;
440 
441     STRNCPY(buf, name, _MAX_PATH);
442     buf[_MAX_PATH] = NUL;
443     p = buf + strlen(buf);
444     if (p > buf)
445 	--p;
446     if (p > buf && (*p == '\\' || *p == '/') && p[-1] != ':')
447 	*p = NUL;
448 #ifdef FEAT_MBYTE
449     if (enc_codepage >= 0 && (int)GetACP() != enc_codepage
450 # ifdef __BORLANDC__
451 	    /* Wide functions of Borland C 5.5 do not work on Windows 98. */
452 	    && g_PlatformId == VER_PLATFORM_WIN32_NT
453 # endif
454        )
455     {
456 	WCHAR	*wp = enc_to_ucs2(buf, NULL);
457 	int	n;
458 
459 	if (wp != NULL)
460 	{
461 	    n = _wstat(wp, (struct _stat *)stp);
462 	    vim_free(wp);
463 	    if (n >= 0)
464 		return n;
465 	    /* Retry with non-wide function (for Windows 98). Can't use
466 	     * GetLastError() here and it's unclear what errno gets set to if
467 	     * the _wstat() fails for missing wide functions. */
468 	}
469     }
470 #endif
471     return stat(buf, stp);
472 }
473 
474 #if defined(FEAT_GUI_MSWIN) || defined(PROTO)
475     void
476 mch_settmode(int tmode)
477 {
478     /* nothing to do */
479 }
480 
481     int
482 mch_get_shellsize(void)
483 {
484     /* never used */
485     return OK;
486 }
487 
488     void
489 mch_set_shellsize(void)
490 {
491     /* never used */
492 }
493 
494 /*
495  * Rows and/or Columns has changed.
496  */
497     void
498 mch_new_shellsize(void)
499 {
500     /* never used */
501 }
502 
503 #endif
504 
505 /*
506  * We have no job control, so fake it by starting a new shell.
507  */
508     void
509 mch_suspend()
510 {
511     suspend_shell();
512 }
513 
514 #if defined(USE_MCH_ERRMSG) || defined(PROTO)
515 
516 #ifdef display_errors
517 # undef display_errors
518 #endif
519 
520 /*
521  * Display the saved error message(s).
522  */
523     void
524 display_errors()
525 {
526     char *p;
527 
528     if (error_ga.ga_data != NULL)
529     {
530 	/* avoid putting up a message box with blanks only */
531 	for (p = (char *)error_ga.ga_data; *p; ++p)
532 	    if (!isspace(*p))
533 	    {
534 		/* Truncate a very long message, it will go off-screen. */
535 		if (STRLEN(p) > 2000)
536 		{
537 		    char_u	*s = p + 2000 - 14;
538 
539 #ifdef FEAT_MBYTE
540 		    if (has_mbyte)
541 			s -= (*mb_head_off)(p, s);
542 #endif
543 		    STRCPY(s, _("...(truncated)"));
544 		}
545 #ifdef WIN3264
546 		MessageBox(NULL, p, "Vim", MB_TASKMODAL|MB_SETFOREGROUND);
547 #else
548 		MessageBox(NULL, p, "Vim", MB_TASKMODAL);
549 #endif
550 		break;
551 	    }
552 	ga_clear(&error_ga);
553     }
554 }
555 #endif
556 
557 
558 /*
559  * Return TRUE if "p" contain a wildcard that can be expanded by
560  * dos_expandpath().
561  */
562     int
563 mch_has_exp_wildcard(char_u *p)
564 {
565     for ( ; *p; ++p)
566     {
567 	if (vim_strchr((char_u *)"?*[", *p) != NULL
568 		|| (*p == '~' && p[1] != NUL))
569 	    return TRUE;
570 #ifdef FEAT_MBYTE
571 	if (has_mbyte)
572 	    p += (*mb_ptr2len_check)(p) - 1;
573 #endif
574     }
575     return FALSE;
576 }
577 
578 /*
579  * Return TRUE if "p" contain a wildcard or a "~1" kind of thing (could be a
580  * shortened file name).
581  */
582     int
583 mch_has_wildcard(char_u *p)
584 {
585     for ( ; *p; ++p)
586     {
587 	if (vim_strchr((char_u *)
588 #  ifdef VIM_BACKTICK
589 				    "?*$[`"
590 #  else
591 				    "?*$["
592 #  endif
593 						, *p) != NULL
594 		|| (*p == '~' && p[1] != NUL))
595 	    return TRUE;
596 #ifdef FEAT_MBYTE
597 	if (has_mbyte)
598 	    p += (*mb_ptr2len_check)(p) - 1;
599 #endif
600     }
601     return FALSE;
602 }
603 
604 
605 /*
606  * The normal _chdir() does not change the default drive.  This one does.
607  * Returning 0 implies success; -1 implies failure.
608  */
609     int
610 mch_chdir(char *path)
611 {
612     if (path[0] == NUL)		/* just checking... */
613 	return -1;
614 
615     if (isalpha(path[0]) && path[1] == ':')	/* has a drive name */
616     {
617 	/* If we can change to the drive, skip that part of the path.  If we
618 	 * can't then the current directory may be invalid, try using chdir()
619 	 * with the whole path. */
620 	if (_chdrive(TOLOWER_ASC(path[0]) - 'a' + 1) == 0)
621 	    path += 2;
622     }
623 
624     if (*path == NUL)		/* drive name only */
625 	return 0;
626 
627     return chdir(path);	       /* let the normal chdir() do the rest */
628 }
629 
630 
631 /*
632  * Switching off termcap mode is only allowed when Columns is 80, otherwise a
633  * crash may result.  It's always allowed on NT or when running the GUI.
634  */
635     int
636 can_end_termcap_mode(
637     int give_msg)
638 {
639 #ifdef FEAT_GUI_MSWIN
640     return TRUE;	/* GUI starts a new console anyway */
641 #else
642     if (g_PlatformId == VER_PLATFORM_WIN32_NT || Columns == 80)
643 	return TRUE;
644     if (give_msg)
645 	msg(_("'columns' is not 80, cannot execute external commands"));
646     return FALSE;
647 #endif
648 }
649 
650 #ifdef FEAT_GUI_MSWIN
651 /*
652  * return non-zero if a character is available
653  */
654     int
655 mch_char_avail()
656 {
657     /* never used */
658     return TRUE;
659 }
660 #endif
661 
662 
663 /*
664  * set screen mode, always fails.
665  */
666     int
667 mch_screenmode(
668     char_u *arg)
669 {
670     EMSG(_(e_screenmode));
671     return FAIL;
672 }
673 
674 
675 #if defined(FEAT_LIBCALL) || defined(PROTO)
676 /*
677  * Call a DLL routine which takes either a string or int param
678  * and returns an allocated string.
679  * Return OK if it worked, FAIL if not.
680  */
681 # ifdef WIN3264
682 typedef LPTSTR (*MYSTRPROCSTR)(LPTSTR);
683 typedef LPTSTR (*MYINTPROCSTR)(int);
684 typedef int (*MYSTRPROCINT)(LPTSTR);
685 typedef int (*MYINTPROCINT)(int);
686 # else
687 typedef LPSTR (*MYSTRPROCSTR)(LPSTR);
688 typedef LPSTR (*MYINTPROCSTR)(int);
689 typedef int (*MYSTRPROCINT)(LPSTR);
690 typedef int (*MYINTPROCINT)(int);
691 # endif
692 
693 # ifndef WIN16
694 /*
695  * Check if a pointer points to a valid NUL terminated string.
696  * Return the length of the string, including terminating NUL.
697  * Returns 0 for an invalid pointer, 1 for an empty string.
698  */
699     static size_t
700 check_str_len(char_u *str)
701 {
702     SYSTEM_INFO			si;
703     MEMORY_BASIC_INFORMATION	mbi;
704     size_t			length = 0;
705     size_t			i;
706     const char			*p;
707 
708     /* get page size */
709     GetSystemInfo(&si);
710 
711     /* get memory information */
712     if (VirtualQuery(str, &mbi, sizeof(mbi)))
713     {
714 	/* pre cast these (typing savers) */
715 	DWORD dwStr = (DWORD)str;
716 	DWORD dwBaseAddress = (DWORD)mbi.BaseAddress;
717 
718 	/* get start address of page that str is on */
719 	DWORD strPage = dwStr - (dwStr - dwBaseAddress) % si.dwPageSize;
720 
721 	/* get length from str to end of page */
722 	DWORD pageLength = si.dwPageSize - (dwStr - strPage);
723 
724 	for (p = str; !IsBadReadPtr(p, pageLength);
725 				  p += pageLength, pageLength = si.dwPageSize)
726 	    for (i = 0; i < pageLength; ++i, ++length)
727 		if (p[i] == NUL)
728 		    return length + 1;
729     }
730 
731     return 0;
732 }
733 # endif
734 
735     int
736 mch_libcall(
737     char_u	*libname,
738     char_u	*funcname,
739     char_u	*argstring,	/* NULL when using a argint */
740     int		argint,
741     char_u	**string_result,/* NULL when using number_result */
742     int		*number_result)
743 {
744     HINSTANCE		hinstLib;
745     MYSTRPROCSTR	ProcAdd;
746     MYINTPROCSTR	ProcAddI;
747     char_u		*retval_str = NULL;
748     int			retval_int = 0;
749     size_t		len;
750 
751     BOOL fRunTimeLinkSuccess = FALSE;
752 
753     // Get a handle to the DLL module.
754     hinstLib = LoadLibrary(libname);
755 
756     // If the handle is valid, try to get the function address.
757     if (hinstLib != NULL)
758     {
759 #ifdef HAVE_TRY_EXCEPT
760 	__try
761 	{
762 #endif
763 	if (argstring != NULL)
764 	{
765 	    /* Call with string argument */
766 	    ProcAdd = (MYSTRPROCSTR) GetProcAddress(hinstLib, funcname);
767 	    if ((fRunTimeLinkSuccess = (ProcAdd != NULL)) != 0)
768 	    {
769 		if (string_result == NULL)
770 		    retval_int = ((MYSTRPROCINT)ProcAdd)(argstring);
771 		else
772 		    retval_str = (ProcAdd)(argstring);
773 	    }
774 	}
775 	else
776 	{
777 	    /* Call with number argument */
778 	    ProcAddI = (MYINTPROCSTR) GetProcAddress(hinstLib, funcname);
779 	    if ((fRunTimeLinkSuccess = (ProcAddI != NULL)) != 0)
780 	    {
781 		if (string_result == NULL)
782 		    retval_int = ((MYINTPROCINT)ProcAddI)(argint);
783 		else
784 		    retval_str = (ProcAddI)(argint);
785 	    }
786 	}
787 
788 	// Save the string before we free the library.
789 	// Assume that a "1" result is an illegal pointer.
790 	if (string_result == NULL)
791 	    *number_result = retval_int;
792 	else if (retval_str != NULL
793 # ifdef WIN16
794 		&& retval_str != (char_u *)1
795 		&& retval_str != (char_u *)-1
796 		&& !IsBadStringPtr(retval_str, INT_MAX)
797 		&& (len = strlen(retval_str) + 1) > 0
798 # else
799 		&& (len = check_str_len(retval_str)) > 0
800 # endif
801 		)
802 	{
803 	    *string_result = lalloc((long_u)len, TRUE);
804 	    if (*string_result != NULL)
805 		mch_memmove(*string_result, retval_str, len);
806 	}
807 
808 #ifdef HAVE_TRY_EXCEPT
809 	}
810 	__except(EXCEPTION_EXECUTE_HANDLER)
811 	{
812 	    if (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW)
813 		RESETSTKOFLW();
814 	    fRunTimeLinkSuccess = 0;
815 	}
816 #endif
817 
818 	// Free the DLL module.
819 	(void)FreeLibrary(hinstLib);
820     }
821 
822     if (!fRunTimeLinkSuccess)
823     {
824 	EMSG2(_(e_libcall), funcname);
825 	return FAIL;
826     }
827 
828     return OK;
829 }
830 #endif
831 
832 #if defined(FEAT_MBYTE) || defined(PROTO)
833 /*
834  * Convert an UTF-8 string to UCS-2.
835  * "instr[inlen]" is the input.  "inlen" is in bytes.
836  * When "outstr" is NULL only return the number of UCS-2 words produced.
837  * Otherwise "outstr" must be a buffer of sufficient size.
838  * Returns the number of UCS-2 words produced.
839  */
840     int
841 utf8_to_ucs2(char_u *instr, int inlen, short_u *outstr, int *unconvlenp)
842 {
843     int		outlen = 0;
844     char_u	*p = instr;
845     int		todo = inlen;
846     int		l;
847 
848     while (todo > 0)
849     {
850 	/* Only convert if we have a complete sequence. */
851 	l = utf_ptr2len_check_len(p, todo);
852 	if (l > todo)
853 	{
854 	    /* Return length of incomplete sequence. */
855 	    if (unconvlenp != NULL)
856 		*unconvlenp = todo;
857 	    break;
858 	}
859 
860 	if (outstr != NULL)
861 	    *outstr++ = utf_ptr2char(p);
862 	++outlen;
863 	p += l;
864 	todo -= l;
865     }
866 
867     return outlen;
868 }
869 
870 /*
871  * Convert an UCS-2 string to UTF-8.
872  * The input is "instr[inlen]" with "inlen" in number of ucs-2 words.
873  * When "outstr" is NULL only return the required number of bytes.
874  * Otherwise "outstr" must be a buffer of sufficient size.
875  * Return the number of bytes produced.
876  */
877     int
878 ucs2_to_utf8(short_u *instr, int inlen, char_u *outstr)
879 {
880     int		outlen = 0;
881     int		todo = inlen;
882     short_u	*p = instr;
883     int		l;
884 
885     while (todo > 0)
886     {
887 	if (outstr != NULL)
888 	{
889 	    l = utf_char2bytes(*p, outstr);
890 	    outstr += l;
891 	}
892 	else
893 	    l = utf_char2len(*p);
894 	++p;
895 	outlen += l;
896 	--todo;
897     }
898 
899     return outlen;
900 }
901 
902 /*
903  * Call MultiByteToWideChar() and allocate memory for the result.
904  * Returns the result in "*out[*outlen]" with an extra zero appended.
905  * "outlen" is in words.
906  */
907     void
908 MultiByteToWideChar_alloc(UINT cp, DWORD flags,
909 	LPCSTR in, int inlen,
910 	LPWSTR *out, int *outlen)
911 {
912     *outlen = MultiByteToWideChar(cp, flags, in, inlen, 0, 0);
913     /* Add one one word to avoid a zero-length alloc(). */
914     *out = (LPWSTR)alloc(sizeof(WCHAR) * (*outlen + 1));
915     if (*out != NULL)
916     {
917 	MultiByteToWideChar(cp, flags, in, inlen, *out, *outlen);
918 	(*out)[*outlen] = 0;
919     }
920 }
921 
922 /*
923  * Call WideCharToMultiByte() and allocate memory for the result.
924  * Returns the result in "*out[*outlen]" with an extra NUL appended.
925  */
926     void
927 WideCharToMultiByte_alloc(UINT cp, DWORD flags,
928 	LPCWSTR in, int inlen,
929 	LPSTR *out, int *outlen,
930 	LPCSTR def, LPBOOL useddef)
931 {
932     *outlen = WideCharToMultiByte(cp, flags, in, inlen, NULL, 0, def, useddef);
933     /* Add one one byte to avoid a zero-length alloc(). */
934     *out = alloc((unsigned)*outlen + 1);
935     if (*out != NULL)
936     {
937 	WideCharToMultiByte(cp, flags, in, inlen, *out, *outlen, def, useddef);
938 	(*out)[*outlen] = 0;
939     }
940 }
941 
942 #endif /* FEAT_MBYTE */
943 
944 #ifdef FEAT_CLIPBOARD
945 /*
946  * Clipboard stuff, for cutting and pasting text to other windows.
947  */
948 
949 /* Type used for the clipboard type of Vim's data. */
950 typedef struct
951 {
952     int type;		/* MCHAR, MBLOCK or MLINE */
953     int txtlen;		/* length of CF_TEXT in bytes */
954     int ucslen;		/* length of CF_UNICODETEXT in words */
955     int rawlen;		/* length of clip_star.format_raw, including encoding,
956 			   excluding terminating NUL */
957 } VimClipType_t;
958 
959 /*
960  * Make vim the owner of the current selection.  Return OK upon success.
961  */
962     int
963 clip_mch_own_selection(VimClipboard *cbd)
964 {
965     /*
966      * Never actually own the clipboard.  If another application sets the
967      * clipboard, we don't want to think that we still own it.
968      */
969     return FAIL;
970 }
971 
972 /*
973  * Make vim NOT the owner of the current selection.
974  */
975     void
976 clip_mch_lose_selection(VimClipboard *cbd)
977 {
978     /* Nothing needs to be done here */
979 }
980 
981 /*
982  * Copy "str[*size]" into allocated memory, changing CR-NL to NL.
983  * Return the allocated result and the size in "*size".
984  * Returns NULL when out of memory.
985  */
986     static char_u *
987 crnl_to_nl(const char_u *str, int *size)
988 {
989     int		pos = 0;
990     int		str_len = *size;
991     char_u	*ret;
992     char_u	*retp;
993 
994     /* Avoid allocating zero bytes, it generates an error message. */
995     ret = lalloc((long_u)(str_len == 0 ? 1 : str_len), TRUE);
996     if (ret != NULL)
997     {
998 	retp = ret;
999 	for (pos = 0; pos < str_len; ++pos)
1000 	{
1001 	    if (str[pos] == '\r' && str[pos + 1] == '\n')
1002 	    {
1003 		++pos;
1004 		--(*size);
1005 	    }
1006 	    *retp++ = str[pos];
1007 	}
1008     }
1009 
1010     return ret;
1011 }
1012 
1013 #if defined(FEAT_MBYTE) || defined(PROTO)
1014 /*
1015  * Note: the following two functions are only guaranteed to work when using
1016  * valid MS-Windows codepages or when iconv() is available.
1017  */
1018 
1019 /*
1020  * Convert "str" from 'encoding' to UCS-2.
1021  * Input in "str" with length "*lenp".  When "lenp" is NULL, use strlen().
1022  * Output is returned as an allocated string.  "*lenp" is set to the length of
1023  * the result.  A trailing NUL is always added.
1024  * Returns NULL when out of memory.
1025  */
1026     short_u *
1027 enc_to_ucs2(char_u *str, int *lenp)
1028 {
1029     vimconv_T	conv;
1030     WCHAR	*ret;
1031     char_u	*allocbuf = NULL;
1032     int		len_loc;
1033     int		length;
1034 
1035     if (lenp == NULL)
1036     {
1037 	len_loc = STRLEN(str) + 1;
1038 	lenp = &len_loc;
1039     }
1040 
1041     if (enc_codepage > 0)
1042     {
1043 	/* We can do any CP### -> UCS-2 in one pass, and we can do it
1044 	 * without iconv() (convert_* may need iconv). */
1045 	MultiByteToWideChar_alloc(enc_codepage, 0, str, *lenp, &ret, &length);
1046     }
1047     else
1048     {
1049 	/* Use "latin1" by default, we might be called before we have p_enc
1050 	 * set up.  Convert to utf-8 first, works better with iconv().  Does
1051 	 * nothing if 'encoding' is "utf-8". */
1052 	conv.vc_type = CONV_NONE;
1053 	if (convert_setup(&conv, p_enc ? p_enc : (char_u *)"latin1",
1054 						   (char_u *)"utf-8") == FAIL)
1055 	    return NULL;
1056 	if (conv.vc_type != CONV_NONE)
1057 	{
1058 	    str = allocbuf = string_convert(&conv, str, lenp);
1059 	    if (str == NULL)
1060 		return NULL;
1061 	}
1062 	convert_setup(&conv, NULL, NULL);
1063 
1064 	length = utf8_to_ucs2(str, *lenp, NULL, NULL);
1065 	ret = (WCHAR *)alloc((unsigned)((length + 1) * sizeof(WCHAR)));
1066 	if (ret != NULL)
1067 	{
1068 	    utf8_to_ucs2(str, *lenp, (short_u *)ret, NULL);
1069 	    ret[length] = 0;
1070 	}
1071 
1072 	vim_free(allocbuf);
1073     }
1074 
1075     *lenp = length;
1076     return (short_u *)ret;
1077 }
1078 
1079 /*
1080  * Convert an UCS-2 string to 'encoding'.
1081  * Input in "str" with length (counted in wide characters) "*lenp".  When
1082  * "lenp" is NULL, use wcslen().
1083  * Output is returned as an allocated string.  If "*lenp" is not NULL it is
1084  * set to the length of the result.
1085  * Returns NULL when out of memory.
1086  */
1087     char_u *
1088 ucs2_to_enc(short_u *str, int *lenp)
1089 {
1090     vimconv_T	conv;
1091     char_u	*utf8_str = NULL, *enc_str = NULL;
1092     int		len_loc;
1093 
1094     if (lenp == NULL)
1095     {
1096 	len_loc = wcslen(str) + 1;
1097 	lenp = &len_loc;
1098     }
1099 
1100     if (enc_codepage > 0)
1101     {
1102 	/* We can do any UCS-2 -> CP### in one pass. */
1103 	int length;
1104 
1105 	WideCharToMultiByte_alloc(enc_codepage, 0, str, *lenp,
1106 					    (LPSTR *)&enc_str, &length, 0, 0);
1107 	*lenp = length;
1108 	return enc_str;
1109     }
1110 
1111     /* Avoid allocating zero bytes, it generates an error message. */
1112     utf8_str = alloc(ucs2_to_utf8(str, *lenp == 0 ? 1 : *lenp, NULL));
1113     if (utf8_str != NULL)
1114     {
1115 	*lenp = ucs2_to_utf8(str, *lenp, utf8_str);
1116 
1117 	/* We might be called before we have p_enc set up. */
1118 	conv.vc_type = CONV_NONE;
1119 	convert_setup(&conv, (char_u *)"utf-8",
1120 					    p_enc? p_enc: (char_u *)"latin1");
1121 	if (conv.vc_type == CONV_NONE)
1122 	{
1123 	    /* p_enc is utf-8, so we're done. */
1124 	    enc_str = utf8_str;
1125 	}
1126 	else
1127 	{
1128 	    enc_str = string_convert(&conv, utf8_str, lenp);
1129 	    vim_free(utf8_str);
1130 	}
1131 
1132 	convert_setup(&conv, NULL, NULL);
1133     }
1134 
1135     return enc_str;
1136 }
1137 #endif /* FEAT_MBYTE */
1138 
1139 /*
1140  * Get the current selection and put it in the clipboard register.
1141  *
1142  * NOTE: Must use GlobalLock/Unlock here to ensure Win32s compatibility.
1143  * On NT/W95 the clipboard data is a fixed global memory object and
1144  * so its handle = its pointer.
1145  * On Win32s, however, co-operation with the Win16 system means that
1146  * the clipboard data is moveable and its handle is not a pointer at all,
1147  * so we can't just cast the return value of GetClipboardData to (char_u*).
1148  * <VN>
1149  */
1150     void
1151 clip_mch_request_selection(VimClipboard *cbd)
1152 {
1153     VimClipType_t	metadata = { -1, -1, -1, -1 };
1154     HGLOBAL		hMem = NULL;
1155     char_u		*str = NULL;
1156 #if defined(FEAT_MBYTE) && defined(WIN3264)
1157     char_u		*to_free = NULL;
1158 #endif
1159 #ifdef FEAT_MBYTE
1160     HGLOBAL		rawh = NULL;
1161 #endif
1162     char_u		*hMemStr = NULL;
1163     int			str_size = 0;
1164     int			maxlen;
1165     size_t		n;
1166 
1167     /*
1168      * Don't pass GetActiveWindow() as an argument to OpenClipboard() because
1169      * then we can't paste back into the same window for some reason - webb.
1170      */
1171     if (!OpenClipboard(NULL))
1172 	return;
1173 
1174     /* Check for vim's own clipboard format first.  This only gets the type of
1175      * the data, still need to use CF_UNICODETEXT or CF_TEXT for the text. */
1176     if (IsClipboardFormatAvailable(cbd->format))
1177     {
1178 	VimClipType_t	*meta_p;
1179 	HGLOBAL		meta_h;
1180 
1181 	/* We have metadata on the clipboard; try to get it. */
1182 	if ((meta_h = GetClipboardData(cbd->format)) != NULL
1183 		&& (meta_p = (VimClipType_t *)GlobalLock(meta_h)) != NULL)
1184 	{
1185 	    /* The size of "VimClipType_t" changed, "rawlen" was added later.
1186 	     * Only copy what is available for backwards compatibility. */
1187 	    n = sizeof(VimClipType_t);
1188 	    if (GlobalSize(meta_h) < n)
1189 		n = GlobalSize(meta_h);
1190 	    memcpy(&metadata, meta_p, n);
1191 	    GlobalUnlock(meta_h);
1192 	}
1193     }
1194 
1195 #ifdef FEAT_MBYTE
1196     /* Check for Vim's raw clipboard format first.  This is used without
1197      * conversion, but only if 'encoding' matches. */
1198     if (IsClipboardFormatAvailable(cbd->format_raw)
1199 				      && metadata.rawlen > (int)STRLEN(p_enc))
1200     {
1201 	/* We have raw data on the clipboard; try to get it. */
1202 	if ((rawh = GetClipboardData(cbd->format_raw)) != NULL)
1203 	{
1204 	    char_u	*rawp;
1205 
1206 	    rawp = (char_u *)GlobalLock(rawh);
1207 	    if (rawp != NULL && STRCMP(p_enc, rawp) == 0)
1208 	    {
1209 		n = STRLEN(p_enc) + 1;
1210 		str = rawp + n;
1211 		str_size = metadata.rawlen - n;
1212 	    }
1213 	    else
1214 	    {
1215 		GlobalUnlock(rawh);
1216 		rawh = NULL;
1217 	    }
1218 	}
1219     }
1220     if (str == NULL)
1221     {
1222 #endif
1223 
1224 #if defined(FEAT_MBYTE) && defined(WIN3264)
1225     /* Try to get the clipboard in Unicode if it's not an empty string. */
1226     if (IsClipboardFormatAvailable(CF_UNICODETEXT) && metadata.ucslen != 0)
1227     {
1228 	HGLOBAL hMemW;
1229 
1230 	if ((hMemW = GetClipboardData(CF_UNICODETEXT)) != NULL)
1231 	{
1232 	    WCHAR *hMemWstr = (WCHAR *)GlobalLock(hMemW);
1233 
1234 	    /* Use the length of our metadata if possible, but limit it to the
1235 	     * GlobalSize() for safety. */
1236 	    maxlen = GlobalSize(hMemW) / sizeof(WCHAR);
1237 	    if (metadata.ucslen >= 0)
1238 	    {
1239 		if (metadata.ucslen > maxlen)
1240 		    str_size = maxlen;
1241 		else
1242 		    str_size = metadata.ucslen;
1243 	    }
1244 	    else
1245 	    {
1246 		for (str_size = 0; str_size < maxlen; ++str_size)
1247 		    if (hMemWstr[str_size] == NUL)
1248 			break;
1249 	    }
1250 	    to_free = str = ucs2_to_enc((short_u *)hMemWstr, &str_size);
1251 	    GlobalUnlock(hMemW);
1252 	}
1253     }
1254     else
1255 #endif
1256     /* Get the clipboard in the Active codepage. */
1257     if (IsClipboardFormatAvailable(CF_TEXT))
1258     {
1259 	if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
1260 	{
1261 	    str = hMemStr = (char_u *)GlobalLock(hMem);
1262 
1263 	    /* The length is either what our metadata says or the strlen().
1264 	     * But limit it to the GlobalSize() for safety. */
1265 	    maxlen = GlobalSize(hMem);
1266 	    if (metadata.txtlen >= 0)
1267 	    {
1268 		if (metadata.txtlen > maxlen)
1269 		    str_size = maxlen;
1270 		else
1271 		    str_size = metadata.txtlen;
1272 	    }
1273 	    else
1274 	    {
1275 		for (str_size = 0; str_size < maxlen; ++str_size)
1276 		    if (str[str_size] == NUL)
1277 			break;
1278 	    }
1279 
1280 #if defined(FEAT_MBYTE) && defined(WIN3264)
1281 	    /* The text is in the active codepage.  Convert to 'encoding',
1282 	     * going through UCS-2. */
1283 	    MultiByteToWideChar_alloc(GetACP(), 0, str, str_size,
1284 						 (LPWSTR *)&to_free, &maxlen);
1285 	    if (to_free != NULL)
1286 	    {
1287 		str_size = maxlen;
1288 		str = ucs2_to_enc((short_u *)to_free, &str_size);
1289 		if (str != NULL)
1290 		{
1291 		    vim_free(to_free);
1292 		    to_free = str;
1293 		}
1294 	    }
1295 #endif
1296 	}
1297     }
1298 #ifdef FEAT_MBYTE
1299     }
1300 #endif
1301 
1302     if (str != NULL && *str != NUL)
1303     {
1304 	char_u *temp_clipboard;
1305 
1306 	/* If the type is not known guess it. */
1307 	if (metadata.type == -1)
1308 	    metadata.type = (vim_strchr(str, '\n') == NULL) ? MCHAR : MLINE;
1309 
1310 	/* Translate <CR><NL> into <NL>. */
1311 	temp_clipboard = crnl_to_nl(str, &str_size);
1312 	if (temp_clipboard != NULL)
1313 	{
1314 	    clip_yank_selection(metadata.type, temp_clipboard, str_size, cbd);
1315 	    vim_free(temp_clipboard);
1316 	}
1317     }
1318 
1319     /* unlock the global object */
1320     if (hMem != NULL)
1321 	GlobalUnlock(hMem);
1322 #ifdef FEAT_MBYTE
1323     if (rawh != NULL)
1324 	GlobalUnlock(rawh);
1325 #endif
1326     CloseClipboard();
1327 #if defined(FEAT_MBYTE) && defined(WIN3264)
1328     vim_free(to_free);
1329 #endif
1330 }
1331 
1332 /*
1333  * Send the current selection to the clipboard.
1334  */
1335     void
1336 clip_mch_set_selection(VimClipboard *cbd)
1337 {
1338     char_u		*str = NULL;
1339     VimClipType_t	metadata;
1340     long_u		txtlen;
1341     HGLOBAL		hMemRaw = NULL;
1342     HGLOBAL		hMem = NULL;
1343     HGLOBAL		hMemVim = NULL;
1344 # if defined(FEAT_MBYTE) && defined(WIN3264)
1345     HGLOBAL		hMemW = NULL;
1346 # endif
1347 
1348     /* If the '*' register isn't already filled in, fill it in now */
1349     cbd->owned = TRUE;
1350     clip_get_selection(cbd);
1351     cbd->owned = FALSE;
1352 
1353     /* Get the text to be put on the clipboard, with CR-LF. */
1354     metadata.type = clip_convert_selection(&str, &txtlen, cbd);
1355     if (metadata.type < 0)
1356 	return;
1357     metadata.txtlen = (int)txtlen;
1358     metadata.ucslen = 0;
1359     metadata.rawlen = 0;
1360 
1361 #ifdef FEAT_MBYTE
1362     /* Always set the raw bytes: 'encoding', NUL and the text.  This is used
1363      * when copy/paste from/to Vim with the same 'encoding', so that illegal
1364      * bytes can also be copied and no conversion is needed. */
1365     {
1366 	LPSTR lpszMemRaw;
1367 
1368 	metadata.rawlen = txtlen + STRLEN(p_enc) + 1;
1369 	hMemRaw = (LPSTR)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1370 							 metadata.rawlen + 1);
1371 	lpszMemRaw = (LPSTR)GlobalLock(hMemRaw);
1372 	if (lpszMemRaw != NULL)
1373 	{
1374 	    STRCPY(lpszMemRaw, p_enc);
1375 	    memcpy(lpszMemRaw + STRLEN(p_enc) + 1, str, txtlen + 1);
1376 	    GlobalUnlock(hMemRaw);
1377 	}
1378 	else
1379 	    metadata.rawlen = 0;
1380     }
1381 #endif
1382 
1383 # if defined(FEAT_MBYTE) && defined(WIN3264)
1384     {
1385 	WCHAR		*out;
1386 	int		len = metadata.txtlen;
1387 
1388 	/* Convert the text to UCS-2. This is put on the clipboard as
1389 	 * CF_UNICODETEXT. */
1390 	out = (WCHAR *)enc_to_ucs2(str, &len);
1391 	if (out != NULL)
1392 	{
1393 	    WCHAR *lpszMemW;
1394 
1395 	    /* Convert the text for CF_TEXT to Active codepage. Otherwise it's
1396 	     * p_enc, which has no relation to the Active codepage. */
1397 	    metadata.txtlen = WideCharToMultiByte(GetACP(), 0, out, len,
1398 							       NULL, 0, 0, 0);
1399 	    vim_free(str);
1400 	    str = (char_u *)alloc((unsigned)(metadata.txtlen == 0 ? 1
1401 							  : metadata.txtlen));
1402 	    if (str == NULL)
1403 	    {
1404 		vim_free(out);
1405 		return;		/* out of memory */
1406 	    }
1407 	    WideCharToMultiByte(GetACP(), 0, out, len,
1408 						  str, metadata.txtlen, 0, 0);
1409 
1410 	    /* Allocate memory for the UCS-2 text, add one NUL word to
1411 	     * terminate the string. */
1412 	    hMemW = (LPSTR)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
1413 						   (len + 1) * sizeof(WCHAR));
1414 	    lpszMemW = (WCHAR *)GlobalLock(hMemW);
1415 	    if (lpszMemW != NULL)
1416 	    {
1417 		memcpy(lpszMemW, out, len * sizeof(WCHAR));
1418 		lpszMemW[len] = NUL;
1419 		GlobalUnlock(hMemW);
1420 	    }
1421 	    vim_free(out);
1422 	    metadata.ucslen = len;
1423 	}
1424     }
1425 # endif
1426 
1427     /* Allocate memory for the text, add one NUL byte to terminate the string.
1428      */
1429     hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, metadata.txtlen + 1);
1430     {
1431 	LPSTR lpszMem = (LPSTR)GlobalLock(hMem);
1432 
1433 	if (lpszMem)
1434 	{
1435 	    STRNCPY(lpszMem, str, metadata.txtlen);
1436 	    lpszMem[metadata.txtlen] = NUL;
1437 	    GlobalUnlock(hMem);
1438 	}
1439     }
1440 
1441     /* Set up metadata: */
1442     {
1443 	VimClipType_t *lpszMemVim = NULL;
1444 
1445 	hMemVim = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,
1446 						       sizeof(VimClipType_t));
1447 	lpszMemVim = (VimClipType_t *)GlobalLock(hMemVim);
1448 	memcpy(lpszMemVim, &metadata, sizeof(metadata));
1449 	GlobalUnlock(hMemVim);
1450     }
1451 
1452     /*
1453      * Open the clipboard, clear it and put our text on it.
1454      * Always set our Vim format.  Put Unicode and plain text on it.
1455      *
1456      * Don't pass GetActiveWindow() as an argument to OpenClipboard()
1457      * because then we can't paste back into the same window for some
1458      * reason - webb.
1459      */
1460     if (OpenClipboard(NULL))
1461     {
1462 	if (EmptyClipboard())
1463 	{
1464 	    SetClipboardData(cbd->format, hMemVim);
1465 	    hMemVim = 0;
1466 # if defined(FEAT_MBYTE) && defined(WIN3264)
1467 	    if (hMemW != NULL)
1468 	    {
1469 		if (SetClipboardData(CF_UNICODETEXT, hMemW) != NULL)
1470 		    hMemW = NULL;
1471 	    }
1472 # endif
1473 	    /* Always use CF_TEXT.  On Win98 Notepad won't obtain the
1474 	     * CF_UNICODETEXT text, only CF_TEXT. */
1475 	    SetClipboardData(CF_TEXT, hMem);
1476 	    hMem = 0;
1477 	}
1478 	CloseClipboard();
1479     }
1480 
1481     vim_free(str);
1482     /* Free any allocations we didn't give to the clipboard: */
1483     if (hMemRaw)
1484 	GlobalFree(hMemRaw);
1485     if (hMem)
1486 	GlobalFree(hMem);
1487 # if defined(FEAT_MBYTE) && defined(WIN3264)
1488     if (hMemW)
1489 	GlobalFree(hMemW);
1490 # endif
1491     if (hMemVim)
1492 	GlobalFree(hMemVim);
1493 }
1494 
1495 #endif /* FEAT_CLIPBOARD */
1496 
1497 
1498 /*
1499  * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
1500  */
1501     void
1502 DumpPutS(
1503     const char *psz)
1504 {
1505 # ifdef MCH_WRITE_DUMP
1506     if (fdDump)
1507     {
1508 	fputs(psz, fdDump);
1509 	if (psz[strlen(psz) - 1] != '\n')
1510 	    fputc('\n', fdDump);
1511 	fflush(fdDump);
1512     }
1513 # endif
1514 }
1515 
1516 #ifdef _DEBUG
1517 
1518 void __cdecl
1519 Trace(
1520     char *pszFormat,
1521     ...)
1522 {
1523     CHAR szBuff[2048];
1524     va_list args;
1525 
1526     va_start(args, pszFormat);
1527     vsprintf(szBuff, pszFormat, args);
1528     va_end(args);
1529 
1530     OutputDebugString(szBuff);
1531 }
1532 
1533 #endif //_DEBUG
1534 
1535 #if !defined(FEAT_GUI) || defined(PROTO)
1536 # if defined(FEAT_TITLE) && defined(WIN3264)
1537 extern HWND g_hWnd;	/* This is in os_win32.c. */
1538 # endif
1539 
1540 /*
1541  * Showing the printer dialog is tricky since we have no GUI
1542  * window to parent it. The following routines are needed to
1543  * get the window parenting and Z-order to work properly.
1544  */
1545     static void
1546 GetConsoleHwnd(void)
1547 {
1548 # define MY_BUFSIZE 1024 // Buffer size for console window titles.
1549 
1550     char pszNewWindowTitle[MY_BUFSIZE]; // Contains fabricated WindowTitle.
1551     char pszOldWindowTitle[MY_BUFSIZE]; // Contains original WindowTitle.
1552 
1553     /* Skip if it's already set. */
1554     if (s_hwnd != 0)
1555 	return;
1556 
1557 # if defined(FEAT_TITLE) && defined(WIN3264)
1558     /* Window handle may have been found by init code (Windows NT only) */
1559     if (g_hWnd != 0)
1560     {
1561 	s_hwnd = g_hWnd;
1562 	return;
1563     }
1564 # endif
1565 
1566     GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);
1567 
1568     wsprintf(pszNewWindowTitle, "%s/%d/%d",
1569 	    pszOldWindowTitle,
1570 	    GetTickCount(),
1571 	    GetCurrentProcessId());
1572     SetConsoleTitle(pszNewWindowTitle);
1573     Sleep(40);
1574     s_hwnd = FindWindow(NULL, pszNewWindowTitle);
1575 
1576     SetConsoleTitle(pszOldWindowTitle);
1577 }
1578 
1579 /*
1580  * Console implementation of ":winpos".
1581  */
1582     int
1583 mch_get_winpos(int *x, int *y)
1584 {
1585     RECT  rect;
1586 
1587     GetConsoleHwnd();
1588     GetWindowRect(s_hwnd, &rect);
1589     *x = rect.left;
1590     *y = rect.top;
1591     return OK;
1592 }
1593 
1594 /*
1595  * Console implementation of ":winpos x y".
1596  */
1597     void
1598 mch_set_winpos(int x, int y)
1599 {
1600     GetConsoleHwnd();
1601     SetWindowPos(s_hwnd, NULL, x, y, 0, 0,
1602 		 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1603 }
1604 #endif
1605 
1606 #if (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) || defined(PROTO)
1607 
1608 # ifdef WIN16
1609 #  define TEXT(a) a
1610 # endif
1611 /*=================================================================
1612  * Win32 printer stuff
1613  */
1614 
1615 static HFONT		prt_font_handles[2][2][2];
1616 static PRINTDLG		prt_dlg;
1617 static const int	boldface[2] = {FW_REGULAR, FW_BOLD};
1618 static TEXTMETRIC	prt_tm;
1619 static int		prt_line_height;
1620 static int		prt_number_width;
1621 static int		prt_left_margin;
1622 static int		prt_right_margin;
1623 static int		prt_top_margin;
1624 static char_u		szAppName[] = TEXT("VIM");
1625 static HWND		hDlgPrint;
1626 static int		*bUserAbort = NULL;
1627 static char_u		*prt_name = NULL;
1628 
1629 /* Defines which are also in vim.rc. */
1630 #define IDC_BOX1		400
1631 #define IDC_PRINTTEXT1		401
1632 #define IDC_PRINTTEXT2		402
1633 #define IDC_PROGRESS		403
1634 
1635 /*
1636  * Convert BGR to RGB for Windows GDI calls
1637  */
1638     static COLORREF
1639 swap_me(COLORREF colorref)
1640 {
1641     int  temp;
1642     char *ptr = (char *)&colorref;
1643 
1644     temp = *(ptr);
1645     *(ptr ) = *(ptr + 2);
1646     *(ptr + 2) = temp;
1647     return colorref;
1648 }
1649 
1650     static BOOL CALLBACK
1651 PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1652 {
1653 #ifdef FEAT_GETTEXT
1654     NONCLIENTMETRICS nm;
1655     static HFONT hfont;
1656 #endif
1657 
1658     switch (message)
1659     {
1660 	case WM_INITDIALOG:
1661 #ifdef FEAT_GETTEXT
1662 	    nm.cbSize = sizeof(NONCLIENTMETRICS);
1663 	    if (SystemParametersInfo(
1664 			SPI_GETNONCLIENTMETRICS,
1665 			sizeof(NONCLIENTMETRICS),
1666 			&nm,
1667 			0))
1668 	    {
1669 		char buff[MAX_PATH];
1670 		int i;
1671 
1672 		/* Translate the dialog texts */
1673 		hfont = CreateFontIndirect(&nm.lfMessageFont);
1674 		for (i = IDC_PRINTTEXT1; i <= IDC_PROGRESS; i++)
1675 		{
1676 		    SendDlgItemMessage(hDlg, i, WM_SETFONT, (WPARAM)hfont, 1);
1677 		    if (GetDlgItemText(hDlg,i, buff, sizeof(buff)))
1678 			SetDlgItemText(hDlg,i, _(buff));
1679 		}
1680 		SendDlgItemMessage(hDlg, IDCANCEL,
1681 						WM_SETFONT, (WPARAM)hfont, 1);
1682 		if (GetDlgItemText(hDlg,IDCANCEL, buff, sizeof(buff)))
1683 		    SetDlgItemText(hDlg,IDCANCEL, _(buff));
1684 	    }
1685 #endif
1686 	    SetWindowText(hDlg, szAppName);
1687 	    if (prt_name != NULL)
1688 	    {
1689 		SetDlgItemText(hDlg, IDC_PRINTTEXT2, (LPSTR)prt_name);
1690 		vim_free(prt_name);
1691 		prt_name = NULL;
1692 	    }
1693 	    EnableMenuItem(GetSystemMenu(hDlg, FALSE), SC_CLOSE, MF_GRAYED);
1694 #ifndef FEAT_GUI
1695 	    BringWindowToTop(s_hwnd);
1696 #endif
1697 	    return TRUE;
1698 
1699 	case WM_COMMAND:
1700 	    *bUserAbort = TRUE;
1701 	    EnableWindow(GetParent(hDlg), TRUE);
1702 	    DestroyWindow(hDlg);
1703 	    hDlgPrint = NULL;
1704 #ifdef FEAT_GETTEXT
1705 	    DeleteObject(hfont);
1706 #endif
1707 	    return TRUE;
1708     }
1709     return FALSE;
1710 }
1711 
1712     static BOOL CALLBACK
1713 AbortProc(HDC hdcPrn, int iCode)
1714 {
1715     MSG msg;
1716 
1717     while (!*bUserAbort && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
1718     {
1719 	if (!hDlgPrint || !IsDialogMessage(hDlgPrint, &msg))
1720 	{
1721 	    TranslateMessage(&msg);
1722 	    DispatchMessage(&msg);
1723 	}
1724     }
1725     return !*bUserAbort;
1726 }
1727 
1728 #ifndef FEAT_GUI
1729 
1730     static UINT CALLBACK
1731 PrintHookProc(
1732 	HWND hDlg,	// handle to dialog box
1733 	UINT uiMsg,	// message identifier
1734 	WPARAM wParam,	// message parameter
1735 	LPARAM lParam	// message parameter
1736 	)
1737 {
1738     HWND	hwndOwner;
1739     RECT	rc, rcDlg, rcOwner;
1740     PRINTDLG	*pPD;
1741 
1742     if (uiMsg == WM_INITDIALOG)
1743     {
1744 	// Get the owner window and dialog box rectangles.
1745 	if ((hwndOwner = GetParent(hDlg)) == NULL)
1746 	    hwndOwner = GetDesktopWindow();
1747 
1748 	GetWindowRect(hwndOwner, &rcOwner);
1749 	GetWindowRect(hDlg, &rcDlg);
1750 	CopyRect(&rc, &rcOwner);
1751 
1752 	// Offset the owner and dialog box rectangles so that
1753 	// right and bottom values represent the width and
1754 	// height, and then offset the owner again to discard
1755 	// space taken up by the dialog box.
1756 
1757 	OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
1758 	OffsetRect(&rc, -rc.left, -rc.top);
1759 	OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
1760 
1761 	// The new position is the sum of half the remaining
1762 	// space and the owner's original position.
1763 
1764 	SetWindowPos(hDlg,
1765 		HWND_TOP,
1766 		rcOwner.left + (rc.right / 2),
1767 		rcOwner.top + (rc.bottom / 2),
1768 		0, 0,		// ignores size arguments
1769 		SWP_NOSIZE);
1770 
1771 	/*  tackle the printdlg copiesctrl problem */
1772 	pPD = (PRINTDLG *)lParam;
1773 	pPD->nCopies = (WORD)pPD->lCustData;
1774 	SetDlgItemInt( hDlg, edt3, pPD->nCopies, FALSE );
1775 	/*  Bring the window to top */
1776 	BringWindowToTop(GetParent(hDlg));
1777 	SetForegroundWindow(hDlg);
1778     }
1779 
1780     return FALSE;
1781 }
1782 #endif
1783 
1784     void
1785 mch_print_cleanup(void)
1786 {
1787     int pifItalic;
1788     int pifBold;
1789     int pifUnderline;
1790 
1791     for (pifBold = 0; pifBold <= 1; pifBold++)
1792 	for (pifItalic = 0; pifItalic <= 1; pifItalic++)
1793 	    for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
1794 		DeleteObject(prt_font_handles[pifBold][pifItalic][pifUnderline]);
1795 
1796     if (prt_dlg.hDC != NULL)
1797 	DeleteDC(prt_dlg.hDC);
1798     if (!*bUserAbort)
1799 	SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
1800 }
1801 
1802     static int
1803 to_device_units(int idx, int dpi, int physsize, int offset, int def_number)
1804 {
1805     int		ret = 0;
1806     int		u;
1807     int		nr;
1808 
1809     u = prt_get_unit(idx);
1810     if (u == PRT_UNIT_NONE)
1811     {
1812 	u = PRT_UNIT_PERC;
1813 	nr = def_number;
1814     }
1815     else
1816 	nr = printer_opts[idx].number;
1817 
1818     switch (u)
1819     {
1820 	case PRT_UNIT_PERC:
1821 	    ret = (physsize * nr) / 100;
1822 	    break;
1823 	case PRT_UNIT_INCH:
1824 	    ret = (nr * dpi);
1825 	    break;
1826 	case PRT_UNIT_MM:
1827 	    ret = (nr * 10 * dpi) / 254;
1828 	    break;
1829 	case PRT_UNIT_POINT:
1830 	    ret = (nr * 10 * dpi) / 720;
1831 	    break;
1832     }
1833 
1834     if (ret < offset)
1835 	return 0;
1836     else
1837 	return ret - offset;
1838 }
1839 
1840     static int
1841 prt_get_cpl(void)
1842 {
1843     int		hr;
1844     int		phyw;
1845     int		dvoff;
1846     int		rev_offset;
1847     int		dpi;
1848 #ifdef WIN16
1849     POINT	pagesize;
1850 #endif
1851 
1852     GetTextMetrics(prt_dlg.hDC, &prt_tm);
1853     prt_line_height = prt_tm.tmHeight + prt_tm.tmExternalLeading;
1854 
1855     hr	    = GetDeviceCaps(prt_dlg.hDC, HORZRES);
1856 #ifdef WIN16
1857     Escape(prt_dlg.hDC, GETPHYSPAGESIZE, NULL, NULL, &pagesize);
1858     phyw    = pagesize.x;
1859     Escape(prt_dlg.hDC, GETPRINTINGOFFSET, NULL, NULL, &pagesize);
1860     dvoff   = pagesize.x;
1861 #else
1862     phyw    = GetDeviceCaps(prt_dlg.hDC, PHYSICALWIDTH);
1863     dvoff   = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETX);
1864 #endif
1865     dpi	    = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSX);
1866 
1867     rev_offset = phyw - (dvoff + hr);
1868 
1869     prt_left_margin = to_device_units(OPT_PRINT_LEFT, dpi, phyw, dvoff, 10);
1870     if (prt_use_number())
1871     {
1872 	prt_number_width = PRINT_NUMBER_WIDTH * prt_tm.tmAveCharWidth;
1873 	prt_left_margin += prt_number_width;
1874     }
1875     else
1876 	prt_number_width = 0;
1877 
1878     prt_right_margin = hr - to_device_units(OPT_PRINT_RIGHT, dpi, phyw,
1879 							       rev_offset, 5);
1880 
1881     return (prt_right_margin - prt_left_margin) / prt_tm.tmAveCharWidth;
1882 }
1883 
1884     static int
1885 prt_get_lpp(void)
1886 {
1887     int vr;
1888     int phyw;
1889     int dvoff;
1890     int rev_offset;
1891     int	bottom_margin;
1892     int	dpi;
1893 #ifdef WIN16
1894     POINT pagesize;
1895 #endif
1896 
1897     vr	    = GetDeviceCaps(prt_dlg.hDC, VERTRES);
1898 #ifdef WIN16
1899     Escape(prt_dlg.hDC, GETPHYSPAGESIZE, NULL, NULL, &pagesize);
1900     phyw    = pagesize.y;
1901     Escape(prt_dlg.hDC, GETPRINTINGOFFSET, NULL, NULL, &pagesize);
1902     dvoff   = pagesize.y;
1903 #else
1904     phyw    = GetDeviceCaps(prt_dlg.hDC, PHYSICALHEIGHT);
1905     dvoff   = GetDeviceCaps(prt_dlg.hDC, PHYSICALOFFSETY);
1906 #endif
1907     dpi	    = GetDeviceCaps(prt_dlg.hDC, LOGPIXELSY);
1908 
1909     rev_offset = phyw - (dvoff + vr);
1910 
1911     prt_top_margin = to_device_units(OPT_PRINT_TOP, dpi, phyw, dvoff, 5);
1912 
1913     /* adjust top margin if there is a header */
1914     prt_top_margin += prt_line_height * prt_header_height();
1915 
1916     bottom_margin = vr - to_device_units(OPT_PRINT_BOT, dpi, phyw,
1917 							       rev_offset, 5);
1918 
1919     return (bottom_margin - prt_top_margin) / prt_line_height;
1920 }
1921 
1922     int
1923 mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
1924 {
1925     static HGLOBAL	stored_dm = NULL;
1926     static HGLOBAL	stored_devn = NULL;
1927     static int		stored_nCopies = 1;
1928     static int		stored_nFlags = 0;
1929 
1930     LOGFONT		fLogFont;
1931     int			pifItalic;
1932     int			pifBold;
1933     int			pifUnderline;
1934 
1935     DEVMODE		*mem;
1936     DEVNAMES		*devname;
1937     int			i;
1938 
1939     bUserAbort = &(psettings->user_abort);
1940     memset(&prt_dlg, 0, sizeof(PRINTDLG));
1941     prt_dlg.lStructSize = sizeof(PRINTDLG);
1942 #ifndef FEAT_GUI
1943     GetConsoleHwnd();	    /* get value of s_hwnd */
1944 #endif
1945     prt_dlg.hwndOwner = s_hwnd;
1946     prt_dlg.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
1947     if (!forceit)
1948     {
1949 	prt_dlg.hDevMode = stored_dm;
1950 	prt_dlg.hDevNames = stored_devn;
1951 	prt_dlg.lCustData = stored_nCopies; // work around bug in print dialog
1952 #ifndef FEAT_GUI
1953 	/*
1954 	 * Use hook to prevent console window being sent to back
1955 	 */
1956 	prt_dlg.lpfnPrintHook = PrintHookProc;
1957 	prt_dlg.Flags |= PD_ENABLEPRINTHOOK;
1958 #endif
1959 	prt_dlg.Flags |= stored_nFlags;
1960     }
1961 
1962     /*
1963      * If bang present, return default printer setup with no dialog
1964      * never show dialog if we are running over telnet
1965      */
1966     if (forceit
1967 #ifndef FEAT_GUI
1968 	    || !term_console
1969 #endif
1970 	    )
1971     {
1972 	prt_dlg.Flags |= PD_RETURNDEFAULT;
1973 #ifdef WIN3264
1974 	/*
1975 	 * MSDN suggests setting the first parameter to WINSPOOL for
1976 	 * NT, but NULL appears to work just as well.
1977 	 */
1978 	if (*p_pdev != NUL)
1979 	    prt_dlg.hDC = CreateDC(NULL, p_pdev, NULL, NULL);
1980 	else
1981 #endif
1982 	{
1983 	    prt_dlg.Flags |= PD_RETURNDEFAULT;
1984 	    if (PrintDlg(&prt_dlg) == 0)
1985 		goto init_fail_dlg;
1986 	}
1987     }
1988     else if (PrintDlg(&prt_dlg) == 0)
1989 	goto init_fail_dlg;
1990     else
1991     {
1992 	/*
1993 	 * keep the previous driver context
1994 	 */
1995 	stored_dm = prt_dlg.hDevMode;
1996 	stored_devn = prt_dlg.hDevNames;
1997 	stored_nFlags = prt_dlg.Flags;
1998 	stored_nCopies = prt_dlg.nCopies;
1999     }
2000 
2001     if (prt_dlg.hDC == NULL)
2002     {
2003 	EMSG(_("E237: Printer selection failed"));
2004 	mch_print_cleanup();
2005 	return FALSE;
2006     }
2007 
2008     /* Not all printer drivers report the support of color (or grey) in the
2009      * same way.  Let's set has_color if there appears to be some way to print
2010      * more than B&W. */
2011     i = GetDeviceCaps(prt_dlg.hDC, NUMCOLORS);
2012     psettings->has_color = (GetDeviceCaps(prt_dlg.hDC, BITSPIXEL) > 1
2013 				   || GetDeviceCaps(prt_dlg.hDC, PLANES) > 1
2014 				   || i > 2 || i == -1);
2015 
2016     /* Ensure all font styles are baseline aligned */
2017     SetTextAlign(prt_dlg.hDC, TA_BASELINE|TA_LEFT);
2018 
2019     /*
2020      * On some windows systems the nCopies parameter is not
2021      * passed back correctly. It must be retrieved from the
2022      * hDevMode struct.
2023      */
2024     mem = (DEVMODE *)GlobalLock(prt_dlg.hDevMode);
2025     if (mem != NULL)
2026     {
2027 #ifdef WIN3264
2028 	if (mem->dmCopies != 1)
2029 	    stored_nCopies = mem->dmCopies;
2030 #endif
2031 	if ((mem->dmFields & DM_DUPLEX) && (mem->dmDuplex & ~DMDUP_SIMPLEX))
2032 	    psettings->duplex = TRUE;
2033 	if ((mem->dmFields & DM_COLOR) && (mem->dmColor & DMCOLOR_COLOR))
2034 	    psettings->has_color = TRUE;
2035     }
2036     GlobalUnlock(prt_dlg.hDevMode);
2037 
2038     devname = (DEVNAMES *)GlobalLock(prt_dlg.hDevNames);
2039     if (devname != 0)
2040     {
2041 	char_u	*printer_name = (char_u *)devname + devname->wDeviceOffset;
2042 	char_u	*port_name = (char_u *)devname +devname->wOutputOffset;
2043 	char_u	*text = _("to %s on %s");
2044 
2045 	prt_name = alloc(STRLEN(printer_name) + STRLEN(port_name)
2046 							      + STRLEN(text));
2047 	if (prt_name != NULL)
2048 	    wsprintf(prt_name, text, printer_name, port_name);
2049     }
2050     GlobalUnlock(prt_dlg.hDevNames);
2051 
2052     /*
2053      * Initialise the font according to 'printfont'
2054      */
2055     memset(&fLogFont, 0, sizeof(fLogFont));
2056     if (!get_logfont(&fLogFont, p_pfn, prt_dlg.hDC))
2057     {
2058 	EMSG2(_("E613: Unknown printer font: %s"), p_pfn);
2059 	mch_print_cleanup();
2060 	return FALSE;
2061     }
2062 
2063     for (pifBold = 0; pifBold <= 1; pifBold++)
2064 	for (pifItalic = 0; pifItalic <= 1; pifItalic++)
2065 	    for (pifUnderline = 0; pifUnderline <= 1; pifUnderline++)
2066 	    {
2067 		fLogFont.lfWeight =  boldface[pifBold];
2068 		fLogFont.lfItalic = pifItalic;
2069 		fLogFont.lfUnderline = pifUnderline;
2070 		prt_font_handles[pifBold][pifItalic][pifUnderline]
2071 					      = CreateFontIndirect(&fLogFont);
2072 	    }
2073 
2074     SetBkMode(prt_dlg.hDC, OPAQUE);
2075     SelectObject(prt_dlg.hDC, prt_font_handles[0][0][0]);
2076 
2077     /*
2078      * Fill in the settings struct
2079      */
2080     psettings->chars_per_line = prt_get_cpl();
2081     psettings->lines_per_page = prt_get_lpp();
2082     psettings->n_collated_copies = (prt_dlg.Flags & PD_COLLATE)
2083 							? prt_dlg.nCopies : 1;
2084     psettings->n_uncollated_copies = (prt_dlg.Flags & PD_COLLATE)
2085 							? 1 : prt_dlg.nCopies;
2086 
2087     if (psettings->n_collated_copies == 0)
2088 	psettings->n_collated_copies = 1;
2089 
2090     if (psettings->n_uncollated_copies == 0)
2091 	psettings->n_uncollated_copies = 1;
2092 
2093     psettings->jobname = jobname;
2094 
2095     return TRUE;
2096 
2097 init_fail_dlg:
2098     {
2099 	DWORD err = CommDlgExtendedError();
2100 
2101 	if (err)
2102 	{
2103 #ifdef WIN16
2104 	    char buf[20];
2105 
2106 	    sprintf(buf, "%ld", err);
2107 	    EMSG2(_("E238: Print error: %s"), buf);
2108 #else
2109 	    char_u *buf;
2110 
2111 	    /* I suspect FormatMessage() doesn't work for values returned by
2112 	     * CommDlgExtendedError().  What does? */
2113 	    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
2114 			  FORMAT_MESSAGE_FROM_SYSTEM |
2115 			  FORMAT_MESSAGE_IGNORE_INSERTS,
2116 			  NULL, err, 0, (LPTSTR)(&buf), 0, NULL);
2117 	    EMSG2(_("E238: Print error: %s"),
2118 				  buf == NULL ? (char_u *)_("Unknown") : buf);
2119 	    LocalFree((LPVOID)(buf));
2120 #endif
2121 	}
2122 	else
2123 	    msg_clr_eos(); /* Maybe canceled */
2124 
2125 	mch_print_cleanup();
2126 	return FALSE;
2127     }
2128 }
2129 
2130 
2131     int
2132 mch_print_begin(prt_settings_T *psettings)
2133 {
2134     int			ret;
2135     static DOCINFO	di;
2136     char		szBuffer[300];
2137 
2138     hDlgPrint = CreateDialog(GetModuleHandle(NULL), TEXT("PrintDlgBox"),
2139 					     prt_dlg.hwndOwner, PrintDlgProc);
2140 #ifdef WIN16
2141     Escape(prt_dlg.hDC, SETABORTPROC, 0, (LPSTR)AbortProc, NULL);
2142 #else
2143     SetAbortProc(prt_dlg.hDC, AbortProc);
2144 #endif
2145     wsprintf(szBuffer, _("Printing '%s'"), gettail(psettings->jobname));
2146     SetDlgItemText(hDlgPrint, IDC_PRINTTEXT1, (LPSTR)szBuffer);
2147 
2148     memset(&di, 0, sizeof(DOCINFO));
2149     di.cbSize = sizeof(DOCINFO);
2150     di.lpszDocName = psettings->jobname;
2151     ret = StartDoc(prt_dlg.hDC, &di);
2152 
2153 #ifdef FEAT_GUI
2154     /* Give focus back to main window (when using MDI). */
2155     SetFocus(s_hwnd);
2156 #endif
2157 
2158     return (ret > 0);
2159 }
2160 
2161     void
2162 mch_print_end(prt_settings_T *psettings)
2163 {
2164     EndDoc(prt_dlg.hDC);
2165     if (!*bUserAbort)
2166 	SendMessage(hDlgPrint, WM_COMMAND, 0, 0);
2167 }
2168 
2169     int
2170 mch_print_end_page(void)
2171 {
2172     return (EndPage(prt_dlg.hDC) > 0);
2173 }
2174 
2175     int
2176 mch_print_begin_page(char_u *msg)
2177 {
2178     if (msg != NULL)
2179 	SetDlgItemText(hDlgPrint, IDC_PROGRESS, (LPSTR)msg);
2180     return (StartPage(prt_dlg.hDC) > 0);
2181 }
2182 
2183     int
2184 mch_print_blank_page(void)
2185 {
2186     return (mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE);
2187 }
2188 
2189 static int prt_pos_x = 0;
2190 static int prt_pos_y = 0;
2191 
2192     void
2193 mch_print_start_line(margin, page_line)
2194     int		margin;
2195     int		page_line;
2196 {
2197     if (margin)
2198 	prt_pos_x = -prt_number_width;
2199     else
2200 	prt_pos_x = 0;
2201     prt_pos_y = page_line * prt_line_height
2202 				 + prt_tm.tmAscent + prt_tm.tmExternalLeading;
2203 }
2204 
2205     int
2206 mch_print_text_out(char_u *p, int len)
2207 {
2208 #ifdef FEAT_PROPORTIONAL_FONTS
2209     SIZE	sz;
2210 #endif
2211 
2212     TextOut(prt_dlg.hDC, prt_pos_x + prt_left_margin,
2213 					  prt_pos_y + prt_top_margin, p, len);
2214 #ifndef FEAT_PROPORTIONAL_FONTS
2215     prt_pos_x += len * prt_tm.tmAveCharWidth;
2216     return (prt_pos_x + prt_left_margin + prt_tm.tmAveCharWidth
2217 				     + prt_tm.tmOverhang > prt_right_margin);
2218 #else
2219 # ifdef WIN16
2220     GetTextExtentPoint(prt_dlg.hDC, p, len, &sz);
2221 # else
2222     GetTextExtentPoint32(prt_dlg.hDC, p, len, &sz);
2223 # endif
2224     prt_pos_x += (sz.cx - prt_tm.tmOverhang);
2225     /* This is wrong when printing spaces for a TAB. */
2226     if (p[len] == NUL)
2227 	return FALSE;
2228 # ifdef WIN16
2229     GetTextExtentPoint(prt_dlg.hDC, p + len, 1, &sz);
2230 # else
2231     GetTextExtentPoint32(prt_dlg.hDC, p + len, 1, &sz);
2232 # endif
2233     return (prt_pos_x + prt_left_margin + sz.cx > prt_right_margin);
2234 #endif
2235 }
2236 
2237     void
2238 mch_print_set_font(int iBold, int iItalic, int iUnderline)
2239 {
2240     SelectObject(prt_dlg.hDC, prt_font_handles[iBold][iItalic][iUnderline]);
2241 }
2242 
2243     void
2244 mch_print_set_bg(unsigned long bgcol)
2245 {
2246     SetBkColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC, swap_me(bgcol)));
2247     /*
2248      * With a white background we can draw characters transparent, which is
2249      * good for italic characters that overlap to the next char cell.
2250      */
2251     if (bgcol == 0xffffffUL)
2252 	SetBkMode(prt_dlg.hDC, TRANSPARENT);
2253     else
2254 	SetBkMode(prt_dlg.hDC, OPAQUE);
2255 }
2256 
2257     void
2258 mch_print_set_fg(unsigned long fgcol)
2259 {
2260     SetTextColor(prt_dlg.hDC, GetNearestColor(prt_dlg.hDC, swap_me(fgcol)));
2261 }
2262 
2263 #endif /*FEAT_PRINTER && !FEAT_POSTSCRIPT*/
2264 
2265 #if defined(FEAT_SHORTCUT) || defined(PROTO)
2266 # include <shlobj.h>
2267 
2268 /*
2269  * When "fname" is the name of a shortcut (*.lnk) resolve the file it points
2270  * to and return that name in allocated memory.
2271  * Otherwise NULL is returned.
2272  */
2273     char_u *
2274 mch_resolve_shortcut(char_u *fname)
2275 {
2276     HRESULT		hr;
2277     IShellLink		*psl = NULL;
2278     IPersistFile	*ppf = NULL;
2279     OLECHAR		wsz[MAX_PATH];
2280     WIN32_FIND_DATA	ffd; // we get those free of charge
2281     TCHAR		buf[MAX_PATH]; // could have simply reused 'wsz'...
2282     char_u		*rfname = NULL;
2283     int			len;
2284 
2285     /* Check if the file name ends in ".lnk". Avoid calling
2286      * CoCreateInstance(), it's quite slow. */
2287     if (fname == NULL)
2288 	return rfname;
2289     len = STRLEN(fname);
2290     if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0)
2291 	return rfname;
2292 
2293     CoInitialize(NULL);
2294 
2295     // create a link manager object and request its interface
2296     hr = CoCreateInstance(
2297 	    &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
2298 	    &IID_IShellLink, (void**)&psl);
2299     if (hr != S_OK)
2300 	goto shortcut_error;
2301 
2302     // Get a pointer to the IPersistFile interface.
2303     hr = psl->lpVtbl->QueryInterface(
2304 	    psl, &IID_IPersistFile, (void**)&ppf);
2305     if (hr != S_OK)
2306 	goto shortcut_error;
2307 
2308     // full path string must be in Unicode.
2309     MultiByteToWideChar(CP_ACP, 0, fname, -1, wsz, MAX_PATH);
2310 
2311     // "load" the name and resove the link
2312     hr = ppf->lpVtbl->Load(ppf, wsz, STGM_READ);
2313     if (hr != S_OK)
2314 	goto shortcut_error;
2315 #if 0  // This makes Vim wait a long time if the target doesn't exist.
2316     hr = psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI);
2317     if (hr != S_OK)
2318 	goto shortcut_error;
2319 #endif
2320 
2321     // Get the path to the link target.
2322     ZeroMemory(buf, MAX_PATH);
2323     hr = psl->lpVtbl->GetPath(psl, buf, MAX_PATH, &ffd, 0);
2324     if (hr == S_OK && buf[0] != NUL)
2325 	rfname = vim_strsave(buf);
2326 
2327 shortcut_error:
2328     // Release all interface pointers (both belong to the same object)
2329     if (ppf != NULL)
2330 	ppf->lpVtbl->Release(ppf);
2331     if (psl != NULL)
2332 	psl->lpVtbl->Release(psl);
2333 
2334     CoUninitialize();
2335     return rfname;
2336 }
2337 #endif
2338 
2339 #if (defined(FEAT_EVAL) && !defined(FEAT_GUI)) || defined(PROTO)
2340 /*
2341  * Bring ourselves to the foreground.  Does work if the OS doesn't allow it.
2342  */
2343     void
2344 win32_set_foreground()
2345 {
2346 # ifndef FEAT_GUI
2347     GetConsoleHwnd();	    /* get value of s_hwnd */
2348 # endif
2349     if (s_hwnd != 0)
2350 	SetForegroundWindow(s_hwnd);
2351 }
2352 #endif
2353 
2354 #if defined(FEAT_CLIENTSERVER) || defined(PROTO)
2355 /*
2356  * Client-server code for Vim
2357  *
2358  * Originally written by Paul Moore
2359  */
2360 
2361 /* In order to handle inter-process messages, we need to have a window. But
2362  * the functions in this module can be called before the main GUI window is
2363  * created (and may also be called in the console version, where there is no
2364  * GUI window at all).
2365  *
2366  * So we create a hidden window, and arrange to destroy it on exit.
2367  */
2368 HWND message_window = 0;	    /* window that's handling messsages */
2369 
2370 #define VIM_CLASSNAME      "VIM_MESSAGES"
2371 #define VIM_CLASSNAME_LEN  (sizeof(VIM_CLASSNAME) - 1)
2372 
2373 /* Communication is via WM_COPYDATA messages. The message type is send in
2374  * the dwData parameter. Types are defined here. */
2375 #define COPYDATA_KEYS		0
2376 #define COPYDATA_REPLY		1
2377 #define COPYDATA_EXPR		10
2378 #define COPYDATA_RESULT		11
2379 #define COPYDATA_ERROR_RESULT	12
2380 
2381 /* This is a structure containing a server HWND and its name. */
2382 struct server_id
2383 {
2384     HWND hwnd;
2385     char_u *name;
2386 };
2387 
2388 /*
2389  * Clean up on exit. This destroys the hidden message window.
2390  */
2391     static void
2392 #ifdef __BORLANDC__
2393     _RTLENTRYF
2394 #endif
2395 CleanUpMessaging(void)
2396 {
2397     if (message_window != 0)
2398     {
2399 	DestroyWindow(message_window);
2400 	message_window = 0;
2401     }
2402 }
2403 
2404 static int save_reply(HWND server, char_u *reply, int expr);
2405 
2406 /*s
2407  * The window procedure for the hidden message window.
2408  * It handles callback messages and notifications from servers.
2409  * In order to process these messages, it is necessary to run a
2410  * message loop. Code which may run before the main message loop
2411  * is started (in the GUI) is careful to pump messages when it needs
2412  * to. Features which require message delivery during normal use will
2413  * not work in the console version - this basically means those
2414  * features which allow Vim to act as a server, rather than a client.
2415  */
2416     static LRESULT CALLBACK
2417 Messaging_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2418 {
2419     if (msg == WM_COPYDATA)
2420     {
2421 	/* This is a message from another Vim. The dwData member of the
2422 	 * COPYDATASTRUCT determines the type of message:
2423 	 *   COPYDATA_KEYS:
2424 	 *	A key sequence. We are a server, and a client wants these keys
2425 	 *	adding to the input queue.
2426 	 *   COPYDATA_REPLY:
2427 	 *	A reply. We are a client, and a server has sent this message
2428 	 *	in response to a request.  (server2client())
2429 	 *   COPYDATA_EXPR:
2430 	 *	An expression. We are a server, and a client wants us to
2431 	 *	evaluate this expression.
2432 	 *   COPYDATA_RESULT:
2433 	 *	A reply. We are a client, and a server has sent this message
2434 	 *	in response to a COPYDATA_EXPR.
2435 	 *   COPYDATA_ERROR_RESULT:
2436 	 *	A reply. We are a client, and a server has sent this message
2437 	 *	in response to a COPYDATA_EXPR that failed to evaluate.
2438 	 */
2439 	COPYDATASTRUCT	*data = (COPYDATASTRUCT*)lParam;
2440 	HWND		sender = (HWND)wParam;
2441 	COPYDATASTRUCT	reply;
2442 	char_u		*res;
2443 	char_u		winstr[30];
2444 	int		retval;
2445 
2446 	switch (data->dwData)
2447 	{
2448 	case COPYDATA_KEYS:
2449 	    /* Remember who sent this, for <client> */
2450 	    clientWindow = sender;
2451 
2452 	    /* Add the received keys to the input buffer.  The loop waiting
2453 	     * for the user to do something should check the input buffer. */
2454 	    server_to_input_buf((char_u *)(data->lpData));
2455 
2456 # ifdef FEAT_GUI
2457 	    /* Wake up the main GUI loop. */
2458 	    if (s_hwnd != 0)
2459 		PostMessage(s_hwnd, WM_NULL, 0, 0);
2460 # endif
2461 	    return 1;
2462 
2463 	case COPYDATA_EXPR:
2464 	    /* Remember who sent this, for <client> */
2465 	    clientWindow = sender;
2466 
2467 	    res = eval_client_expr_to_string(data->lpData);
2468 	    if (res == NULL)
2469 	    {
2470 		res = vim_strsave(_(e_invexprmsg));
2471 		reply.dwData = COPYDATA_ERROR_RESULT;
2472 	    }
2473 	    else
2474 		reply.dwData = COPYDATA_RESULT;
2475 	    reply.lpData = res;
2476 	    reply.cbData = STRLEN(res) + 1;
2477 
2478 	    retval = SendMessage(sender, WM_COPYDATA, (WPARAM)message_window,
2479 							    (LPARAM)(&reply));
2480 	    vim_free(res);
2481 	    return retval;
2482 
2483 	case COPYDATA_REPLY:
2484 	case COPYDATA_RESULT:
2485 	case COPYDATA_ERROR_RESULT:
2486 	    if (data->lpData != NULL)
2487 	    {
2488 		save_reply(sender, data->lpData,
2489 			   (data->dwData == COPYDATA_REPLY ?  0 :
2490 			   (data->dwData == COPYDATA_RESULT ? 1 :
2491 							      2)));
2492 #ifdef FEAT_AUTOCMD
2493 		if (data->dwData == COPYDATA_REPLY)
2494 		{
2495 		    sprintf((char *)winstr, "0x%x", (unsigned)sender);
2496 		    apply_autocmds(EVENT_REMOTEREPLY, winstr, data->lpData,
2497 								TRUE, curbuf);
2498 		}
2499 #endif
2500 	    }
2501 	    return 1;
2502 	}
2503 
2504 	return 0;
2505     }
2506 
2507     else if (msg == WM_ACTIVATE && wParam == WA_ACTIVE)
2508     {
2509 	/* When the message window is activated (brought to the foreground),
2510 	 * this actually applies to the text window. */
2511 #ifndef FEAT_GUI
2512 	GetConsoleHwnd();	    /* get value of s_hwnd */
2513 #endif
2514 	if (s_hwnd != 0)
2515 	{
2516 	    SetForegroundWindow(s_hwnd);
2517 	    return 0;
2518 	}
2519     }
2520 
2521     return DefWindowProc(hwnd, msg, wParam, lParam);
2522 }
2523 
2524 /*
2525  * Initialise the message handling process.  This involves creating a window
2526  * to handle messages - the window will not be visible.
2527  */
2528     void
2529 serverInitMessaging(void)
2530 {
2531     WNDCLASS wndclass;
2532     HINSTANCE s_hinst;
2533 
2534     /* Clean up on exit */
2535     atexit(CleanUpMessaging);
2536 
2537     /* Register a window class - we only really care
2538      * about the window procedure
2539      */
2540     s_hinst = (HINSTANCE)GetModuleHandle(0);
2541     wndclass.style = 0;
2542     wndclass.lpfnWndProc = Messaging_WndProc;
2543     wndclass.cbClsExtra = 0;
2544     wndclass.cbWndExtra = 0;
2545     wndclass.hInstance = s_hinst;
2546     wndclass.hIcon = NULL;
2547     wndclass.hCursor = NULL;
2548     wndclass.hbrBackground = NULL;
2549     wndclass.lpszMenuName = NULL;
2550     wndclass.lpszClassName = VIM_CLASSNAME;
2551     RegisterClass(&wndclass);
2552 
2553     /* Create the message window. It will be hidden, so the details don't
2554      * matter.  Don't use WS_OVERLAPPEDWINDOW, it will make a shortcut remove
2555      * focus from gvim. */
2556     message_window = CreateWindow(VIM_CLASSNAME, "",
2557 			 WS_POPUPWINDOW | WS_CAPTION,
2558 			 CW_USEDEFAULT, CW_USEDEFAULT,
2559 			 100, 100, NULL, NULL,
2560 			 s_hinst, NULL);
2561 }
2562 
2563 /*
2564  * Get the title of the window "hwnd", which is the Vim server name, in
2565  * "name[namelen]" and return the length.
2566  * Returns zero if window "hwnd" is not a Vim server.
2567  */
2568     static int
2569 getVimServerName(HWND hwnd, char *name, int namelen)
2570 {
2571     int		len;
2572     char	buffer[VIM_CLASSNAME_LEN + 1];
2573 
2574     /* Ignore windows which aren't Vim message windows */
2575     len = GetClassName(hwnd, buffer, sizeof(buffer));
2576     if (len != VIM_CLASSNAME_LEN || STRCMP(buffer, VIM_CLASSNAME) != 0)
2577 	return 0;
2578 
2579     /* Get the title of the window */
2580     return GetWindowText(hwnd, name, namelen);
2581 }
2582 
2583     static BOOL CALLBACK
2584 enumWindowsGetServer(HWND hwnd, LPARAM lparam)
2585 {
2586     struct	server_id *id = (struct server_id *)lparam;
2587     char	server[MAX_PATH];
2588 
2589     /* Get the title of the window */
2590     if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2591 	return TRUE;
2592 
2593     /* If this is the server we're looking for, return its HWND */
2594     if (STRICMP(server, id->name) == 0)
2595     {
2596 	id->hwnd = hwnd;
2597 	return FALSE;
2598     }
2599 
2600     /* Otherwise, keep looking */
2601     return TRUE;
2602 }
2603 
2604     static BOOL CALLBACK
2605 enumWindowsGetNames(HWND hwnd, LPARAM lparam)
2606 {
2607     garray_T	*ga = (garray_T *)lparam;
2608     char	server[MAX_PATH];
2609 
2610     /* Get the title of the window */
2611     if (getVimServerName(hwnd, server, sizeof(server)) == 0)
2612 	return TRUE;
2613 
2614     /* Add the name to the list */
2615     ga_concat(ga, server);
2616     ga_concat(ga, "\n");
2617     return TRUE;
2618 }
2619 
2620     static HWND
2621 findServer(char_u *name)
2622 {
2623     struct server_id id;
2624 
2625     id.name = name;
2626     id.hwnd = 0;
2627 
2628     EnumWindows(enumWindowsGetServer, (LPARAM)(&id));
2629 
2630     return id.hwnd;
2631 }
2632 
2633     void
2634 serverSetName(char_u *name)
2635 {
2636     char_u	*ok_name;
2637     HWND	hwnd = 0;
2638     int		i = 0;
2639     char_u	*p;
2640 
2641     /* Leave enough space for a 9-digit suffix to ensure uniqueness! */
2642     ok_name = alloc(STRLEN(name) + 10);
2643 
2644     STRCPY(ok_name, name);
2645     p = ok_name + STRLEN(name);
2646 
2647     for (;;)
2648     {
2649 	/* This is inefficient - we're doing an EnumWindows loop for each
2650 	 * possible name. It would be better to grab all names in one go,
2651 	 * and scan the list each time...
2652 	 */
2653 	hwnd = findServer(ok_name);
2654 	if (hwnd == 0)
2655 	    break;
2656 
2657 	++i;
2658 	if (i >= 1000)
2659 	    break;
2660 
2661 	sprintf((char *)p, "%d", i);
2662     }
2663 
2664     if (hwnd != 0)
2665 	vim_free(ok_name);
2666     else
2667     {
2668 	/* Remember the name */
2669 	serverName = ok_name;
2670 #ifdef FEAT_TITLE
2671 	need_maketitle = TRUE;	/* update Vim window title later */
2672 #endif
2673 
2674 	/* Update the message window title */
2675 	SetWindowText(message_window, ok_name);
2676 
2677 #ifdef FEAT_EVAL
2678 	/* Set the servername variable */
2679 	set_vim_var_string(VV_SEND_SERVER, serverName, -1);
2680 #endif
2681     }
2682 }
2683 
2684     char_u *
2685 serverGetVimNames(void)
2686 {
2687     garray_T ga;
2688 
2689     ga_init2(&ga, 1, 100);
2690 
2691     EnumWindows(enumWindowsGetNames, (LPARAM)(&ga));
2692 
2693     return ga.ga_data;
2694 }
2695 
2696     int
2697 serverSendReply(name, reply)
2698     char_u	*name;		/* Where to send. */
2699     char_u	*reply;		/* What to send. */
2700 {
2701     HWND	target;
2702     COPYDATASTRUCT data;
2703     int		n = 0;
2704 
2705     /* The "name" argument is a magic cookie obtained from expand("<client>").
2706      * It should be of the form 0xXXXXX - i.e. a C hex literal, which is the
2707      * value of the client's message window HWND.
2708      */
2709     sscanf((char *)name, "%x", &n);
2710     if (n == 0)
2711 	return -1;
2712 
2713     target = (HWND)n;
2714     if (!IsWindow(target))
2715 	return -1;
2716 
2717     data.dwData = COPYDATA_REPLY;
2718     data.cbData = STRLEN(reply) + 1;
2719     data.lpData = reply;
2720 
2721     if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2722 							     (LPARAM)(&data)))
2723 	return 0;
2724 
2725     return -1;
2726 }
2727 
2728     int
2729 serverSendToVim(name, cmd, result, ptarget, asExpr, silent)
2730     char_u	 *name;			/* Where to send. */
2731     char_u	 *cmd;			/* What to send. */
2732     char_u	 **result;		/* Result of eval'ed expression */
2733     void	 *ptarget;		/* HWND of server */
2734     int		 asExpr;		/* Expression or keys? */
2735     int		 silent;		/* don't complain about no server */
2736 {
2737     HWND	target = findServer(name);
2738     COPYDATASTRUCT data;
2739     char_u	*retval = NULL;
2740     int		retcode = 0;
2741 
2742     if (target == 0)
2743     {
2744 	if (!silent)
2745 	    EMSG2(_(e_noserver), name);
2746 	return -1;
2747     }
2748 
2749     if (ptarget)
2750 	*(HWND *)ptarget = target;
2751 
2752     data.dwData = asExpr ? COPYDATA_EXPR : COPYDATA_KEYS;
2753     data.cbData = STRLEN(cmd) + 1;
2754     data.lpData = cmd;
2755 
2756     if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
2757 							(LPARAM)(&data)) == 0)
2758 	return -1;
2759 
2760     if (asExpr)
2761 	retval = serverGetReply(target, &retcode, TRUE, TRUE);
2762 
2763     if (result == NULL)
2764 	vim_free(retval);
2765     else
2766 	*result = retval; /* Caller assumes responsibility for freeing */
2767 
2768     return retcode;
2769 }
2770 
2771 /*
2772  * Bring the server to the foreground.
2773  */
2774     void
2775 serverForeground(name)
2776     char_u	*name;
2777 {
2778     HWND	target = findServer(name);
2779 
2780     if (target != 0)
2781 	SetForegroundWindow(target);
2782 }
2783 
2784 /* Replies from server need to be stored until the client picks them up via
2785  * remote_read(). So we maintain a list of server-id/reply pairs.
2786  * Note that there could be multiple replies from one server pending if the
2787  * client is slow picking them up.
2788  * We just store the replies in a simple list. When we remove an entry, we
2789  * move list entries down to fill the gap.
2790  * The server ID is simply the HWND.
2791  */
2792 typedef struct
2793 {
2794     HWND	server;		/* server window */
2795     char_u	*reply;		/* reply string */
2796     int		expr_result;	/* 0 for REPLY, 1 for RESULT 2 for error */
2797 }
2798 reply_T;
2799 
2800 static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
2801 
2802 #define REPLY_ITEM(i) ((reply_T *)(reply_list.ga_data) + (i))
2803 #define REPLY_COUNT (reply_list.ga_len)
2804 #define REPLY_ROOM (reply_list.ga_room)
2805 
2806 /* Flag which is used to wait for a reply */
2807 static int reply_received = 0;
2808 
2809     static int
2810 save_reply(HWND server, char_u *reply, int expr)
2811 {
2812     reply_T *rep;
2813 
2814     if (ga_grow(&reply_list, 1) == FAIL)
2815 	return FAIL;
2816 
2817     rep = REPLY_ITEM(REPLY_COUNT);
2818     rep->server = server;
2819     rep->reply = vim_strsave(reply);
2820     rep->expr_result = expr;
2821     if (rep->reply == NULL)
2822 	return FAIL;
2823 
2824     ++REPLY_COUNT;
2825     --REPLY_ROOM;
2826     reply_received = 1;
2827     return OK;
2828 }
2829 
2830 /*
2831  * Get a reply from server "server".
2832  * When "expr_res" is non NULL, get the result of an expression, otherwise a
2833  * server2client() message.
2834  * When non NULL, point to return code. 0 => OK, -1 => ERROR
2835  * If "remove" is TRUE, consume the message, the caller must free it then.
2836  * if "wait" is TRUE block until a message arrives (or the server exits).
2837  */
2838     char_u *
2839 serverGetReply(HWND server, int *expr_res, int remove, int wait)
2840 {
2841     int		i;
2842     char_u	*reply;
2843     reply_T	*rep;
2844 
2845     /* When waiting, loop until the message waiting for is received. */
2846     for (;;)
2847     {
2848 	/* Reset this here, in case a message arrives while we are going
2849 	 * through the already received messages. */
2850 	reply_received = 0;
2851 
2852 	for (i = 0; i < REPLY_COUNT; ++i)
2853 	{
2854 	    rep = REPLY_ITEM(i);
2855 	    if (rep->server == server
2856 		    && ((rep->expr_result != 0) == (expr_res != NULL)))
2857 	    {
2858 		/* Save the values we've found for later */
2859 		reply = rep->reply;
2860 		if (expr_res != NULL)
2861 		    *expr_res = rep->expr_result == 1 ? 0 : -1;
2862 
2863 		if (remove)
2864 		{
2865 		    /* Move the rest of the list down to fill the gap */
2866 		    mch_memmove(rep, rep + 1,
2867 				     (REPLY_COUNT - i - 1) * sizeof(reply_T));
2868 		    --REPLY_COUNT;
2869 		    ++REPLY_ROOM;
2870 		}
2871 
2872 		/* Return the reply to the caller, who takes on responsibility
2873 		 * for freeing it if "remove" is TRUE. */
2874 		return reply;
2875 	    }
2876 	}
2877 
2878 	/* If we got here, we didn't find a reply. Return immediately if the
2879 	 * "wait" parameter isn't set.  */
2880 	if (!wait)
2881 	    break;
2882 
2883 	/* We need to wait for a reply. Enter a message loop until the
2884 	 * "reply_received" flag gets set. */
2885 
2886 	/* Loop until we receive a reply */
2887 	while (reply_received == 0)
2888 	{
2889 	    /* Wait for a SendMessage() call to us.  This could be the reply
2890 	     * we are waiting for.  Use a timeout of a second, to catch the
2891 	     * situation that the server died unexpectedly. */
2892 	    MsgWaitForMultipleObjects(0, NULL, TRUE, 1000, QS_ALLINPUT);
2893 
2894 	    /* If the server has died, give up */
2895 	    if (!IsWindow(server))
2896 		return NULL;
2897 
2898 	    serverProcessPendingMessages();
2899 	}
2900     }
2901 
2902     return NULL;
2903 }
2904 
2905 /*
2906  * Process any messages in the Windows message queue.
2907  */
2908     void
2909 serverProcessPendingMessages(void)
2910 {
2911     MSG msg;
2912 
2913     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
2914     {
2915 	TranslateMessage(&msg);
2916 	DispatchMessage(&msg);
2917     }
2918 }
2919 
2920 #endif /* FEAT_CLIENTSERVER */
2921 
2922 #if defined(FEAT_GUI) || (defined(FEAT_PRINTER) && !defined(FEAT_POSTSCRIPT)) \
2923 	|| defined(PROTO)
2924 
2925 struct charset_pair
2926 {
2927     char	*name;
2928     BYTE	charset;
2929 };
2930 
2931 static struct charset_pair
2932 charset_pairs[] =
2933 {
2934     {"ANSI",		ANSI_CHARSET},
2935     {"CHINESEBIG5",	CHINESEBIG5_CHARSET},
2936     {"DEFAULT",		DEFAULT_CHARSET},
2937     {"HANGEUL",		HANGEUL_CHARSET},
2938     {"OEM",		OEM_CHARSET},
2939     {"SHIFTJIS",	SHIFTJIS_CHARSET},
2940     {"SYMBOL",		SYMBOL_CHARSET},
2941 #ifdef WIN3264
2942     {"ARABIC",		ARABIC_CHARSET},
2943     {"BALTIC",		BALTIC_CHARSET},
2944     {"EASTEUROPE",	EASTEUROPE_CHARSET},
2945     {"GB2312",		GB2312_CHARSET},
2946     {"GREEK",		GREEK_CHARSET},
2947     {"HEBREW",		HEBREW_CHARSET},
2948     {"JOHAB",		JOHAB_CHARSET},
2949     {"MAC",		MAC_CHARSET},
2950     {"RUSSIAN",		RUSSIAN_CHARSET},
2951     {"THAI",		THAI_CHARSET},
2952     {"TURKISH",		TURKISH_CHARSET},
2953 # if (!defined(_MSC_VER) || (_MSC_VER > 1010)) \
2954 	&& (!defined(__BORLANDC__) || (__BORLANDC__ > 0x0500))
2955     {"VIETNAMESE",	VIETNAMESE_CHARSET},
2956 # endif
2957 #endif
2958     {NULL,		0}
2959 };
2960 
2961 /*
2962  * Convert a charset ID to a name.
2963  * Return NULL when not recognized.
2964  */
2965     char *
2966 charset_id2name(int id)
2967 {
2968     struct charset_pair *cp;
2969 
2970     for (cp = charset_pairs; cp->name != NULL; ++cp)
2971 	if ((BYTE)id == cp->charset)
2972 	    break;
2973     return cp->name;
2974 }
2975 
2976 static const LOGFONT s_lfDefault =
2977 {
2978     -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2979     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2980     PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE,
2981     "Fixedsys"	/* see _ReadVimIni */
2982 };
2983 
2984 /* Initialise the "current height" to -12 (same as s_lfDefault) just
2985  * in case the user specifies a font in "guifont" with no size before a font
2986  * with an explicit size has been set. This defaults the size to this value
2987  * (-12 equates to roughly 9pt).
2988  */
2989 int current_font_height = -12;		/* also used in gui_w48.c */
2990 
2991 /* Convert a string representing a point size into pixels. The string should
2992  * be a positive decimal number, with an optional decimal point (eg, "12", or
2993  * "10.5"). The pixel value is returned, and a pointer to the next unconverted
2994  * character is stored in *end. The flag "vertical" says whether this
2995  * calculation is for a vertical (height) size or a horizontal (width) one.
2996  */
2997     static int
2998 points_to_pixels(char_u *str, char_u **end, int vertical, int pprinter_dc)
2999 {
3000     int		pixels;
3001     int		points = 0;
3002     int		divisor = 0;
3003     HWND	hwnd = (HWND)0;
3004     HDC		hdc;
3005     HDC		printer_dc = (HDC)pprinter_dc;
3006 
3007     while (*str != NUL)
3008     {
3009 	if (*str == '.' && divisor == 0)
3010 	{
3011 	    /* Start keeping a divisor, for later */
3012 	    divisor = 1;
3013 	}
3014 	else
3015 	{
3016 	    if (!VIM_ISDIGIT(*str))
3017 		break;
3018 
3019 	    points *= 10;
3020 	    points += *str - '0';
3021 	    divisor *= 10;
3022 	}
3023 	++str;
3024     }
3025 
3026     if (divisor == 0)
3027 	divisor = 1;
3028 
3029     if (printer_dc == NULL)
3030     {
3031 	hwnd = GetDesktopWindow();
3032 	hdc = GetWindowDC(hwnd);
3033     }
3034     else
3035 	hdc = printer_dc;
3036 
3037     pixels = MulDiv(points,
3038 		    GetDeviceCaps(hdc, vertical ? LOGPIXELSY : LOGPIXELSX),
3039 		    72 * divisor);
3040 
3041     if (printer_dc == NULL)
3042 	ReleaseDC(hwnd, hdc);
3043 
3044     *end = str;
3045     return pixels;
3046 }
3047 
3048     static int CALLBACK
3049 font_enumproc(
3050     ENUMLOGFONT	    *elf,
3051     NEWTEXTMETRIC   *ntm,
3052     int		    type,
3053     LPARAM	    lparam)
3054 {
3055     /* Return value:
3056      *	  0 = terminate now (monospace & ANSI)
3057      *	  1 = continue, still no luck...
3058      *	  2 = continue, but we have an acceptable LOGFONT
3059      *	      (monospace, not ANSI)
3060      * We use these values, as EnumFontFamilies returns 1 if the
3061      * callback function is never called. So, we check the return as
3062      * 0 = perfect, 2 = OK, 1 = no good...
3063      * It's not pretty, but it works!
3064      */
3065 
3066     LOGFONT *lf = (LOGFONT *)(lparam);
3067 
3068 #ifndef FEAT_PROPORTIONAL_FONTS
3069     /* Ignore non-monospace fonts without further ado */
3070     if ((ntm->tmPitchAndFamily & 1) != 0)
3071 	return 1;
3072 #endif
3073 
3074     /* Remember this LOGFONT as a "possible" */
3075     *lf = elf->elfLogFont;
3076 
3077     /* Terminate the scan as soon as we find an ANSI font */
3078     if (lf->lfCharSet == ANSI_CHARSET
3079 	    || lf->lfCharSet == OEM_CHARSET
3080 	    || lf->lfCharSet == DEFAULT_CHARSET)
3081 	return 0;
3082 
3083     /* Continue the scan - we have a non-ANSI font */
3084     return 2;
3085 }
3086 
3087     static int
3088 init_logfont(LOGFONT *lf)
3089 {
3090     int		n;
3091     HWND	hwnd = GetDesktopWindow();
3092     HDC		hdc = GetWindowDC(hwnd);
3093 
3094     n = EnumFontFamilies(hdc,
3095 			 (LPCSTR)lf->lfFaceName,
3096 			 (FONTENUMPROC)font_enumproc,
3097 			 (LPARAM)lf);
3098 
3099     ReleaseDC(hwnd, hdc);
3100 
3101     /* If we couldn't find a useable font, return failure */
3102     if (n == 1)
3103 	return FAIL;
3104 
3105     /* Tidy up the rest of the LOGFONT structure. We set to a basic
3106      * font - get_logfont() sets bold, italic, etc based on the user's
3107      * input.
3108      */
3109     lf->lfHeight = current_font_height;
3110     lf->lfWidth = 0;
3111     lf->lfItalic = FALSE;
3112     lf->lfUnderline = FALSE;
3113     lf->lfStrikeOut = FALSE;
3114     lf->lfWeight = FW_NORMAL;
3115 
3116     /* Return success */
3117     return OK;
3118 }
3119 
3120     int
3121 get_logfont(
3122     LOGFONT *lf,
3123     char_u  *name,
3124     HDC printer_dc)
3125 {
3126     char_u	*p;
3127     int		i;
3128     static LOGFONT *lastlf = NULL;
3129 
3130     *lf = s_lfDefault;
3131     if (name == NULL)
3132 	return 1;
3133 
3134     if (STRCMP(name, "*") == 0)
3135     {
3136 #if defined(FEAT_GUI_W32)
3137 	CHOOSEFONT	cf;
3138 	/* if name is "*", bring up std font dialog: */
3139 	memset(&cf, 0, sizeof(cf));
3140 	cf.lStructSize = sizeof(cf);
3141 	cf.hwndOwner = s_hwnd;
3142 	cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_INITTOLOGFONTSTRUCT;
3143 	if (lastlf != NULL)
3144 	    *lf = *lastlf;
3145 	cf.lpLogFont = lf;
3146 	cf.nFontType = 0 ; //REGULAR_FONTTYPE;
3147 	if (ChooseFont(&cf))
3148 	    goto theend;
3149 #else
3150 	return 0;
3151 #endif
3152     }
3153 
3154     /*
3155      * Split name up, it could be <name>:h<height>:w<width> etc.
3156      */
3157     for (p = name; *p && *p != ':'; p++)
3158     {
3159 	if (p - name + 1 > LF_FACESIZE)
3160 	    return 0;			/* Name too long */
3161 	lf->lfFaceName[p - name] = *p;
3162     }
3163     if (p != name)
3164 	lf->lfFaceName[p - name] = NUL;
3165 
3166     /* First set defaults */
3167     lf->lfHeight = -12;
3168     lf->lfWidth = 0;
3169     lf->lfWeight = FW_NORMAL;
3170     lf->lfItalic = FALSE;
3171     lf->lfUnderline = FALSE;
3172     lf->lfStrikeOut = FALSE;
3173 
3174     /*
3175      * If the font can't be found, try replacing '_' by ' '.
3176      */
3177     if (init_logfont(lf) == FAIL)
3178     {
3179 	int	did_replace = FALSE;
3180 
3181 	for (i = 0; lf->lfFaceName[i]; ++i)
3182 	    if (lf->lfFaceName[i] == '_')
3183 	    {
3184 		lf->lfFaceName[i] = ' ';
3185 		did_replace = TRUE;
3186 	    }
3187 	if (!did_replace || init_logfont(lf) == FAIL)
3188 	    return 0;
3189     }
3190 
3191     while (*p == ':')
3192 	p++;
3193 
3194     /* Set the values found after ':' */
3195     while (*p)
3196     {
3197 	switch (*p++)
3198 	{
3199 	    case 'h':
3200 		lf->lfHeight = - points_to_pixels(p, &p, TRUE, (int)printer_dc);
3201 		break;
3202 	    case 'w':
3203 		lf->lfWidth = points_to_pixels(p, &p, FALSE, (int)printer_dc);
3204 		break;
3205 	    case 'b':
3206 #ifndef MSWIN16_FASTTEXT
3207 		lf->lfWeight = FW_BOLD;
3208 #endif
3209 		break;
3210 	    case 'i':
3211 #ifndef MSWIN16_FASTTEXT
3212 		lf->lfItalic = TRUE;
3213 #endif
3214 		break;
3215 	    case 'u':
3216 		lf->lfUnderline = TRUE;
3217 		break;
3218 	    case 's':
3219 		lf->lfStrikeOut = TRUE;
3220 		break;
3221 	    case 'c':
3222 		{
3223 		    struct charset_pair *cp;
3224 
3225 		    for (cp = charset_pairs; cp->name != NULL; ++cp)
3226 			if (STRNCMP(p, cp->name, strlen(cp->name)) == 0)
3227 			{
3228 			    lf->lfCharSet = cp->charset;
3229 			    p += strlen(cp->name);
3230 			    break;
3231 			}
3232 		    if (cp->name == NULL)
3233 		    {
3234 			sprintf((char *)IObuff, _("E244: Illegal charset name \"%s\" in font name \"%s\""), p, name);
3235 			EMSG(IObuff);
3236 			break;
3237 		    }
3238 		    break;
3239 		}
3240 	    default:
3241 		sprintf((char *)IObuff,
3242 			_("E245: Illegal char '%c' in font name \"%s\""),
3243 			p[-1], name);
3244 		EMSG(IObuff);
3245 		break;
3246 	}
3247 	while (*p == ':')
3248 	    p++;
3249     }
3250 
3251 #if defined(FEAT_GUI_W32)
3252 theend:
3253 #endif
3254     /* ron: init lastlf */
3255     if (printer_dc == NULL)
3256     {
3257 	vim_free(lastlf);
3258 	lastlf = (LOGFONT *)alloc(sizeof(LOGFONT));
3259 	if (lastlf != NULL)
3260 	    mch_memmove(lastlf, lf, sizeof(LOGFONT));
3261     }
3262 
3263     return 1;
3264 }
3265 
3266 #endif /* defined(FEAT_GUI) || defined(FEAT_PRINTER) */
3267