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