1 // winsupport.cpp Copyright (C) 2021 Codemist
2
3 // $Id: winsupport.cpp 5728 2021-03-13 14:54:03Z arthurcnorman $
4
5
6 /**************************************************************************
7 * Copyright (C) 2021, Codemist. A C Norman *
8 * *
9 * Redistribution and use in source and binary forms, with or without *
10 * modification, are permitted provided that the following conditions are *
11 * met: *
12 * *
13 * * Redistributions of source code must retain the relevant *
14 * copyright notice, this list of conditions and the following *
15 * disclaimer. *
16 * * Redistributions in binary form must reproduce the above *
17 * copyright notice, this list of conditions and the following *
18 * disclaimer in the documentation and/or other materials provided *
19 * with the distribution. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
25 * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
30 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
31 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
32 * DAMAGE. *
33 *************************************************************************/
34
35 // Code that needs <windows.h>, separated out into this file because
36 // <windows.h> clashes with some of the Cygwin header files that I have
37 // included generally.
38
39 #include "config.h"
40
41 #define LONGEST_LEGAL_FILENAME 1024
42
43 #ifdef WIN32
44
45 #include <cstdio>
46 #include <cstring>
47
48 #include "winsupport.h"
49
win32_stacklimit(uintptr_t & C_stacklimit)50 void win32_stacklimit(uintptr_t &C_stacklimit)
51 { HMODULE h = GetModuleHandle(nullptr); // For current executable
52 if (h != nullptr)
53 { IMAGE_DOS_HEADER *dh = (IMAGE_DOS_HEADER*)h;
54 IMAGE_NT_HEADERS *NTh =
55 (IMAGE_NT_HEADERS*)((BYTE*)dh + dh->e_lfanew);
56 int64_t maxStackSize =
57 (int64_t)NTh->OptionalHeader.SizeOfStackReserve;
58 // If the limit recovered above is under 200K I will pretend it is
59 // just plain wrong and increase it to that. The effect may be that I
60 // end up with an untidy stack overflow but at least I get closer to
61 // using all the space that I have.
62 if (maxStackSize < 200*1024) maxStackSize = 200*1024;
63 // I also assume that any figure over 20 Mbytes is a mess so ignore it
64 if (maxStackSize <= 20*1024*1024)
65 { // I try to give myself 64K spare...
66 C_stacklimit = (uintptr_t)C_stackbase - maxStackSize + 0x10000;
67 }
68 }
69 }
70
getMemorySize()71 size_t getMemorySize()
72 { MEMORYSTATUSEX s;
73 s.dwLength = sizeof(s);
74 GlobalMemoryStatusEx(&s);
75 return s.ullTotalPhys;
76 }
77
windowsGetPid()78 int windowsGetPid()
79 { return _getpid();
80 }
81
82 static char error_name[32];
83
WSAErrName(int i)84 const char *WSAErrName(int i)
85 { switch (i)
86 { default: std::sprintf(error_name,
87 "Socket error %d", i);
88 return error_name;
89 case WSAEINTR: return "WSAEINTR";
90 case WSAEBADF: return "WSAEBADF";
91 case WSAEACCES: return "WSAEACCES";
92 #ifdef WSAEDISCON
93 case WSAEDISCON: return "WSAEDISCON";
94 #endif
95 case WSAEFAULT: return "WSAEFAULT";
96 case WSAEINVAL: return "WSAEINVAL";
97 case WSAEMFILE: return "WSAEMFILE";
98 case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
99 case WSAEINPROGRESS: return "WSAEINPROGRESS";
100 case WSAEALREADY: return "WSAEALREADY";
101 case WSAENOTSOCK: return "WSAENOTSOCK";
102 case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ";
103 case WSAEMSGSIZE: return "WSAEMSGSIZE";
104 case WSAEPROTOTYPE: return "WSAEPROTOTYPE";
105 case WSAENOPROTOOPT: return "WSAENOPROTOOPT";
106 case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT";
107 case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT";
108 case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP";
109 case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT";
110 case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT";
111 case WSAEADDRINUSE: return "WSAEADDRINUSE";
112 case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL";
113 case WSAENETDOWN: return "WSAENETDOWN";
114 case WSAENETUNREACH: return "WSAENETUNREACH";
115 case WSAENETRESET: return "WSAENETRESET";
116 case WSAECONNABORTED: return "WSAECONNABORTED";
117 case WSAECONNRESET: return "WSAECONNRESET";
118 case WSAENOBUFS: return "WSAENOBUFS";
119 case WSAEISCONN: return "WSAEISCONN";
120 case WSAENOTCONN: return "WSAENOTCONN";
121 case WSAESHUTDOWN: return "WSAESHUTDOWN";
122 case WSAETOOMANYREFS: return "WSAETOOMANYREFS";
123 case WSAETIMEDOUT: return "WSAETIMEDOUT";
124 case WSAECONNREFUSED: return "WSAECONNREFUSED";
125 case WSAELOOP: return "WSAELOOP";
126 case WSAENAMETOOLONG: return "WSAENAMETOOLONG";
127 case WSAEHOSTDOWN: return "WSAEHOSTDOWN";
128 case WSAEHOSTUNREACH: return "WSAEHOSTUNREACH";
129 case WSASYSNOTREADY: return "WSASYSNOTREADY";
130 case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED";
131 case WSANOTINITIALISED: return "WSANOTINITIALISED";
132 case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND";
133 case WSATRY_AGAIN: return "WSATRY_AGAIN";
134 case WSANO_RECOVERY: return "WSANO_RECOVERY";
135 case WSANO_DATA: return "WSANO_DATA";
136 }
137 }
138
windowsPrepareSockets()139 int windowsPrepareSockets()
140 {
141 // Under Windows the socket stuff is not automatically active, so some
142 // system calls have to be made at the start of a run. I demand a
143 // Winsock 1.1, and fail if that is not available.
144 WSADATA wsadata;
145 int i = WSAStartup(MAKEWORD(1,1), &wsadata);
146 if (i != 0) return i; /* Failed to start winsock for some reason */;
147 if (LOBYTE(wsadata.wVersion) != 1 ||
148 HIBYTE(wsadata.wVersion) != 1)
149 { WSACleanup();
150 return 1; // Version 1.1 of winsock needed
151 }
152 return 0;
153 }
154
155 HANDLE gnuplot_process = 0;
156 HWND gnuplot_handle = 0;
157 bool gnuplotActive = false;
158
159 static GnuplotClass gnuplotAlive;
160
find_text(HWND h,LPARAM)161 BOOL CALLBACK find_text(HWND h, LPARAM)
162 { char buffer[24];
163 GetClassName(h, buffer, 20);
164 if (std::strcmp(buffer, "wgnuplot_text") != 0) return TRUE;
165 gnuplot_handle = h;
166 return FALSE;
167 }
168
windowsFindGnuplot(const char * command,const char * direction)169 FILE *windowsFindGnuplot(const char *command, const char *direction)
170 { int i = 0;
171 // The following test would trigger if the string "wgnuplot.exe" was present
172 // within the path even if it was not the final component. I think that at
173 // present I will take the view that anybody who finds themselves hurt because
174 // of that has only themselves to blame.
175 if (std::strstr(command, "wgnuplot.exe") != nullptr)
176 { HWND parent = 0;
177 // Win32 would rather I used the following long-winded version, which provides
178 // a pile of generality that is irrelevant here!
179 STARTUPINFO startup;
180 PROCESS_INFORMATION process;
181 char c1[LONGEST_LEGAL_FILENAME];
182 std::clock_t t0, t1;
183 std::memset(&startup, 0, sizeof(STARTUPINFO));
184 startup.cb = sizeof(startup);
185 startup.lpReserved = nullptr;
186 startup.lpDesktop = nullptr;
187 startup.lpTitle = nullptr;
188 startup.dwFlags = STARTF_USESHOWWINDOW;
189 startup.wShowWindow = SW_SHOWMINIMIZED;
190 startup.cbReserved2 = 0;
191 startup.lpReserved2 = nullptr;
192 std::strncpy(c1, command, sizeof(c1));
193 c1[sizeof(c1)-1] = 0;
194 if (!CreateProcess(nullptr, c1, nullptr, nullptr, FALSE,
195 0, nullptr, nullptr, &startup, &process))
196 return nullptr;
197 gnuplot_process = process.hProcess;
198 gnuplotActive = true;
199 gnuplot_handle = 0;
200 t0 = std::clock();
201 for (i=0; i<25; i++) // Give it 5 seconds to appear
202 { parent = FindWindow((LPSTR)"wgnuplot_parent",
203 (LPSTR)"gnuplot");
204 if (parent != 0) break;
205 t0 += CLOCKS_PER_SEC/5;
206 while ((t1 = std::clock()) < t0) ; // a busy-wait here
207 t0 = t1;
208 }
209 if (parent != 0)
210 { for (i=0; i<10; i++) // 2 more seconds for the child
211 { EnumChildWindows(parent, find_text, 0);
212 if (gnuplot_handle != 0) break;
213 t0 += CLOCKS_PER_SEC/5;
214 while ((t1 = std::clock()) < t0) ; // busy-wait
215 t0 = t1;
216 }
217 }
218 // special handle for the gnuplot pipe
219 return reinterpret_cast<std::FILE *>(-1);
220 }
221 return _popen(command, direction);
222 }
223
224 #ifdef HAVE_PUTC_UNLOCKED
225 #define PUTC(x, y) putc_unlocked((x), (y))
226 #else
227 #ifdef HAVE__PUTC_NOLOCK
228 #define PUTC(x, y) _putc_nolock((x), (y))
229 #else
230 #define PUTC(x, y) putc((x), (y))
231 #endif
232 #endif
233
234
my_pipe_putc(int c,std::FILE * f)235 int my_pipe_putc(int c, std::FILE *f)
236 { if (f == (std::FILE *)(-1))
237 { if (gnuplot_handle == 0) return EOF;
238 if (c == '\n') c = '\r';
239 SendMessage(gnuplot_handle, WM_CHAR, c, 1L);
240 return c;
241 }
242 else return PUTC(c, f);
243 }
244
my_pipe_flush(std::FILE * f)245 int my_pipe_flush(std::FILE *f)
246 {
247 if (f == (std::FILE *)(-1)) return 0;
248 return std::fflush(f);
249 }
250
my_pclose(std::FILE * stream)251 void my_pclose(std::FILE *stream)
252 {
253 if (stream == (std::FILE *)(-1))
254 { SendMessage(gnuplot_handle, WM_CHAR, 'q', 1L);
255 SendMessage(gnuplot_handle, WM_CHAR, 'u', 1L);
256 SendMessage(gnuplot_handle, WM_CHAR, 'i', 1L);
257 SendMessage(gnuplot_handle, WM_CHAR, 't', 1L);
258 SendMessage(gnuplot_handle, WM_CHAR, '\r', 1L);
259 return;
260 }
261 _pclose(stream);
262 }
263
read_clock()264 uint64_t read_clock()
265 { FILETIME t0, t1, t2, t3;
266 if (GetProcessTimes(GetCurrentProcess(), &t0, &t1, &t2, &t3) == 0)
267 return 0;
268 // The 4 times are: CreationTime, ExitTime, KernelTime, UserTime
269 ULARGE_INTEGER ul;
270 // Times are returned in FILETIME format so I need to convert to an arithmetic
271 // type that I can use.
272 ul.LowPart = t3.dwLowDateTime;
273 ul.HighPart = t3.dwHighDateTime;
274 uint64_t n = ul.QuadPart;
275 // Times are returned in units of 100ns, so I divide by 10 to get
276 // microseconds.
277 return n/10;
278 }
279
windowsFindGnuplot1(char * name)280 int windowsFindGnuplot1(char *name)
281 { HKEY keyhandle;
282 // I need to use RegQueryValueEx here rather than RegGetValue if I am to
283 // support 32-bit Windows XP. Note that buffer overflow in the path to
284 // gnuplot could leave an unterminated string, but that should not happen
285 // here.
286 DWORD length = LONGEST_LEGAL_FILENAME, type;
287 LONG r = RegOpenKeyEx(
288 HKEY_LOCAL_MACHINE,
289 "Software\\Microsoft\\Windows\\CurrentVersion\\"
290 "App Paths\\gnuplot.exe",
291 0,
292 KEY_QUERY_VALUE,
293 &keyhandle);
294 if (r == ERROR_SUCCESS)
295 { r = RegQueryValueEx(keyhandle,
296 nullptr,
297 nullptr,
298 (LPDWORD)&type,
299 (LPBYTE)name,
300 (LPDWORD)&length);
301 name[LONGEST_LEGAL_FILENAME-1] = 0;
302 if (r == ERROR_SUCCESS) return 1;
303 }
304 return 0;
305 }
306
windowsGetTempPath(size_t n,char * s)307 size_t windowsGetTempPath(size_t n, char *s)
308 { return GetTempPath(n, s);
309 }
310
311 // On Windows I can query the page that the address is within, and accept
312 // it if there is read/write access and if it is not a guard page.
313
valid_address(void * pointer)314 bool valid_address(void *pointer)
315 { MEMORY_BASIC_INFORMATION mbi = {0};
316 if (::VirtualQuery(pointer, &mbi, sizeof(mbi)))
317 { // check the page is not a guard page
318 if (mbi.Protect & (PAGE_GUARD|PAGE_NOACCESS)) return false;
319 return ((mbi.Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) != 0);
320 }
321 return false; // ::VirtualQuery failed.
322 }
323
324 #elif defined __CYGWIN__
325
326 // The aim here is to avoid use of the Microsoft versions of printf and
327 // friends and (hence) allow g++ to parse and check format strings reliably.
328 #define __USE_MINGW_ANSI_STDIO 1
329
330 #include <winsock.h>
331 #include <process.h>
332 #include <windows.h>
333
334 #include <cstring>
335 #include <cstdio>
336
337 #include "winsupport.h"
338
windowsFindGnuplot2(char * name)339 int windowsFindGnuplot2(char *name)
340 { HKEY keyhandle;
341 // I need to use RegQueryValueEx here rather than RegGetValue if I am to
342 // support 32-bit Windows XP. Note that buffer overflow in the path to
343 // gnuplot could leave an unterminated string, but that should not happen
344 // here.
345 DWORD length = LONGEST_LEGAL_FILENAME, type;
346 int ll, i;
347 LONG r = RegOpenKeyEx(
348 HKEY_LOCAL_MACHINE,
349 "Software\\Microsoft\\Windows\\CurrentVersion\\"
350 "App Paths\\wgnuplot.exe",
351 0,
352 KEY_QUERY_VALUE,
353 &keyhandle);
354 if (r == ERROR_SUCCESS)
355 { r = RegQueryValueEx(keyhandle,
356 nullptr,
357 nullptr,
358 (LPDWORD)&type,
359 (LPBYTE)name,
360 (LPDWORD)&length);
361 name[LONGEST_LEGAL_FILENAME-1] = 0;
362 if (r == ERROR_SUCCESS)
363 {
364 // Now I have a further delight. The path I have just identified is a
365 // Windows one, but I need to convert it into a Cygwin-style one. It
366 // should start "x:\" with a drive name so I map that onto "/cygdrive/x/"
367 // and convert every "\" to a "/". The code here is rather grotty with the
368 // numeric "magic offsets" and the use of sprintf followed by patching
369 // up after the terminating null from that, but it is at least concise.
370 ll = std::strlen(name);
371 for (i=ll; i>=0; i--)
372 name[i+9] = name[i]=='\\' ? '/' : name[i];
373 std::sprintf(name, "/cygdrive/%c", name[0]);
374 name[11] = '/';
375 return 1;
376 }
377 }
378 return 0;
379 }
380
windowsGetTempPath(size_t n,char * s)381 size_t windowsGetTempPath(size_t n, char *s)
382 { return GetTempPath(n, s);
383 }
384
385 #endif // Windows & Cygwin
386
387 // end of winsupport.cpp
388