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