1 /* GNUPLOT - win/winmain.c */
2 /*[
3  * Copyright 1992, 1993, 1998, 2004   Maurice Castro, Russell Lang
4  *
5  * Permission to use, copy, and distribute this software and its
6  * documentation for any purpose with or without fee is hereby granted,
7  * provided that the above copyright notice appear in all copies and
8  * that both that copyright notice and this permission notice appear
9  * in supporting documentation.
10  *
11  * Permission to modify the software is granted, but not the right to
12  * distribute the complete modified source code.  Modifications are to
13  * be distributed as patches to the released version.  Permission to
14  * distribute binaries produced by compiling modified sources is granted,
15  * provided you
16  *   1. distribute the corresponding source modifications from the
17  *    released version in the form of a patch file along with the binaries,
18  *   2. add special version identification to distinguish your version
19  *    in addition to the base release version number,
20  *   3. provide your name and address as the primary contact for the
21  *    support of your modified version, and
22  *   4. retain our contact information in regard to use of the base
23  *    software.
24  * Permission to distribute the released version of the source code along
25  * with corresponding source modifications in the form of a patch file is
26  * granted with same provisions 2 through 4 for binary distributions.
27  *
28  * This software is provided "as is" without express or implied warranty
29  * to the extent permitted by applicable law.
30 ]*/
31 
32 /*
33  * AUTHORS
34  *
35  *   Maurice Castro
36  *   Russell Lang
37  *
38  */
39 
40 /* This file implements the initialization code for running gnuplot   */
41 /* under Microsoft Windows.                                           */
42 /*                                                                    */
43 /* The modifications to allow Gnuplot to run under Windows were made  */
44 /* by Maurice Castro. (maurice@bruce.cs.monash.edu.au)  3 Jul 1992    */
45 /* and Russell Lang (rjl@monu1.cc.monash.edu.au) 30 Nov 1992          */
46 /*                                                                    */
47 
48 #include "syscfg.h"
49 #define STRICT
50 #include <windows.h>
51 #include <windowsx.h>
52 #include <commctrl.h>
53 #include <shlobj.h>
54 #include <shlwapi.h>
55 #include <htmlhelp.h>
56 #include <dos.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <stdarg.h>
61 #include <tchar.h>
62 #include <ctype.h>
63 #include <fcntl.h>
64 #include <io.h>
65 #include <sys/stat.h>
66 #include "alloc.h"
67 #include "plot.h"
68 #include "setshow.h"
69 #include "version.h"
70 #include "command.h"
71 #include "encoding.h"
72 #include "winmain.h"
73 #include "wtext.h"
74 #include "wcommon.h"
75 #ifdef HAVE_GDIPLUS
76 #include "wgdiplus.h"
77 #endif
78 #ifdef HAVE_D2D
79 #include "wd2d.h"
80 #endif
81 #ifdef WXWIDGETS
82 #include "wxterminal/wxt_term.h"
83 #endif
84 #ifdef HAVE_LIBCACA
85 # define TERM_PUBLIC_PROTO
86 # include "caca.trm"
87 # undef TERM_PUBLIC_PROTO
88 #endif
89 
90 
91 /* workaround for old header files */
92 #ifndef CSIDL_APPDATA
93 # define CSIDL_APPDATA (0x001a)
94 #endif
95 
96 /* limits */
97 #define MAXSTR 255
98 #define MAXPRINTF 1024
99   /* used if vsnprintf(NULL,0,...) returns zero (MingW 3.4) */
100 
101 /* globals */
102 #ifndef WGP_CONSOLE
103 TW textwin;
104 MW menuwin;
105 #endif
106 LPGW graphwin; /* current graph window */
107 LPGW listgraphs; /* list of graph windows */
108 PW pausewin;
109 LPTSTR szModuleName;
110 LPTSTR szPackageDir;
111 LPTSTR winhelpname;
112 LPTSTR szMenuName;
113 static LPTSTR szLanguageCode = NULL;
114 HWND help_window = NULL;
115 
116 char *authors[]={
117                  "Colin Kelley",
118                  "Thomas Williams"
119                 };
120 
121 void WinExit(void);
122 static void WinCloseHelp(void);
123 int CALLBACK ShutDown(void);
124 #ifdef WGP_CONSOLE
125 static int ConsolePutS(const char *str);
126 static int ConsolePutCh(int ch);
127 #endif
128 
129 
130 static void
CheckMemory(LPTSTR str)131 CheckMemory(LPTSTR str)
132 {
133     if (str == NULL) {
134 	MessageBox(NULL, TEXT("out of memory"), TEXT("gnuplot"), MB_ICONSTOP | MB_OK);
135 	gp_exit(EXIT_FAILURE);
136     }
137 }
138 
139 
140 int
Pause(LPSTR str)141 Pause(LPSTR str)
142 {
143     int rc;
144 
145     pausewin.Message = UnicodeText(str, encoding);
146     rc = PauseBox(&pausewin) == IDOK;
147     free(pausewin.Message);
148     return rc;
149 }
150 
151 
152 void
kill_pending_Pause_dialog(void)153 kill_pending_Pause_dialog(void)
154 {
155     if (!pausewin.bPause) /* no Pause dialog displayed */
156 	return;
157     /* Pause dialog displayed, thus kill it */
158     DestroyWindow(pausewin.hWndPause);
159     pausewin.bPause = FALSE;
160 }
161 
162 
163 /* atexit procedure */
164 void
WinExit(void)165 WinExit(void)
166 {
167     LPGW lpgw;
168 
169     /* Last chance to close Windows help, call before anything else to avoid a crash. */
170     WinCloseHelp();
171 
172     /* clean-up call for printing system */
173     PrintingCleanup();
174 
175     term_reset();
176 
177     _fcloseall();
178 
179     /* Close all graph windows */
180     for (lpgw = listgraphs; lpgw != NULL; lpgw = lpgw->next) {
181 	if (GraphHasWindow(lpgw))
182 	    GraphClose(lpgw);
183     }
184 
185 #ifndef WGP_CONSOLE
186     TextMessage();  /* process messages */
187 # ifndef __WATCOMC__
188     /* revert C++ stream redirection */
189     RedirectOutputStreams(FALSE);
190 # endif
191 #endif
192 #ifdef HAVE_GDIPLUS
193     gdiplusCleanup();
194 #endif
195 #ifdef HAVE_D2D
196     d2dCleanup();
197 #endif
198     CoUninitialize();
199     return;
200 }
201 
202 
203 /* call back function from Text Window WM_CLOSE */
204 int CALLBACK
ShutDown(void)205 ShutDown(void)
206 {
207     /* First chance for wgnuplot to close help system. */
208     WinCloseHelp();
209     gp_exit(EXIT_SUCCESS);
210     return 0;
211 }
212 
213 
214 /* This function can be used to retrieve version information from
215  * Window's Shell and common control libraries (Comctl32.dll,
216  * Shell32.dll, and Shlwapi.dll) The code was copied from the MSDN
217  * article "Shell and Common Controls Versions" */
218 DWORD
GetDllVersion(LPCTSTR lpszDllName)219 GetDllVersion(LPCTSTR lpszDllName)
220 {
221     HINSTANCE hinstDll;
222     DWORD dwVersion = 0;
223 
224     /* For security purposes, LoadLibrary should be provided with a
225        fully-qualified path to the DLL. The lpszDllName variable should be
226        tested to ensure that it is a fully qualified path before it is used. */
227     hinstDll = LoadLibrary(lpszDllName);
228 
229     if (hinstDll) {
230 	DLLGETVERSIONPROC pDllGetVersion;
231 	pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll, "DllGetVersion");
232 
233 	/* Because some DLLs might not implement this function, you
234 	must test for it explicitly. Depending on the particular
235 	DLL, the lack of a DllGetVersion function can be a useful
236 	indicator of the version. */
237 	if (pDllGetVersion) {
238 	    DLLVERSIONINFO dvi;
239 	    HRESULT hr;
240 
241 	    ZeroMemory(&dvi, sizeof(dvi));
242 	    dvi.cbSize = sizeof(dvi);
243 	    hr = (*pDllGetVersion)(&dvi);
244 	    if (SUCCEEDED(hr))
245 		dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
246 	}
247 	FreeLibrary(hinstDll);
248     }
249     return dwVersion;
250 }
251 
252 
253 BOOL
IsWindowsXPorLater(void)254 IsWindowsXPorLater(void)
255 {
256     OSVERSIONINFO versionInfo;
257 
258     /* get Windows version */
259     ZeroMemory(&versionInfo, sizeof(OSVERSIONINFO));
260     versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
261     GetVersionEx(&versionInfo);
262     return ((versionInfo.dwMajorVersion > 5) ||
263            ((versionInfo.dwMajorVersion == 5) && (versionInfo.dwMinorVersion >= 1)));
264 }
265 
266 
267 char *
appdata_directory(void)268 appdata_directory(void)
269 {
270     HMODULE hShell32;
271     FARPROC pSHGetSpecialFolderPath;
272     static char dir[MAX_PATH] = "";
273 
274     if (dir[0])
275 	return dir;
276 
277     /* FIMXE: "ANSI" Version, no Unicode support */
278 
279     /* Make sure that SHGetSpecialFolderPath is supported. */
280     hShell32 = LoadLibrary(TEXT("shell32.dll"));
281     if (hShell32) {
282 	pSHGetSpecialFolderPath =
283 	    GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
284 	if (pSHGetSpecialFolderPath)
285 	    (*pSHGetSpecialFolderPath)(NULL, dir, CSIDL_APPDATA, FALSE);
286 	FreeModule(hShell32);
287 	return dir;
288     }
289 
290     /* use APPDATA environment variable as fallback */
291     if (dir[0] == NUL) {
292 	char *appdata = getenv("APPDATA");
293 	if (appdata) {
294 	    strcpy(dir, appdata);
295 	    return dir;
296 	}
297     }
298 
299     return NULL;
300 }
301 
302 
303 /* retrieve path relative to gnuplot executable */
304 LPSTR
RelativePathToGnuplot(const char * path)305 RelativePathToGnuplot(const char * path)
306 {
307 #ifdef UNICODE
308     LPSTR ansi_dir = AnsiText(szPackageDir, encoding);
309     LPSTR rel_path = (char *) gp_realloc(ansi_dir, strlen(ansi_dir) + strlen(path) + 1, "RelativePathToGnuplot");
310     if (rel_path == NULL) {
311 	free(ansi_dir);
312 	return (LPSTR) path;
313     }
314 #else
315     char * rel_path = (char * ) gp_alloc(strlen(szPackageDir) + strlen(path) + 1, "RelativePathToGnuplot");
316     strcpy(rel_path, szPackageDir);
317 #endif
318     /* szPackageDir is guaranteed to have a trailing backslash */
319     strcat(rel_path, path);
320     return rel_path;
321 }
322 
323 
324 static void
WinCloseHelp(void)325 WinCloseHelp(void)
326 {
327     /* Due to a known bug in the HTML help system we have to
328      * call this as soon as possible before the end of the program.
329      * See e.g. http://helpware.net/FAR/far_faq.htm#HH_CLOSE_ALL
330      */
331     if (IsWindow(help_window))
332 	SendMessage(help_window, WM_CLOSE, 0, 0);
333     Sleep(0);
334 }
335 
336 
337 static LPTSTR
GetLanguageCode(void)338 GetLanguageCode(void)
339 {
340     static TCHAR lang[6] = TEXT("");
341 
342     if (lang[0] == NUL) {
343 	GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, lang, sizeof(lang));
344 	//strcpy(lang, "JPN"); //TEST
345 	/* language definition files for Japanese already use "ja" as abbreviation */
346 	if (_tcscmp(lang, TEXT("JPN")) == 0)
347 	    lang[1] = 'A';
348 	/* prefer lower case */
349 	lang[0] = tolower((unsigned char)lang[0]);
350 	lang[1] = tolower((unsigned char)lang[1]);
351 	/* only use two character sequence */
352 	lang[2] = NUL;
353     }
354 
355     return lang;
356 }
357 
358 
359 static LPTSTR
LocalisedFile(LPCTSTR name,LPCTSTR ext,LPCTSTR defaultname)360 LocalisedFile(LPCTSTR name, LPCTSTR ext, LPCTSTR defaultname)
361 {
362     LPTSTR lang;
363     LPTSTR filename;
364 
365     /* Allow user to override language detection. */
366     if (szLanguageCode)
367 	lang = szLanguageCode;
368     else
369 	lang = GetLanguageCode();
370 
371     filename = (LPTSTR) malloc((_tcslen(szModuleName) + _tcslen(name) + _tcslen(lang) + _tcslen(ext) + 1) * sizeof(TCHAR));
372     if (filename) {
373 	_tcscpy(filename, szModuleName);
374 	_tcscat(filename, name);
375 	_tcscat(filename, lang);
376 	_tcscat(filename, ext);
377 	if (!PathFileExists(filename)) {
378 	    _tcscpy(filename, szModuleName);
379 	    _tcscat(filename, defaultname);
380 	}
381     }
382     return filename;
383 }
384 
385 
386 static void
ReadMainIni(LPTSTR file,LPTSTR section)387 ReadMainIni(LPTSTR file, LPTSTR section)
388 {
389     TCHAR profile[81] = TEXT("");
390     const TCHAR hlpext[] = TEXT(".chm");
391     const TCHAR name[] = TEXT("wgnuplot-");
392 
393     /* Language code override */
394     GetPrivateProfileString(section, TEXT("Language"), TEXT(""), profile, 80, file);
395     if (profile[0] != NUL)
396 	szLanguageCode = _tcsdup(profile);
397     else
398 	szLanguageCode = NULL;
399 
400     /* help file name */
401     GetPrivateProfileString(section, TEXT("HelpFile"), TEXT(""), profile, 80, file);
402     if (profile[0] != NUL) {
403 	winhelpname = (LPTSTR) malloc((_tcslen(szModuleName) + _tcslen(profile) + 1) * sizeof(TCHAR));
404 	if (winhelpname) {
405 	    _tcscpy(winhelpname, szModuleName);
406 	    _tcscat(winhelpname, profile);
407 	}
408     } else {
409 	/* default name is "wgnuplot-LL.chm" */
410 	winhelpname = LocalisedFile(name, hlpext, TEXT(HELPFILE));
411     }
412 
413     /* menu file name */
414     GetPrivateProfileString(section, TEXT("MenuFile"), TEXT(""), profile, 80, file);
415     if (profile[0] != NUL) {
416 	szMenuName = (LPTSTR) malloc((_tcslen(szModuleName) + _tcslen(profile) + 1) * sizeof(TCHAR));
417 	if (szMenuName) {
418 	    _tcscpy(szMenuName, szModuleName);
419 	    _tcscat(szMenuName, profile);
420 	}
421     } else {
422 	/* default name is "wgnuplot-LL.mnu" */
423 	szMenuName = LocalisedFile(name, TEXT(".mnu"), TEXT("wgnuplot.mnu"));
424     }
425 }
426 
427 
428 #ifndef WGP_CONSOLE
429 int WINAPI
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)430 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
431 #else
432 int
433 main(int argc, char **argv)
434 #endif
435 {
436     LPTSTR tail;
437 #ifdef WGP_CONSOLE
438     HINSTANCE hInstance = GetModuleHandle(NULL), hPrevInstance = NULL;
439 #else
440     int i;
441 #endif
442 
443 #ifndef WGP_CONSOLE
444 # if defined( __MINGW32__) && !defined(_W64)
445 #  define argc _argc
446 #  define argv _argv
447 # else /* MSVC, WATCOM, MINGW-W64 */
448 #  define argc __argc
449 #  define argv __argv
450 # endif
451 #endif /* WGP_CONSOLE */
452 
453     szModuleName = (LPTSTR) malloc((MAXSTR + 1) * sizeof(TCHAR));
454     CheckMemory(szModuleName);
455 
456     /* get path to gnuplot executable  */
457     GetModuleFileName(hInstance, szModuleName, MAXSTR);
458     if ((tail = _tcsrchr(szModuleName, '\\')) != NULL) {
459 	tail++;
460 	*tail = 0;
461     }
462     szModuleName = (LPTSTR) realloc(szModuleName, (_tcslen(szModuleName) + 1) * sizeof(TCHAR));
463     CheckMemory(szModuleName);
464 
465     if (_tcslen(szModuleName) >= 5 && _tcsnicmp(&szModuleName[_tcslen(szModuleName)-5], TEXT("\\bin\\"), 5) == 0) {
466 	size_t len = _tcslen(szModuleName) - 4;
467 	szPackageDir = (LPTSTR) malloc((len + 1) * sizeof(TCHAR));
468 	CheckMemory(szPackageDir);
469 	_tcsncpy(szPackageDir, szModuleName, len);
470 	szPackageDir[len] = NUL;
471     } else {
472 	szPackageDir = szModuleName;
473     }
474 
475 #ifndef WGP_CONSOLE
476     textwin.hInstance = hInstance;
477     textwin.hPrevInstance = hPrevInstance;
478     textwin.nCmdShow = nCmdShow;
479     textwin.Title = L"gnuplot";
480 #endif
481 
482     /* create structure of first graph window */
483     graphwin = (LPGW) calloc(1, sizeof(GW));
484     listgraphs = graphwin;
485 
486     /* locate ini file */
487     {
488 	char * inifile;
489 #ifdef UNICODE
490 	LPWSTR winifile;
491 #endif
492 	get_user_env(); /* this hasn't been called yet */
493 	inifile = gp_strdup("~\\wgnuplot.ini");
494 	gp_expand_tilde(&inifile);
495 
496 	/* if tilde expansion fails use current directory as
497 	    default - that was the previous default behaviour */
498 	if (inifile[0] == '~') {
499 	    free(inifile);
500 	    inifile = "wgnuplot.ini";
501 	}
502 #ifdef UNICODE
503 	graphwin->IniFile = winifile = UnicodeText(inifile, S_ENC_DEFAULT);
504 #else
505 	graphwin->IniFile = inifile;
506 #endif
507 #ifndef WGP_CONSOLE
508 	textwin.IniFile = graphwin->IniFile;
509 #endif
510 	ReadMainIni(graphwin->IniFile, TEXT("WGNUPLOT"));
511     }
512 
513 #ifndef WGP_CONSOLE
514     textwin.IniSection = TEXT("WGNUPLOT");
515     textwin.DragPre = L"load '";
516     textwin.DragPost = L"'\n";
517     textwin.lpmw = &menuwin;
518     textwin.ScreenSize.x = 80;
519     textwin.ScreenSize.y = 80;
520     textwin.KeyBufSize = 2048;
521     textwin.CursorFlag = 1; /* scroll to cursor after \n & \r */
522     textwin.shutdown = MakeProcInstance((FARPROC)ShutDown, hInstance);
523     textwin.AboutText = (LPTSTR) malloc(1024 * sizeof(TCHAR));
524     CheckMemory(textwin.AboutText);
525     wsprintf(textwin.AboutText,
526 	TEXT("Version %hs patchlevel %hs\n") \
527 	TEXT("last modified %hs\n") \
528 	TEXT("%hs\n%hs, %hs and many others\n") \
529 	TEXT("gnuplot home:     http://www.gnuplot.info\n"),
530 	gnuplot_version, gnuplot_patchlevel,
531 	gnuplot_date,
532 	gnuplot_copyright, authors[1], authors[0]);
533     textwin.AboutText = (LPTSTR) realloc(textwin.AboutText, (_tcslen(textwin.AboutText) + 1) * sizeof(TCHAR));
534     CheckMemory(textwin.AboutText);
535 
536     menuwin.szMenuName = szMenuName;
537 #endif
538 
539     pausewin.hInstance = hInstance;
540     pausewin.hPrevInstance = hPrevInstance;
541     pausewin.Title = L"gnuplot pause";
542 
543     graphwin->hInstance = hInstance;
544     graphwin->hPrevInstance = hPrevInstance;
545 #ifdef WGP_CONSOLE
546     graphwin->lptw = NULL;
547 #else
548     graphwin->lptw = &textwin;
549 #endif
550 
551     /* COM Initialization */
552     if (!SUCCEEDED(CoInitialize(NULL))) {
553 	// FIXME: Need to abort
554     }
555 
556     /* init common controls */
557     {
558 	INITCOMMONCONTROLSEX initCtrls;
559 	initCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
560 	initCtrls.dwICC = ICC_WIN95_CLASSES;
561 	InitCommonControlsEx(&initCtrls);
562     }
563 
564 #ifndef WGP_CONSOLE
565     if (TextInit(&textwin))
566 	gp_exit(EXIT_FAILURE);
567     textwin.hIcon = LoadIcon(hInstance, TEXT("TEXTICON"));
568     SetClassLongPtr(textwin.hWndParent, GCLP_HICON, (LONG_PTR)textwin.hIcon);
569 
570     /* Note: we want to know whether this is an interactive session so that we can
571      * decide whether or not to write status information to stderr.  The old test
572      * for this was to see if (argc > 1) but the addition of optional command line
573      * switches broke this.  What we really wanted to know was whether any of the
574      * command line arguments are file names or an explicit in-line "-e command".
575      * (This is a copy of a code snippet from plot.c)
576      */
577     for (i = 1; i < argc; i++) {
578 	if (!_stricmp(argv[i], "/noend"))
579 	    continue;
580 	if ((argv[i][0] != '-') || (argv[i][1] == 'e')) {
581 	    interactive = FALSE;
582 	    break;
583 	}
584     }
585     if (interactive)
586 	ShowWindow(textwin.hWndParent, textwin.nCmdShow);
587     if (IsIconic(textwin.hWndParent)) { /* update icon */
588 	RECT rect;
589 	GetClientRect(textwin.hWndParent, (LPRECT) &rect);
590 	InvalidateRect(textwin.hWndParent, (LPRECT) &rect, 1);
591 	UpdateWindow(textwin.hWndParent);
592     }
593 # ifndef __WATCOMC__
594     /* Finally, also redirect C++ standard output streams. */
595     RedirectOutputStreams(TRUE);
596 # endif
597 #else  /* !WGP_CONSOLE */
598 # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
599 #  define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
600 # endif
601     {
602 	/* Enable Windows 10 Console Virtual Terminal Sequences */
603 	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
604 	DWORD  mode;
605 	GetConsoleMode(handle, &mode);
606 	SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
607     }
608 
609     // set console mode handler to catch "abort" signals
610     SetConsoleCtrlHandler(ConsoleHandler, TRUE);
611 #endif
612 
613     gp_atexit(WinExit);
614 
615     if (!_isatty(_fileno(stdin)))
616 	_setmode(_fileno(stdin), O_BINARY);
617 
618     gnu_main(argc, argv);
619 
620     /* First chance to close help system for console gnuplot,
621 	second for wgnuplot */
622     WinCloseHelp();
623     gp_exit_cleanup();
624     return 0;
625 }
626 
627 
628 void
MultiByteAccumulate(BYTE ch,LPWSTR wstr,int * count)629 MultiByteAccumulate(BYTE ch, LPWSTR wstr, int * count)
630 {
631     static char mbstr[4] = "";
632     static int mbwait = 0;
633     static int mbcount = 0;
634 
635     *count = 0;
636 
637     /* try to re-sync on control characters */
638     /* works for utf8 and sjis */
639     if (ch < 32) {
640 	mbwait = mbcount = 0;
641 	mbstr[0] = NUL;
642     }
643 
644     if (encoding == S_ENC_UTF8) { /* combine UTF8 byte sequences */
645 	if (mbwait == 0) {
646 	    /* first byte */
647 	    mbcount = 0;
648 	    mbstr[mbcount] = ch;
649 	    if ((ch & 0xE0) == 0xC0) {
650 		// expect one more byte
651 		mbwait = 1;
652 	    } else if ((ch & 0xF0) == 0xE0) {
653 		// expect two more bytes
654 		mbwait = 2;
655 	    } else if ((ch & 0xF8) == 0xF0) {
656 		// expect three more bytes
657 		mbwait = 3;
658 	    }
659 	} else {
660 	    /* subsequent byte */
661 	    /*assert((ch & 0xC0) == 0x80);*/
662 	    if ((ch & 0xC0) == 0x80) {
663 		mbcount++;
664 		mbwait--;
665 	    } else {
666 		/* invalid sequence */
667 		mbcount = 0;
668 		mbwait = 0;
669 	    }
670 	    mbstr[mbcount] = ch;
671 	}
672 	if (mbwait == 0) {
673 	    *count = MultiByteToWideChar(CP_UTF8, 0, mbstr, mbcount + 1, wstr, 2);
674 	}
675     } else if (encoding == S_ENC_SJIS) { /* combine S-JIS sequences */
676 	if (mbwait == 0) {
677 	    /* first or single byte */
678 	    mbcount = 0;
679 	    mbstr[mbcount] = ch;
680 	    if (is_sjis_lead_byte(ch)) {
681 		/* first byte */
682 		mbwait = 1;
683 	    }
684 	} else {
685 	    if ((ch >= 0x40) && (ch <= 0xfc)) {
686 		/* valid */
687 		mbcount++;
688 	    } else {
689 		/* invalid */
690 		mbcount = 0;
691 	    }
692 	    mbwait = 0; /* max. double byte sequences */
693 	    mbstr[mbcount] = ch;
694 	}
695 	if (mbwait == 0) {
696 	    *count = MultiByteToWideChar(932, 0, mbstr, mbcount + 1, wstr, 2);
697 	}
698     } else {
699 	mbcount = 0;
700 	mbwait = 0;
701 	mbstr[0] = (char) ch;
702 	*count = MultiByteToWideChar(WinGetCodepage(encoding), 0, mbstr, mbcount + 1, wstr, 2);
703     }
704 }
705 
706 
707 /* replacement stdio routines that use
708  *  -  Text Window for stdin/stdout (wgnuplot)
709  *  -  Unicode console APIs to handle encodings (console gnuplot)
710  * WARNING: Do not write to stdout/stderr with functions not listed
711  * in win/wtext.h
712 */
713 
714 #undef kbhit
715 #undef getche
716 #undef getch
717 #undef putch
718 
719 #undef fgetc
720 #undef getchar
721 #undef getc
722 #undef fgets
723 #undef gets
724 
725 #undef fputc
726 #undef putchar
727 #undef putc
728 #undef fputs
729 #undef puts
730 
731 #undef fprintf
732 #undef printf
733 #undef vprintf
734 #undef vfprintf
735 
736 #undef fwrite
737 #undef fread
738 
739 #ifndef WGP_CONSOLE
740 # define TEXTMESSAGE TextMessage()
741 # define GETCH() TextGetChE(&textwin)
742 # define PUTS(s) TextPutS(&textwin, (char*) s)
743 # define PUTCH(c) TextPutCh(&textwin, (BYTE) c)
744 # define isterm(f) (f==stdin || f==stdout || f==stderr)
745 #else
746 # define TEXTMESSAGE
747 # define GETCH() ConsoleReadCh()
748 # define PUTS(s) ConsolePutS(s)
749 # define PUTCH(c) ConsolePutCh(c)
750 # define isterm(f) _isatty(_fileno(f))
751 #endif
752 
753 int
MyPutCh(int ch)754 MyPutCh(int ch)
755 {
756     return PUTCH(ch);
757 }
758 
759 #ifndef WGP_CONSOLE
760 int
MyKBHit(void)761 MyKBHit(void)
762 {
763     return TextKBHit(&textwin);
764 }
765 
766 int
MyGetCh(void)767 MyGetCh(void)
768 {
769     return TextGetCh(&textwin);
770 }
771 
772 int
MyGetChE(void)773 MyGetChE(void)
774 {
775     return TextGetChE(&textwin);
776 }
777 #endif
778 
779 
780 int
MyFGetC(FILE * file)781 MyFGetC(FILE *file)
782 {
783     if (isterm(file))
784 	return GETCH();
785     return fgetc(file);
786 }
787 
788 char *
MyGetS(char * str)789 MyGetS(char *str)
790 {
791     MyFGetS(str, 80, stdin);
792     if (strlen(str) > 0 && str[strlen(str) - 1] == '\n')
793 	str[strlen(str) - 1] = '\0';
794     return str;
795 }
796 
797 char *
MyFGetS(char * str,unsigned int size,FILE * file)798 MyFGetS(char *str, unsigned int size, FILE *file)
799 {
800     if (isterm(file)) {
801 #ifndef WGP_CONSOLE
802 	char * p = TextGetS(&textwin, str, size);
803 	if (p != NULL)
804 	    return str;
805 	return NULL;
806 #else
807 	unsigned int i;
808 	int c;
809 
810 	c = ConsoleGetch();
811 	if (c == EOF)
812 	    return NULL;
813 
814 	for (i = 1; i < size - 1; i++) {
815 	    c = ConsoleGetch();
816 	    if (str[i] == EOF)
817 		break;
818 	     str[i] = c;
819 	    if (str[i] == '\n')
820 		break;
821 	}
822 	str[i] = NUL;
823 	return str;
824 #endif
825     }
826     return fgets(str,size,file);
827 }
828 
829 int
MyFPutC(int ch,FILE * file)830 MyFPutC(int ch, FILE *file)
831 {
832     if (isterm(file)) {
833 	PUTCH(ch);
834 	TEXTMESSAGE;
835 	return ch;
836     }
837     return fputc(ch,file);
838 }
839 
840 int
MyFPutS(const char * str,FILE * file)841 MyFPutS(const char *str, FILE *file)
842 {
843     if (isterm(file)) {
844 	PUTS(str);
845 	TEXTMESSAGE;
846 	return (*str);
847     }
848     return fputs(str,file);
849 }
850 
851 int
MyPutS(const char * str)852 MyPutS(const char *str)
853 {
854     PUTS(str);
855     PUTCH('\n');
856     TEXTMESSAGE;
857     return 0;
858 }
859 
860 int
MyFPrintF(FILE * file,const char * fmt,...)861 MyFPrintF(FILE *file, const char *fmt, ...)
862 {
863     int count;
864     va_list args;
865 
866     va_start(args, fmt);
867     if (isterm(file)) {
868 	char *buf;
869 
870 	count = vsnprintf(NULL, 0, fmt, args) + 1;
871 	if (count == 0)
872 	    count = MAXPRINTF;
873 	va_end(args);
874 	va_start(args, fmt);
875 	buf = (char *) malloc(count * sizeof(char));
876 	count = vsnprintf(buf, count, fmt, args);
877 	PUTS(buf);
878 	free(buf);
879     } else {
880 	count = vfprintf(file, fmt, args);
881     }
882     va_end(args);
883     return count;
884 }
885 
886 int
MyVFPrintF(FILE * file,const char * fmt,va_list args)887 MyVFPrintF(FILE *file, const char *fmt, va_list args)
888 {
889     int count;
890 
891     if (isterm(file)) {
892 	char *buf;
893 	va_list args_copied;
894 
895 	va_copy(args_copied, args);
896 	count = vsnprintf(NULL, 0U, fmt, args) + 1;
897 	if (count == 0)
898 	    count = MAXPRINTF;
899 	va_end(args_copied);
900 	buf = (char *) malloc(count * sizeof(char));
901 	count = vsnprintf(buf, count, fmt, args);
902 	PUTS(buf);
903 	free(buf);
904     } else {
905 	count = vfprintf(file, fmt, args);
906     }
907     return count;
908 }
909 
910 int
MyPrintF(const char * fmt,...)911 MyPrintF(const char *fmt, ...)
912 {
913     int count;
914     char *buf;
915     va_list args;
916 
917     va_start(args, fmt);
918     count = vsnprintf(NULL, 0, fmt, args) + 1;
919     if (count == 0)
920 	count = MAXPRINTF;
921     va_end(args);
922     va_start(args, fmt);
923     buf = (char *) malloc(count * sizeof(char));
924     count = vsnprintf(buf, count, fmt, args);
925     PUTS(buf);
926     free(buf);
927     va_end(args);
928     return count;
929 }
930 
931 size_t
MyFWrite(const void * ptr,size_t size,size_t n,FILE * file)932 MyFWrite(const void *ptr, size_t size, size_t n, FILE *file)
933 {
934     if (isterm(file)) {
935 	size_t i;
936 	for (i = 0; i < n; i++)
937 	    PUTCH(((BYTE *)ptr)[i]);
938 	TEXTMESSAGE;
939 	return n;
940     }
941     return fwrite(ptr, size, n, file);
942 }
943 
944 size_t
MyFRead(void * ptr,size_t size,size_t n,FILE * file)945 MyFRead(void *ptr, size_t size, size_t n, FILE *file)
946 {
947     if (isterm(file)) {
948 	size_t i;
949 
950 	for (i = 0; i < n; i++)
951 	    ((BYTE *)ptr)[i] = GETCH();
952 	TEXTMESSAGE;
953 	return n;
954     }
955     return fread(ptr, size, n, file);
956 }
957 
958 
959 #ifdef USE_FAKEPIPES
960 
961 static char pipe_type = NUL;
962 static char * pipe_filename = NULL;
963 static char * pipe_command = NULL;
964 
965 FILE *
fake_popen(const char * command,const char * type)966 fake_popen(const char * command, const char * type)
967 {
968     FILE * f = NULL;
969     char tmppath[MAX_PATH];
970     char tmpfile[MAX_PATH];
971     DWORD ret;
972 
973     if (type == NULL)
974 	return NULL;
975 
976     pipe_type = NUL;
977     if (pipe_filename != NULL)
978 	free(pipe_filename);
979 
980     /* Random temp file name in %TEMP% */
981     ret = GetTempPathA(sizeof(tmppath), tmppath);
982     if ((ret == 0) || (ret > sizeof(tmppath)))
983 	return NULL;
984     ret = GetTempFileNameA(tmppath, "gpp", 0, tmpfile);
985     if (ret == 0)
986 	return NULL;
987     pipe_filename = gp_strdup(tmpfile);
988 
989     if (*type == 'r') {
990 	char * cmd;
991 	int rc;
992 	LPWSTR wcmd;
993 	pipe_type = *type;
994 	/* Execute command with redirection of stdout to temporary file. */
995 #ifndef HAVE_BROKEN_WSYSTEM
996 	cmd = (char *) malloc(strlen(command) + strlen(pipe_filename) + 5);
997 	sprintf(cmd, "%s > %s", command, pipe_filename);
998 	wcmd = UnicodeText(cmd, encoding);
999 	rc = _wsystem(wcmd);
1000 	free(wcmd);
1001 #else
1002 	cmd = (char *) malloc(strlen(command) + strlen(pipe_filename) + 15);
1003 	sprintf(cmd, "cmd /c %s > %s", command, pipe_filename);
1004 	rc = system(cmd);
1005 #endif
1006 	free(cmd);
1007 	/* Now open temporary file. */
1008 	/* system() returns 1 if the command could not be executed. */
1009 	if (rc != 1) {
1010 	    f = fopen(pipe_filename, "r");
1011 	} else {
1012 	    remove(pipe_filename);
1013 	    free(pipe_filename);
1014 	    pipe_filename = NULL;
1015 	    errno = EINVAL;
1016 	}
1017     } else if (*type == 'w') {
1018 	pipe_type = *type;
1019 	/* Write output to temporary file and handle the rest in fake_pclose. */
1020 	if (type[1] == 'b')
1021 	    int_error(NO_CARET, "Could not execute pipe '%s'. Writing to binary pipes is not supported.", command);
1022 	else
1023 	    f = fopen(pipe_filename, "w");
1024 	pipe_command = gp_strdup(command);
1025     }
1026 
1027     return f;
1028 }
1029 
1030 
1031 int
fake_pclose(FILE * stream)1032 fake_pclose(FILE *stream)
1033 {
1034     int rc = 0;
1035     if (!stream)
1036 	return ECHILD;
1037 
1038     /* Close temporary file */
1039     fclose(stream);
1040 
1041     /* Finally, execute command with redirected stdin. */
1042     if (pipe_type == 'w') {
1043 	char * cmd;
1044 	LPWSTR wcmd;
1045 
1046 #ifndef HAVE_BROKEN_WSYSTEM
1047 	cmd = (char *) gp_alloc(strlen(pipe_command) + strlen(pipe_filename) + 10, "fake_pclose");
1048 	/* FIXME: this won't work for binary data. We need a proper `cat` replacement. */
1049 	sprintf(cmd, "type %s | %s", pipe_filename, pipe_command);
1050 	wcmd = UnicodeText(cmd, encoding);
1051 	rc = _wsystem(wcmd);
1052 	free(wcmd);
1053 #else
1054 	cmd = (char *) gp_alloc(strlen(pipe_command) + strlen(pipe_filename) + 20, "fake_pclose");
1055 	sprintf(cmd, "cmd/c type %s | %s", pipe_filename, pipe_command);
1056 	rc = system(cmd);
1057 #endif
1058 	free(cmd);
1059     }
1060 
1061     /* Delete temp file again. */
1062     if (pipe_filename) {
1063 	remove(pipe_filename);
1064 	errno = 0;
1065 	free(pipe_filename);
1066 	pipe_filename = NULL;
1067     }
1068 
1069     if (pipe_command) {
1070 	/* system() returns 255 if the command could not be executed.
1071 	    The real popen would have returned an error already. */
1072 	if (rc == 255)
1073 	    int_error(NO_CARET, "Could not execute pipe '%s'.", pipe_command);
1074 	free(pipe_command);
1075     }
1076 
1077     return rc;
1078 }
1079 #endif
1080 
1081 
1082 #ifdef WGP_CONSOLE
1083 
1084 int
ConsoleGetch(void)1085 ConsoleGetch(void)
1086 {
1087     int fd = _fileno(stdin);
1088     HANDLE h;
1089     DWORD waitResult;
1090 
1091     h = (HANDLE)_get_osfhandle(fd);
1092     if (h == INVALID_HANDLE_VALUE)
1093 	fprintf(stderr, "ERROR: Invalid stdin handle value!\n");
1094 
1095     do {
1096 	waitResult = MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, QS_ALLINPUT);
1097 	if (waitResult == WAIT_OBJECT_0) {
1098 	    if (_isatty(fd)) {
1099 		DWORD c = ConsoleReadCh();
1100 		if (c != NUL)
1101 		    return c;
1102 	    } else {
1103 		unsigned char c;
1104 		if (fread(&c, 1, 1, stdin) == 1)
1105 		    return c;
1106 		else
1107 		    return EOF;
1108 	    }
1109 	} else if (waitResult == WAIT_OBJECT_0+1) {
1110 	    WinMessageLoop();
1111 	    if (ctrlc_flag)
1112 		return '\r';
1113 	} else
1114 		break;
1115     } while (1);
1116 
1117     return '\r';
1118 }
1119 
1120 #endif /* WGP_CONSOLE */
1121 
1122 
1123 int
ConsoleReadCh(void)1124 ConsoleReadCh(void)
1125 {
1126     const int max_input = 8;
1127     static char console_input[8];
1128     static int first_input_char = 0;
1129     static int last_input_char = 0;
1130     INPUT_RECORD rec;
1131     DWORD recRead;
1132     HANDLE h;
1133 
1134     if (first_input_char != last_input_char) {
1135 	int c = console_input[first_input_char];
1136 	first_input_char++;
1137 	first_input_char %= max_input;
1138 	return c;
1139     }
1140 
1141     h = GetStdHandle(STD_INPUT_HANDLE);
1142     if (h == NULL)
1143 	return NUL;
1144 
1145     ReadConsoleInputW(h, &rec, 1, &recRead);
1146     /* FIXME: We should handle rec.Event.KeyEvent.wRepeatCount > 1, too. */
1147     if (recRead == 1 && rec.EventType == KEY_EVENT && rec.Event.KeyEvent.bKeyDown &&
1148        (rec.Event.KeyEvent.wVirtualKeyCode < VK_SHIFT ||
1149 	rec.Event.KeyEvent.wVirtualKeyCode > VK_MENU)) {
1150 	    if (rec.Event.KeyEvent.uChar.UnicodeChar) {
1151 		if ((rec.Event.KeyEvent.dwControlKeyState == SHIFT_PRESSED) && (rec.Event.KeyEvent.wVirtualKeyCode == VK_TAB)) {
1152 		    return 034; /* remap Shift-Tab */
1153 		} else {
1154 		    int i, count;
1155 		    char mbchar[8];
1156 		    count = WideCharToMultiByte(WinGetCodepage(encoding), 0,
1157 				&rec.Event.KeyEvent.uChar.UnicodeChar, 1,
1158 				mbchar, sizeof(mbchar),
1159 				NULL, NULL);
1160 		    for (i = 1; i < count; i++) {
1161 			console_input[last_input_char] = mbchar[i];
1162 			last_input_char++;
1163 			last_input_char %= max_input;
1164 		    }
1165 		    return mbchar[0];
1166 		}
1167 	    } else {
1168 		switch (rec.Event.KeyEvent.wVirtualKeyCode) {
1169 		case VK_UP: return 020;
1170 		case VK_DOWN: return 016;
1171 		case VK_LEFT: return 002;
1172 		case VK_RIGHT: return 006;
1173 		case VK_HOME: return 001;
1174 		case VK_END: return 005;
1175 		case VK_DELETE: return 0117;
1176 		}
1177 	    }
1178     }
1179 
1180     /* Error reading event or, key up or, one of the following event records:
1181 	MOUSE_EVENT_RECORD, WINDOW_BUFFER_SIZE_RECORD, MENU_EVENT_RECORD, FOCUS_EVENT_RECORD */
1182     return NUL;
1183 }
1184 
1185 #ifdef WGP_CONSOLE
1186 
1187 static int
ConsolePutS(const char * str)1188 ConsolePutS(const char *str)
1189 {
1190     LPWSTR wstr = UnicodeText(str, encoding);
1191     // Use standard file IO instead of Console API
1192     // to enable word-wrapping on Windows 10 and
1193     // allow for redirection of stdout/stderr.
1194     //HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
1195     //WriteConsoleW(h, wstr, wcslen(wstr), NULL, NULL);
1196     fputws(wstr, stdout);
1197     free(wstr);
1198     return 0;
1199 }
1200 
1201 
1202 static int
ConsolePutCh(int ch)1203 ConsolePutCh(int ch)
1204 {
1205     WCHAR w[4];
1206     int count;
1207 
1208     MultiByteAccumulate(ch, w, &count);
1209     if (count > 0) {
1210 	// Use standard file IO instead of Console API
1211 	// to enable word-wrapping on Windows 10.
1212 	//HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
1213 	//WriteConsoleW(h, w, count, NULL, NULL);
1214 	w[count] = 0;
1215 	fputws(w, stdout);
1216     }
1217     return ch;
1218 }
1219 #endif
1220 
1221 
1222 /* This is called by the system to signal various events.
1223    Note that it is executed in a separate thread.  */
1224 BOOL WINAPI
ConsoleHandler(DWORD dwType)1225 ConsoleHandler(DWORD dwType)
1226 {
1227     switch (dwType) {
1228     case CTRL_CLOSE_EVENT:
1229     case CTRL_LOGOFF_EVENT:
1230     case CTRL_SHUTDOWN_EVENT: {
1231 #ifdef WGP_CONSOLE
1232 	HANDLE h;
1233 	INPUT_RECORD rec;
1234 	DWORD written;
1235 #endif
1236 
1237 	// NOTE: returning from this handler terminates the application.
1238 	// Instead, we signal the main thread to clean up and exit and
1239 	// then idle by sleeping.
1240 #ifndef WGP_CONSOLE
1241 	// close the main window to exit gnuplot
1242 	PostMessage(textwin.hWndParent, WM_CLOSE, 0, 0);
1243 #else
1244 	terminate_flag = TRUE;
1245 	// send ^D to main thread input queue
1246 	h = GetStdHandle(STD_INPUT_HANDLE);
1247 	ZeroMemory(&rec, sizeof(rec));
1248 	rec.EventType = KEY_EVENT;
1249 	rec.Event.KeyEvent.bKeyDown = TRUE;
1250 	rec.Event.KeyEvent.wRepeatCount = 1;
1251 	rec.Event.KeyEvent.uChar.AsciiChar = 004;
1252 	WriteConsoleInput(h, &rec, 1, &written);
1253 #endif
1254 	// give the main thread time to exit
1255 	Sleep(10000);
1256 	return TRUE;
1257     }
1258     default:
1259 	break;
1260     }
1261     return FALSE;
1262 }
1263 
1264 
1265 /* public interface to printer routines : Windows PRN emulation
1266  * (formerly in win.trm)
1267  */
1268 
1269 #define MAX_PRT_LEN 256
1270 static char win_prntmp[MAX_PRT_LEN+1];
1271 
1272 FILE *
open_printer(void)1273 open_printer(void)
1274 {
1275     char *temp;
1276 
1277     if ((temp = getenv("TEMP")) == NULL)
1278 	*win_prntmp = '\0';
1279     else  {
1280 	safe_strncpy(win_prntmp, temp, MAX_PRT_LEN);
1281 	/* stop X's in path being converted by _mktemp */
1282 	for (temp = win_prntmp; *temp != NUL; temp++)
1283 	    *temp = tolower((unsigned char)*temp);
1284 	if ((strlen(win_prntmp) > 0) && (win_prntmp[strlen(win_prntmp) - 1] != '\\'))
1285 	    strcat(win_prntmp, "\\");
1286     }
1287     strncat(win_prntmp, "_gptmp", MAX_PRT_LEN - strlen(win_prntmp));
1288     strncat(win_prntmp, "XXXXXX", MAX_PRT_LEN - strlen(win_prntmp));
1289     _mktemp(win_prntmp);
1290     return fopen(win_prntmp, "wb");
1291 }
1292 
1293 
1294 void
close_printer(FILE * outfile)1295 close_printer(FILE *outfile)
1296 {
1297     LPTSTR fname;
1298     HWND hwnd;
1299     TCHAR title[100];
1300 
1301 #ifdef UNICODE
1302     fname = UnicodeText(win_prntmp, S_ENC_DEFAULT);
1303 #else
1304     fname = win_prntmp;
1305 #endif
1306     fclose(outfile);
1307 
1308 #ifndef WGP_CONSOLE
1309     hwnd = textwin.hWndParent;
1310 #else
1311     hwnd = GetDesktopWindow();
1312 #endif
1313     if (term->name != NULL)
1314 	wsprintf(title, TEXT("gnuplot graph (%hs)"), term->name);
1315     else
1316 	_tcscpy(title, TEXT("gnuplot graph"));
1317     DumpPrinter(hwnd, title, fname);
1318 
1319 #ifdef UNICODE
1320     free(fname);
1321 #endif
1322 }
1323 
1324 
1325 void
screen_dump(void)1326 screen_dump(void)
1327 {
1328     if (term == NULL) {
1329 	int_error(c_token, "");
1330     }
1331     if (strcmp(term->name, "windows") == 0)
1332 	GraphPrint(graphwin);
1333 #ifdef WXWIDGETS
1334     else if (strcmp(term->name, "wxt") == 0)
1335 	wxt_screen_dump();
1336 #endif
1337 #ifdef QTTERM
1338     //else if (strcmp(term->name, "qt") == 0)
1339 #endif
1340     else
1341 	int_error(c_token, "screendump not supported for terminal `%s`", term->name);
1342 }
1343 
1344 
1345 void
win_raise_terminal_window(int id)1346 win_raise_terminal_window(int id)
1347 {
1348     LPGW lpgw = listgraphs;
1349     while ((lpgw != NULL) && (lpgw->Id != id))
1350 	lpgw = lpgw->next;
1351     if (lpgw != NULL) {
1352 	if (IsIconic(lpgw->hWndGraph))
1353 	    ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
1354 	BringWindowToTop(lpgw->hWndGraph);
1355     }
1356 }
1357 
1358 
1359 void
win_raise_terminal_group(void)1360 win_raise_terminal_group(void)
1361 {
1362     LPGW lpgw = listgraphs;
1363     while (lpgw != NULL) {
1364 	if (IsIconic(lpgw->hWndGraph))
1365 	    ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
1366 	BringWindowToTop(lpgw->hWndGraph);
1367 	lpgw = lpgw->next;
1368     }
1369 }
1370 
1371 
1372 void
win_lower_terminal_window(int id)1373 win_lower_terminal_window(int id)
1374 {
1375     LPGW lpgw = listgraphs;
1376     while ((lpgw != NULL) && (lpgw->Id != id))
1377 	lpgw = lpgw->next;
1378     if (lpgw != NULL)
1379 	SetWindowPos(lpgw->hWndGraph, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1380 }
1381 
1382 
1383 void
win_lower_terminal_group(void)1384 win_lower_terminal_group(void)
1385 {
1386     LPGW lpgw = listgraphs;
1387     while (lpgw != NULL) {
1388 	SetWindowPos(lpgw->hWndGraph, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1389 	lpgw = lpgw->next;
1390     }
1391 }
1392 
1393 
1394 /* returns true if there are any graph windows open (win terminal) */
1395 static TBOOLEAN
WinWindowOpened(void)1396 WinWindowOpened(void)
1397 {
1398     LPGW lpgw;
1399 
1400     lpgw = listgraphs;
1401     while (lpgw != NULL) {
1402 	if (GraphHasWindow(lpgw))
1403 	    return TRUE;
1404 	lpgw = lpgw->next;
1405     }
1406     return FALSE;
1407 }
1408 
1409 
1410 /* returns true if there are any graph windows open (wxt/caca/win terminals) */
1411 /* Note: This routine is used to handle "persist". Do not test for qt windows here
1412          since they run in a separate process */
1413 TBOOLEAN
WinAnyWindowOpen(void)1414 WinAnyWindowOpen(void)
1415 {
1416     TBOOLEAN window_opened = WinWindowOpened();
1417 #ifdef WXWIDGETS
1418     window_opened |= wxt_window_opened();
1419 #endif
1420 #ifdef HAVE_LIBCACA
1421     window_opened |= CACA_window_opened();
1422 #endif
1423     return window_opened;
1424 }
1425 
1426 
1427 #ifndef WGP_CONSOLE
1428 void
WinPersistTextClose(void)1429 WinPersistTextClose(void)
1430 {
1431     if (!WinAnyWindowOpen() &&
1432 	(textwin.hWndParent != NULL) && !IsWindowVisible(textwin.hWndParent))
1433 	PostMessage(textwin.hWndParent, WM_CLOSE, 0, 0);
1434 }
1435 #endif
1436 
1437 
1438 void
WinMessageLoop(void)1439 WinMessageLoop(void)
1440 {
1441     MSG msg;
1442 
1443     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1444 	/* HBB 19990505: Petzold says we should check this: */
1445 	if (msg.message == WM_QUIT)
1446 	    return;
1447 	TranslateMessage(&msg);
1448 	DispatchMessage(&msg);
1449     }
1450 }
1451 
1452 
1453 #ifndef WGP_CONSOLE
1454 void
WinOpenConsole(void)1455 WinOpenConsole(void)
1456 {
1457     /* Try to attach to an existing console window. */
1458     if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
1459 	if (GetLastError() != ERROR_ACCESS_DENIED) {
1460 	    /* Open new console if we are are not attached to one already.
1461 	       Note that closing this console window will end wgnuplot, too. */
1462 	    AllocConsole();
1463 	}
1464     }
1465     SetConsoleCtrlHandler(ConsoleHandler, TRUE);
1466 }
1467 #endif
1468 
1469 
1470 void
WinRaiseConsole(void)1471 WinRaiseConsole(void)
1472 {
1473     HWND console = NULL;
1474 #ifndef WGP_CONSOLE
1475     console = textwin.hWndParent;
1476     if (pausewin.bPause && IsWindow(pausewin.hWndPause))
1477 	console = pausewin.hWndPause;
1478 #else
1479     console = GetConsoleWindow();
1480 #endif
1481     if (console != NULL) {
1482 	if (IsIconic(console))
1483 	    ShowWindow(console, SW_SHOWNORMAL);
1484 	BringWindowToTop(console);
1485     }
1486 }
1487 
1488 
1489 /* WinGetCodepage:
1490     Map gnuplot's internal character encoding to Windows codepage codes.
1491 */
1492 UINT
WinGetCodepage(enum set_encoding_id encoding)1493 WinGetCodepage(enum set_encoding_id encoding)
1494 {
1495     UINT codepage;
1496 
1497     /* For a list of code page identifiers see
1498 	http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx
1499     */
1500     switch (encoding) {
1501 	case S_ENC_DEFAULT:    codepage = CP_ACP; break;
1502 	case S_ENC_ISO8859_1:  codepage = 28591; break;
1503 	case S_ENC_ISO8859_2:  codepage = 28592; break;
1504 	case S_ENC_ISO8859_9:  codepage = 28599; break;
1505 	case S_ENC_ISO8859_15: codepage = 28605; break;
1506 	case S_ENC_CP437:      codepage =   437; break;
1507 	case S_ENC_CP850:      codepage =   850; break;
1508 	case S_ENC_CP852:      codepage =   852; break;
1509 	case S_ENC_CP950:      codepage =   950; break;
1510 	case S_ENC_CP1250:     codepage =  1250; break;
1511 	case S_ENC_CP1251:     codepage =  1251; break;
1512 	case S_ENC_CP1252:     codepage =  1252; break;
1513 	case S_ENC_CP1254:     codepage =  1254; break;
1514 	case S_ENC_KOI8_R:     codepage = 20866; break;
1515 	case S_ENC_KOI8_U:     codepage = 21866; break;
1516 	case S_ENC_SJIS:       codepage =   932; break;
1517 	case S_ENC_UTF8:       codepage = CP_UTF8; break;
1518 	default: {
1519 	    /* unknown encoding, fall back to default "ANSI" codepage */
1520 	    codepage = CP_ACP;
1521 	    FPRINTF((stderr, "unknown encoding: %i\n", encoding));
1522 	}
1523     }
1524     return codepage;
1525 }
1526 
1527 
1528 LPWSTR
UnicodeText(LPCSTR str,enum set_encoding_id encoding)1529 UnicodeText(LPCSTR str, enum set_encoding_id encoding)
1530 {
1531     LPWSTR strw = NULL;
1532     UINT codepage = WinGetCodepage(encoding);
1533     int length;
1534 
1535     /* sanity check */
1536     if (str == NULL)
1537 	return NULL;
1538 
1539     /* get length of converted string */
1540     length = MultiByteToWideChar(codepage, 0, str, -1, NULL, 0);
1541     strw = (LPWSTR) malloc(sizeof(WCHAR) * length);
1542 
1543     /* convert string to UTF-16 */
1544     length = MultiByteToWideChar(codepage, 0, str, -1, strw, length);
1545 
1546     return strw;
1547 }
1548 
1549 
1550 LPSTR
AnsiText(LPCWSTR strw,enum set_encoding_id encoding)1551 AnsiText(LPCWSTR strw,  enum set_encoding_id encoding)
1552 {
1553     LPSTR str = NULL;
1554     UINT codepage = WinGetCodepage(encoding);
1555     int length;
1556 
1557     /* get length of converted string */
1558     length = WideCharToMultiByte(codepage, 0, strw, -1, NULL, 0, NULL, 0);
1559     str = (LPSTR) malloc(sizeof(char) * length);
1560 
1561     /* convert string to "Ansi" */
1562     length = WideCharToMultiByte(codepage, 0, strw, -1, str, length, NULL, 0);
1563 
1564     return str;
1565 }
1566 
1567 
1568 FILE *
win_fopen(const char * filename,const char * mode)1569 win_fopen(const char *filename, const char *mode)
1570 {
1571     FILE * file;
1572     LPWSTR wfilename = UnicodeText(filename, encoding);
1573     LPWSTR wmode = UnicodeText(mode, encoding);
1574     file = _wfopen(wfilename, wmode);
1575     free(wfilename);
1576     free(wmode);
1577     return file;
1578 }
1579 
1580 
1581 #ifndef USE_FAKEPIPES
1582 FILE *
win_popen(const char * filename,const char * mode)1583 win_popen(const char *filename, const char *mode)
1584 {
1585     FILE * file;
1586     LPWSTR wfilename = UnicodeText(filename, encoding);
1587     LPWSTR wmode = UnicodeText(mode, encoding);
1588     file = _wpopen(wfilename, wmode);
1589     free(wfilename);
1590     free(wmode);
1591     return file;
1592 }
1593 #endif
1594 
1595 
1596 UINT
GetDPI(void)1597 GetDPI(void)
1598 {
1599     HDC hdc_screen = GetDC(NULL);
1600     if (hdc_screen) {
1601 	UINT dpi = GetDeviceCaps(hdc_screen, LOGPIXELSX);
1602 	ReleaseDC(NULL, hdc_screen);
1603 	return dpi;
1604     } else {
1605 	return 96;
1606     }
1607 }
1608