1 // stub.cpp Copyright A C Norman 2014
2 //
3
4 // The object of this code is to have several binaries embedded in it and
5 // it picks one, copies it to disc on a temporary file and from there
6 // it can execute it. It decides which to use based on whether it finds
7 // that the system being run on is 64-bit capable and whether it ought to
8 // use a cygwin version to interact with a cygwin console using unix-style
9 // calls to set raw mode and ncurses to move around the screen. It also
10 // takes a view as to when it should use a cygwin version so that although
11 // it is running on windows it can use an X11 display: it may do that
12 // if it finds the environment variable DISPLAY set or it discovers that it
13 // is being run on a windows host that is being accessed over ssh. When these
14 // special cases do not apply it will run a native windows version,
15 //
16 // An accompanying program "addresource.c" is used to add extra bodies
17 // of code to a compiled version of this.
18
19
20 /**************************************************************************
21 * Copyright (C) 2014, Codemist Ltd. A C Norman *
22 * *
23 * Redistribution and use in source and binary forms, with or without *
24 * modification, are permitted provided that the following conditions are *
25 * met: *
26 * *
27 * * Redistributions of source code must retain the relevant *
28 * copyright notice, this list of conditions and the following *
29 * disclaimer. *
30 * * Redistributions in binary form must reproduce the above *
31 * copyright notice, this list of conditions and the following *
32 * disclaimer in the documentation and/or other materials provided *
33 * with the distribution. *
34 * *
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
36 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
38 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
39 * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
40 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
41 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
42 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
44 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
45 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
46 * DAMAGE. *
47 *************************************************************************/
48
49 // $Id: stub.cpp 5555 2020-12-30 22:11:56Z arthurcnorman $
50
51
52 // This should be compiled with one of the following symbols predefined:
53 // FAT32 isatty32 w32 cyg32
54 // FAT64 isatty32 isatty64 w32 w64 cyg32 cyg64
55 // FATGUIDEMO as FAT64 for now, but should only launch in gui mode
56 // FATWIN win32 win64
57
58
59 #include <cstdio>
60 #include <cstdint>
61 #include <cinttypes>
62 #include <cstring>
63 #include <windows.h>
64 #include <tchar.h>
65 #include <io.h>
66 #include <cstdlib>
67 #include <ctime>
68 #include <cassert>
69 #include <zlib.h>
70
71
72 //
73 // The compression parts of this code are modelled on.......
74 //
75 // zpipe.c: example of proper use of zlib's inflate() and deflate()
76 // Not copyrighted -- provided to the public domain
77 // Version 1.4 11 December 2005 Mark Adler
78 //
79
80 // Using a large buffer for decompression is expected to speed things
81 // up, so here I use 256 Kbytes.
82
83 #define CHUNK 0x40000
84
85 //
86 // Decompress from file source to file dest until stream ends or EOF.
87 // inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
88 // allocated for processing, Z_DATA_ERROR if the deflate data is
89 // invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
90 // the version of the library linked do not match, or Z_ERRNO if there
91 // is an error reading or writing the files.
92 //
93
inf(std::FILE * source,std::FILE * dest,int length)94 int inf(std::FILE *source, std::FILE *dest, int length)
95 { int ret;
96 unsigned have;
97 z_stream strm;
98 unsigned char in[CHUNK];
99 unsigned char out[CHUNK];
100
101 // allocate inflate state
102 strm.zalloc = Z_NULL;
103 strm.zfree = Z_NULL;
104 strm.opaque = Z_NULL;
105 strm.avail_in = 0;
106 strm.next_in = Z_NULL;
107 ret = inflateInit(&strm);
108 if (ret != Z_OK)
109 return ret;
110
111 // decompress until deflate stream ends or end of file or given
112 // number of bytes have been written.
113 do
114 { strm.avail_in = std::fread(in, 1, CHUNK, source);
115 if (std::ferror(source))
116 { static_cast<void>(inflateEnd(&strm));
117 return Z_ERRNO;
118 }
119 if (strm.avail_in == 0) break;
120 strm.next_in = in;
121
122 // run inflate() on input until output buffer not full
123 do
124 { strm.avail_out = CHUNK;
125 strm.next_out = out;
126 ret = inflate(&strm, Z_NO_FLUSH);
127 assert(ret != Z_STREAM_ERROR); // state not clobbered
128 switch (ret)
129 { case Z_NEED_DICT:
130 ret = Z_DATA_ERROR; // and fall through
131 case Z_DATA_ERROR:
132 case Z_MEM_ERROR:
133 static_cast<void>(inflateEnd(&strm));
134 return ret;
135 }
136 have = CHUNK - strm.avail_out;
137 if (have > length) have = length;
138 if (std::fwrite(out, 1, have, dest) != have || std::ferror(dest))
139 { static_cast<void>(inflateEnd(&strm));
140 return Z_ERRNO;
141 }
142 length -= have;
143 if (length == 0) break;
144 }
145 while (strm.avail_out == 0);
146
147 // done when inflate() says it's done
148 }
149 while ((length != 0) && (ret != Z_STREAM_END));
150
151 // clean up and return
152 static_cast<void>(inflateEnd(&strm));
153 return ret == (Z_STREAM_END && length == 0) ? Z_OK : Z_DATA_ERROR;
154 }
155
156
157 #if defined FAT32
158
159 // A fat 32 bit binary can be run under either native windows or
160 // 32-bit cygwin. Since it is a linked as a console mode application
161 // it is not suitable for launching by double-clicking. This is
162 // going to be usefully smaller than the FAT64 binary, but whether the
163 // space saving makes the extra complication of having this one available
164 // is yet to be determined!
165
166 #define NUMBER_OF_MODULES 3
167 #define MODULE_ISATTY32 0
168 #define MODULE_WIN32 1
169 #define MODULE_CYG32 2
170
171 #elif defined FAT64 || defined FATGUIDEMO
172
173 // A fat 64 bit binary is the full works, supporting either 32 or 64-bit
174 // usage either under native windows or either 32 or 64-bit cygwin, To
175 // cope with all of this makes if the biggest of all the binaries established
176 // here.
177
178 #define NUMBER_OF_MODULES 6
179 #define MODULE_ISATTY32 0
180 #define MODULE_ISATTY64 1
181 #define MODULE_WIN32 2
182 #define MODULE_WIN64 3
183 #define MODULE_CYG32 4
184 #define MODULE_CYG64 5
185
186 #elif defined FATWIN
187
188 // A fat windows binary is linked as a windows application and as such
189 // is not useful for launching from a console - but it is good for
190 // double-clicking on. The version here will take advantage of 64-bit
191 // windows if run in that context.
192
193 #define NUMBER_OF_MODULES 2
194 #define MODULE_WIN32 0
195 #define MODULE_WIN64 1
196
197 #else
198
199 #error Unknown version of stub code
200
201 #endif
202
203 #ifndef FAT32
204 // FAT32 only support 32-bit versions (windows or cygwin) so does
205 // not need this.
206
207 // The next function is as shown at
208 // msdn.microsoft.com/en-us/library/windows/desktop/ms684139%28v=vs.85%29.aspx
209 // as the proper way to detect when the current 32-bit program is in fact
210 // running under Wow64 (ie with a 64-bit version of Windows beneath its feet).
211
212 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
213
214 LPFN_ISWOW64PROCESS fnIsWow64Process;
215
IsWow64()216 BOOL IsWow64()
217 { BOOL bIsWow64 = FALSE;
218 // IsWow64Process is not available on all supported versions of Windows.
219 // Use GetModuleHandle to get a handle to the DLL that contains the function
220 // and GetProcAddress to get a pointer to the function if available. Well
221 // probably these days it is always available on operating systems that matter,
222 // but the hack here is fairly simple and local so to be kind to the
223 // historical world I will preserve it for a while!
224 fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
225 GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
226 if(nullptr != fnIsWow64Process)
227 { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
228 { //handle error - well heer I just return "no"
229 return FALSE;
230 }
231 }
232 return bIsWow64;
233 }
234
235 #endif // FAT32
236
read8(std::FILE * f)237 static int64_t read8(std::FILE *f)
238 { int64_t r = 0;
239 int i;
240 for (i=0; i<8; i++)
241 { int w = std::getc(f) & 0xff;
242 r |= ((int64_t)w) << (8*i);
243 }
244 return r;
245 }
246
247 static char pPath[MAX_PATH];
248 static int64_t address[NUMBER_OF_MODULES];
249 static int64_t length[NUMBER_OF_MODULES];
250
251 //
252 // Run the program stored with this code and kept as a resource with
253 // the specified index.
254 //
255
256 #define ERROR_UNABLE_TO_OPEN_SELF 81
257 #define ERROR_BAD_LEADER 82
258 #define ERROR_BAD_TRAILER 83
259 #define ERROR_NO_MEMORY 84
260 #define ERROR_PROCESS_INFO 85
261 #define ERROR_CREATEPROCESS 86
262 #define ERROR_UNABLE_TO_WRITE 87
263 #define ERROR_NO_TEMP_FILE 88
264
RunResource(int index,int forcegui,const char * modulename)265 int RunResource(int index, int forcegui, const char *modulename)
266 { std::FILE *src, *dest;
267 int i;
268 uint64_t hdr;
269 #ifdef DEBUG
270 std::printf("RunResource %s: %d %d\n", modulename, index, forcegui);
271 std::fflush(stdout);
272 #endif
273 GetModuleFileName(nullptr, pPath, sizeof(pPath));
274 #ifdef DEBUG
275 std::printf("my name is %s\n", pPath);
276 std::fflush(stdout);
277 #endif
278 src = std::fopen(pPath, "rb");
279 if (src == nullptr) return ERROR_UNABLE_TO_OPEN_SELF;
280 std::fseek(src, -16*(NUMBER_OF_MODULES+1), SEEK_END);
281 // The way I put "resources" in a file puts a header word before
282 // a table and a trailer word after it. These form some sort of
283 // signature to confirm that the file is in a good state. If either
284 // is not found I exit.
285 if ((hdr = read8(src)) != 0x1234567887654321LL)
286 return ERROR_BAD_LEADER;
287 for (i=0; i<NUMBER_OF_MODULES; i++)
288 { address[i] = read8(src);
289 length[i] = read8(src);
290 #ifdef DEBUG_SHOW_MODULES
291 std::printf("Module %d at %" PRIx64 " has length %"
292 PRId64 " = %#" PRIx64 "\n",
293 i, address[i], length[i], length[i]);
294 std::fflush(stdout);
295 #endif
296 }
297 if ((hdr = read8(src)) != 0x8765432112345678LL)
298 return ERROR_BAD_TRAILER;
299 // Now I want a temporary file that I can put the unpacked executable
300 // into. Note that GetTempPath() will not guarantee that the directory
301 // it points me to is writable! In case of trouble I fall back to creating
302 // the temporary files that I need in whatever is the current directory.
303 //
304 // One thing that concerns me here is the possibility that some antivirus
305 // programs might object to attempts to launch executables out of a
306 // temporary directory... sufficiently agressive behaviour of that kind
307 // could cause real misery to me here. I have McAfee running on my
308 // Windows machine and seem to be OK...
309 //
310 DWORD path = GetTempPath(sizeof(pPath), pPath);
311 if (path ==0 || path > sizeof(pPath)-14)
312 std::strcpy(pPath, ".\\"); // Try to use currect directory
313 // Now pPath holds a directory, with a "\" character at the end...
314 //
315 // I can not use most of the functions I would like to to create the
316 // file name because most do not allow me to specify a suffix, and I
317 // would like the name to be "something.exe". So I do it all myself.
318 DWORD myid = GetProcessId(GetCurrentProcess());
319 SYSTEMTIME t0;
320 GetSystemTime(&t0);
321 // Here I create a number that depends on the date and time of day as
322 // well as on my process number. Using this in the generated file name
323 // can not guarantee to avoid clashes. but it will at least help.
324 int k = (t0.wMilliseconds + 1000*t0.wSecond) +
325 314159*static_cast<int>(std)::time(nullptr) +
326 2718281*static_cast<int>(myid);
327 char *fname = pPath + std::strlen(pPath);
328 // I will try ten times in the temporary directory found above, and if
329 // all those attempts fail I will try another 10 times in the current
330 // directory. If all those fail I will give up.
331 int tries = 0;
332 for (;; k=69069*k+1, tries++)
333 { if (tries == 10)
334 { std::strcpy(pPath, ".\\");
335 fname = pPath + std::strlen(pPath);
336 }
337 else if (tries == 20) return ERROR_NO_TEMP_FILE;
338 // The file-name I create is in the form "Txxxxxxx.exe" with 7 hex digits
339 // in place of the "x" characters. If my first attempt fails I will
340 // make my next probe increases the hex constant in the spirit of a 32-bit
341 // linear congruential pseudo-random sequence in the expectation that that
342 // would tend to reduce clustering. That really ought not to matter!
343 std::sprintf(fname, "T%.7x.exe", k & 0xfffffff);
344 // This use of CreateFile arranges that the file opened is guaranteed
345 // to be new. This is just what I want.
346 HANDLE h = CreateFile(
347 pPath, // name
348 GENERIC_WRITE, // access
349 0, // shared
350 nullptr, // security attributes
351 CREATE_NEW, // creation disposition
352 FILE_ATTRIBUTE_NORMAL, // flags & attributes
353 nullptr); // template file
354 if (h == INVALID_HANDLE_VALUE) continue;
355 // I want to write to the file using a C style FILE object so I convert
356 // from a Windows handle to one of those - in two steps.
357 int ch = _open_osfhandle((intptr_t)h, 0);
358 if (ch == -1)
359 { CloseHandle(h);
360 DeleteFile(pPath);
361 continue;
362 }
363 dest = fdopen(ch, "wb");
364 if (dest == nullptr)
365 { close(ch);
366 DeleteFile(pPath);
367 continue;
368 }
369 break;
370 }
371 #ifdef DEBUG
372 std::printf("File will be <%s>\n", pPath);
373 #endif
374 std::fseek(src, address[index], SEEK_SET);
375 // Decompress the relevant resource into the new file.
376 inf(src, dest, length[index]);
377 std::fclose(src);
378 std::fclose(dest);
379 chmod(pPath, 0755); // Make executable
380
381 const char *cmd = GetCommandLine();
382 char *cmd1 =
383 new (std::nothrow) char[std::strlen(cmd) + 12];
384 if (cmd1 == nullptr)
385 { std::printf("No memory for new command line\n");
386 std::fflush(stdout);
387 DeleteFile(pPath);
388 return ERROR_NO_MEMORY;
389 }
390 std::strcpy(cmd1, cmd);
391 //
392 // Now a rather horrible mess. I will have several versions of Reduce
393 // created here
394 // reduce.exe the general one
395 // reduce32.exe only supports 32-bit systems
396 // winreduce.exe for double-clicking - does not support cygwin
397 // [winreduce32.exe] double-clickable and 32-bit only
398 // The last of these does not go through this packing process.
399 // To cope with all of those the code inside Reduce will strin "win" from
400 // the front of an application and "32" from the end before using it
401 // to decide where to look for an image file. That way all the above
402 // will be able to use a single file "reduce.img". Further it will be
403 // essential that this name be picked up from argv[0] not from the
404 // name of the file that the application was actually launches from since
405 // the latter is the weird temporary file I just created.
406 // [Hmm - the naming conventions here need review...]
407 //
408 if (forcegui) std::strcat(cmd1, " --gui");
409
410 STARTUPINFO peStartUpInformation;
411 PROCESS_INFORMATION peProcessInformation;
412 std::memset(&peStartUpInformation, 0, sizeof(STARTUPINFO));
413 peStartUpInformation.cb = sizeof(STARTUPINFO);
414 std::memset(&peProcessInformation, 0, sizeof(PROCESS_INFORMATION));
415 #ifdef DEBUG
416 std::printf("Launch <%s> cmd line <%s>\n", pPath, cmd1);
417 std::fflush(stdout);
418 #endif
419 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
420 // _set_abort_behavior(0,_WRITE_ABORT_MSG | _CALL_REPORTFAULT);
421 if (CreateProcessA(pPath, // appname
422 cmd1, // command line
423 nullptr, // process attributes
424 nullptr, // thread attributes
425 1, // inherits handles
426 0, // allow it to run now
427 nullptr, // environment
428 nullptr, // current directory
429 &peStartUpInformation,
430 &peProcessInformation))
431 { WaitForSingleObject(peProcessInformation.hProcess, INFINITE);
432 DWORD rc;
433 if (GetExitCodeProcess(peProcessInformation.hProcess, &rc) == 0)
434 rc = ERROR_PROCESS_INFO; // Getting the return code failed!
435 #ifdef DEBUG
436 std::printf("CreateProcess happened, rc reported as %d = %#x\n", rc,
437 rc);
438 #endif
439 std::fflush(stdout);
440 CloseHandle(peProcessInformation.hProcess);
441 CloseHandle(peProcessInformation.hThread);
442 DeleteFile(pPath);
443 return rc;
444 }
445 else
446 {
447 #ifdef DEBUG
448 std::printf("CreateProcess failed\n"); std::fflush(stdout);
449 DWORD dw = GetLastError();
450 LPVOID lpMsgBuf;
451 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
452 FORMAT_MESSAGE_FROM_SYSTEM |
453 FORMAT_MESSAGE_IGNORE_INSERTS,
454 nullptr,
455 dw,
456 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
457 (LPSTR)&lpMsgBuf,
458 0,
459 nullptr);
460 std::printf("CreateProcess failed (%d): %s\n", dw, lpMsgBuf);
461 std::fflush(stdout);
462 #endif
463 DeleteFile(pPath);
464 return ERROR_CREATEPROCESS;
465 }
466 }
467
468 static const char *dll32[] =
469 {
470 #include "dll32.cpp"
471 nullptr
472 };
473
474 static const char *dll64[] =
475 {
476 #include "dll64.cpp"
477 nullptr
478 };
479
480 #include <windows.h>
481 #include <cstdio>
482
dllcheck(const char ** table)483 void dllcheck(const char **table)
484 { int i, messaged = 0;
485 for (i=0; table[i]!=nullptr; i++)
486 { HMODULE h = LoadLibraryEx(table[i], nullptr,
487 DONT_RESOLVE_DLL_REFERENCES);
488 if (h == nullptr)
489 { if (!messaged)
490 { std::printf("\nCygwin needs at least %s", table[i]);
491 messaged = 3;
492 }
493 else if (messaged >= 5)
494 { std::printf(",\n %s", table[1]);
495 messaged = 1;
496 }
497 else
498 { std::printf(", %s", table[i]);
499 messaged++;
500 }
501 }
502 else FreeLibrary(h);
503 }
504 if (messaged)
505 { std::printf("\n");
506 std::printf("Please run cygwin setup and install packages that will\n");
507 std::printf("provide these. Then try again.\n\n");
508 std::fflush(stdout);
509 }
510 }
511
main(int argc,char * argv[])512 int main(int argc, char* argv[])
513 {
514 //
515 // The logic here starts by collecting information as to whether I am being
516 // run in a context where things have access to a console.
517 // This will NOT be the case if the code is run from mintty (the current
518 // cygwin terminal), a cygwin X terminal or via ssh into a cygwin version
519 // of sshd. However if either input or output is connected to a disc file
520 // I will act the same way that I would have if there had been direct
521 // contact to a Windows console since in that case the run is (probably)
522 // non-interactive and so terminal handling is not an issue.
523 // Furthermore the command-line option "--", (which is used here to redirect
524 // the standard output) is detected and is similarly viewed as a signature
525 // of non-interactive use.
526 //
527 // What this is really about is deciding if there is a risk that I need to
528 // use the Cygwin console API.
529 //
530 // There are times in this code where I may try to launch other programs
531 // in ways that could potentially pop up unwanted and ugly error boxes. I
532 // try to disable that here.
533 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
534
535 int possibly_under_cygwin = 1, rc;
536 //
537 // I will start by detecting a number of cases where I will NOT want to
538 // use the cygwin version of the code. So
539 // (a) If I am attached to a regular Windows console then I am certainly
540 // not under a cygwin mintty style console (or linked in over ssh),
541 // and I am very liable to have a Windows display available. So there is
542 // no point in using cygwin even if it is available.
543 // (b) If the user has put "-- file" on the command line then that redirects
544 // the output to a file. In that case again there is no point in trying
545 // to use cygwin console management and I feel happier defaulting to
546 // use of the native windows version.
547 // (c) If either stdin or stdout is attached to a "disk" file then as in case
548 // (b) I am not interactive enough to need cygwin console handling. Note
549 // that if I was under mintty (etc) stdin and stdout would appear to
550 // be attached to pipes rather than to a disk file.
551 //
552 // However if a command line flag "--cygwin" is provided I will make that
553 // force use of Cygwin. That use-case is required for people to use the
554 // "cuba" package on Windows because the Cuba-4.1 library is not available
555 // for and will not build for native Windows. If "--cygwin" is specified
556 // the code will only even attempt to run a GUI if DISPLAY is set, and in
557 // that case it will expect an X-windows server to be available for use.
558 CONSOLE_SCREEN_BUFFER_INFO csbi;
559 HANDLE h0 = GetStdHandle(STD_INPUT_HANDLE);
560 HANDLE h1 = GetStdHandle(STD_OUTPUT_HANDLE);
561 int t0 = GetFileType(h0),
562 t1 = GetFileType(h1);
563 int gcsbi = GetConsoleScreenBufferInfo(h1, &csbi);
564 int force_cygwin = 0, dashdash = 0, i;
565 for (i=1; i<argc; i++)
566 { if (std::strcmp(argv[i], "--") == 0) dashdash = 1;
567 else if (std::strcmp(argv[i], "--cygwin") == 0) force_cygwin = 1;
568 }
569 if (!force_cygwin &&
570 (gcsbi || // console available: can use Windows API
571 dashdash || // "--" seen: no console API needed
572 t0==FILE_TYPE_DISK || // stdin or stdout disk: no console API needed
573 t1==FILE_TYPE_DISK)) possibly_under_cygwin = 0;
574 //
575 // This stub runs as 32-bit code, but if it is on a 64-bit version of
576 // windows it will be running under "wow64". Checking for that should allow
577 // me to know if there is any prospect of running a 64-bit version of the
578 // application - and if I can then I should!
579 //
580 #ifdef FAT32
581 // I find it easier to have these variables present even when they are
582 // then not used. I rather expect that the C compiler will optimise away
583 // any potential code clumsiness.
584 int wow64 = 0;
585 #else // FAT32
586 int wow64 = IsWow64();
587
588 #ifdef DEBUG
589 // While debugging this code a "--32" on the command line disables 64 bit use.
590 // The only need for that that I can really see is when checking the code
591 // to verify that it works. But if somebody found a situation where it was
592 // valuable I could reinstate and publicise it.
593 for (i=1; i<argc; i++)
594 { if (std::strcmp(argv[i], "--32") == 0)
595 { wow64 = 0;
596 }
597 }
598 #endif // DEBUG
599 #endif // FAT32
600
601 int forcegui = 0;
602
603 #ifndef FATWIN
604 // The FATWIN case does not support cygwin at all...
605 if (possibly_under_cygwin)
606 { int cygwin32 = 0;
607 // Here I want to execute cygwin_isatty.exe (and/or cygwin64_isatty.exe).
608 // There can not merely have their code incorporated here directly
609 // because they are cygwin programs and so their startup is rather
610 // different from this native windows code. I had at one stage tried
611 // loading the cygwin dlls by hand to start simulating the cygwin startup
612 // but I failed to make that work!
613 //
614 // The logic here is that cygwin_isatty checks if stdin and stdout (as seen in
615 // a cygwin context) are direct from the terminal. It appears hard to
616 // tell if this would be the case without running cygwin code. It will also
617 // test if cygwin is availlable at all (in that the helper program launched
618 // here will fail utterly if not - and that can sort out whether 32 or
619 // 64-bit cygwin is available).
620 // If stdin and stdout are attached to the tty and if also the environment
621 // variables DISPLAY and SSH_HOST are unset and the user did not put
622 // "--nogui" on the command line then even though I am under a cygwin console
623 // it looks as if I might run a windows GUI. I force that by adding "--gui"
624 // to the command line and run a windows (rather than cygwin) version.
625 // If stdin and stdout where attached to the tty otherwise I will use cygwin.
626 // If DISPLAY is set and "--nogui" is not present it will launch an X11
627 // windowed GUI, otherwise it will run in console mode.
628 // Finally either stdin or stdout has been redirected in the cygwin world,
629 // and since I will in effect be in batch mode I can drop back and use the
630 // windows binaries.
631 //
632 int rc = -1;
633 #if defined FAT64 || defined FATGUIDEMO
634 if (wow64) rc = RunResource(MODULE_ISATTY64, 0, "isatty64");
635 #endif // FAT64
636 if (rc != 0)
637 { rc = RunResource(MODULE_ISATTY32, 0, "isatty32");
638 #if defined FAT64 || defined FATGUIDEMO
639 if (rc == 0) cygwin32 = 1;
640 #endif // FAT64
641 }
642 // If one of stdin or stdout is not connected to a cygwin console there is
643 // no point in trying to use cygwin at all. That is unless the user is
644 // explicitly forcing things. If the user specified "--cygwin" and does
645 // not launch from a context where cygwin1.dll and so on are available
646 // they need to expect failure.
647 if (!force_cygwin && rc != 0) possibly_under_cygwin = 0;
648 // If DISPLAY and SSH_ENV are both null then I will look at the command
649 // line options... "--cygwin" trumps most other options.
650 else if (std::getenv("DISPLAY") == nullptr &&
651 std::getenv("SSH_HOST") == nullptr &&
652 !force_cygwin)
653 { int nogui = 0;
654 // ... I look for "--nogui" (or the abbreviations "-w" or "-w-") ...
655 for (i=1; i<argc; i++)
656 { if (std::strcmp(argv[i], "--nogui") == 0 ||
657 std::strcmp(argv[i], "-w") == 0 ||
658 std::strcmp(argv[i], "-w-") == 0)
659 { nogui = 1;
660 break;
661 }
662 }
663 // If there was NOT a "--nogui" then I will run a native windows
664 // version.
665 if (nogui == 0)
666 { possibly_under_cygwin = 0;
667 forcegui = 1;
668 }
669 // Otherwise if I found cygwin32 rather than cygwin64 I must use that version.
670 else if (cygwin32) wow64 = 0;
671 }
672 // Here DISPLAY or SSH_HOST was set so I just have to choose which cygwin
673 // version to use.
674 else if (cygwin32) wow64 = 0;
675 }
676 #else
677 possibly_under_cygwin = 0;
678 #endif // FATWIN
679 #ifdef DEBUG
680 std::printf("Analysis yields wow64=%d cygwin=%d forcegui=%d\n",
681 wow64, possibly_under_cygwin, forcegui);
682 #endif
683 //
684 // Now I will run the version of Reduce that I have picked. All the #ifdef
685 // stuff is to allow for smaller binaries that support only a subset of the
686 // cases.
687 //
688 switch ((wow64<<4) | possibly_under_cygwin)
689 { case 0x00:
690 return RunResource(MODULE_WIN32, forcegui, "win32");
691 #ifndef FAT32
692 case 0x10:
693 return RunResource(MODULE_WIN64, forcegui, "win64");
694 #endif
695 #ifndef FATWIN
696 case 0x01:
697 rc = RunResource(MODULE_CYG32, forcegui, "cyg32");
698 if (rc != 0)
699 { dllcheck(dll32);
700 }
701 return rc;
702 #ifndef FAT32
703 case 0x11:
704 rc = RunResource(MODULE_CYG64, forcegui, "cyg64");
705 if (rc != 0)
706 { dllcheck(dll64);
707 }
708 return rc;
709 #endif // FAT32
710 #endif // FATWIN
711 default:
712 return 1;
713 }
714 }
715
716 // End of stub.cpp
717