1 // sysfwin.cpp                             Copyright (C) 1989-2021 Codemist
2 
3 //
4 // System-specific code for use with the "fwin" window interface code.
5 // The system will also build as a terminal-mode program as well as
6 // a windowed one.
7 //
8 
9 /**************************************************************************
10  * Copyright (C) 2021, Codemist.                         A C Norman       *
11  *                                                                        *
12  * Redistribution and use in source and binary forms, with or without     *
13  * modification, are permitted provided that the following conditions are *
14  * met:                                                                   *
15  *                                                                        *
16  *     * Redistributions of source code must retain the relevant          *
17  *       copyright notice, this list of conditions and the following      *
18  *       disclaimer.                                                      *
19  *     * Redistributions in binary form must reproduce the above          *
20  *       copyright notice, this list of conditions and the following      *
21  *       disclaimer in the documentation and/or other materials provided  *
22  *       with the distribution.                                           *
23  *                                                                        *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         *
28  * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   *
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
31  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
32  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR  *
33  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF     *
34  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
35  * DAMAGE.                                                                *
36  *************************************************************************/
37 
38 
39 // $Id: sysfwin.cpp 5736 2021-03-16 10:41:22Z arthurcnorman $
40 
41 #ifdef __CYGWIN__
42 #include <sys/cygwin.h>
43 #endif
44 
45 #include "headers.h"
46 
47 // There is platform-specific code in this file. Here are some of the
48 // issues it tries to encapsulate.
49 //
50 // WIN32                     all Windows platforms that I support
51 // <else>                    Unix-like
52 //
53 //    popen(cmd, dir)  vs   _popen(cms, dir)
54 //    pclose(stream)   vs   _pclose(stream)
55 //    fileno(file)     vs   _fileno(file)
56 //    struct stat      vs   struct _stat
57 //    stat             vs   _stat
58 //    ftruncate(file)  vs   chsize(file)
59 //    S_IFMT __S_IFMT       to go with stat
60 //    S_IFDIR __S_IFDIR
61 //    DO_NOT_USE_GETUID     is getuid available
62 //    UNIX_TIMES            how can I read the clock
63 //    UTIME_TIME_T          struct utimbuf
64 
65 
66 #include <sys/stat.h>
67 #include <sys/types.h>
68 #include <errno.h>
69 
70 #ifdef HAVE_UNISTD_H
71 #include <unistd.h>
72 #endif
73 
74 #ifdef HAVE_DIRENT_H
75 #include <dirent.h>
76 #endif
77 
78 #ifdef HAVE_SYS_TIME_H
79 #include <sys/time.h>
80 #endif
81 
82 #ifdef HAVE_SYS_TIMES_H
83 #include <sys/times.h>
84 #endif
85 
86 #ifdef HAVE_SYSCALL_H
87 #include <syscall.h>
88 #endif
89 
90 #ifdef HAVE_SCHED_H
91 #include <sched.h>
92 #endif
93 
94 // At present CSL is single threaded - at least as regards file IO - and
95 // using the unlocked versions of putc and getc can be a MAJOR saving.
96 // I put these macros here not in some header to try to keep me reminded
97 // that if threads ever happened I would need to do my own buffering.
98 
99 #ifdef HAVE_PUTC_UNLOCKED
100 #define PUTC(x, y) putc_unlocked((x), (y))
101 #else
102 #ifdef HAVE__PUTC_NOLOCK
103 #define PUTC(x, y) _putc_nolock((x), (y))
104 #else
105 #define PUTC(x, y) putc((x), (y))
106 #endif
107 #endif
108 
109 // Jollies re GC statistics...
110 
111 static char time_string[40], space_string[32];
112 
113 // If I am running the CSL GUI then on the top bar of the window I
114 // display some status information about how much time and space the
115 // calculation has been using. These two functions update that display, and
116 // are called periodically - ideally so that the user gets to see things
117 // chance roughtly once per second.
118 
report_time(int32_t t,int32_t gct)119 void report_time(int32_t t, int32_t gct)
120 {
121 #ifndef EMBEDDED
122     std::sprintf(time_string, "%ld.%.2ld+%ld.%.2ld secs  ",
123                  t/100L, t%100L, gct/100L, gct%100L);
124     if ((window_heading & 1) == 0) fwin_report_left(time_string);
125 #endif
126 }
127 
report_space(uint64_t n,double percent,double mbytes)128 void report_space(uint64_t n, double percent, double mbytes)
129 {
130 #ifndef EMBEDDED
131     if (mbytes > 9500.0)
132         std::sprintf(space_string, "[GC %" PRIu64 "]:%.2f%% %dG",
133                      n, percent, static_cast<int>((mbytes+500.0)/1000.0));
134     else if (mbytes > 700.0)
135         std::sprintf(space_string, "[GC %" PRIu64 "]:%.2f%% %.1fG",
136                      n, percent, mbytes/1000.0);
137     else std::sprintf(space_string, "[GC %" PRIu64 "]:%.2f%% %dM",
138                           n, percent, static_cast<int>(mbytes + 0.5));
139     if ((window_heading & 4) == 0) fwin_report_right(space_string);
140 #endif
141 }
142 
flush_screen()143 void flush_screen()
144 {   fwin_ensure_screen();
145 }
146 
147 int terminal_eof_seen = 0;
148 
149 #define CTRL_C  3
150 #define CTRL_D  4
151 #define CTRL_G  7
152 
wimpget(char * buf)153 int wimpget(char *buf)
154 {   int c, n=0;
155     ensure_screen();
156     while (n < 255)
157     {   if (terminal_eof_seen) c = EOF;
158         else
159         {   c = fwin_getchar();
160             stackcheck();       // Responds to exceptions!
161             if (c == EOF || c == CTRL_D) terminal_eof_seen = 1;
162         }
163         if (c == EOF) c = 0x1f & 'D';
164         buf[n++] = static_cast<char>(c);
165         if (c == '\n' || c == (0x1f & 'D')) break;
166     };
167     return n;
168 }
169 
my_popen(const char * command,const char * direction)170 std::FILE *my_popen(const char *command, const char *direction)
171 {
172 #ifdef EMBEDDED
173     return nullptr;
174 #else // EMBEDDED
175 // Here I have something that might count as an ugliness. If I am on
176 // Windows and am using a console-mode binary then in fact I am using a
177 // cygwin-built version of Reduce. That means that WIN32 will not be
178 // defined and this special magic will not be activated - I will end up
179 // requiring that /usr/bin/gnuplot exists in the cygwin sense of the
180 // meaning of that path-name, and that X11 and DISPLAY are available. This
181 // may mildly astonish some people. However when I tried to use the
182 // Windows-specific code in my Cygwin build I found problems to do with
183 // (perhaps) collision between Cygwin and Windows libraries, or to do with
184 // availablity of the relevant Windows system-calls under cygwin. So for
185 // now that somewhat unsatisfactory arrangement will persist.
186 #ifdef WIN32
187 // Here I take a pretty shameless direction and spot the special case of
188 // opening an output pipe to gnuplot... and hook in a behind-the-scenes
189 // way.
190     return windowsFindGnuplot(command, direction);
191 #else // WIN32
192 // The following use of "signal" is so that pipe failure does not raise
193 // an exception and blow everything out of the water. I might have expected
194 // that "popen(command-that-does-not-exist, "w")" would return nullptr, but it
195 // seems that sometimes it returns a pipe handle, and puts works on that
196 // without visible pain and only when one does an fflush does a SIGPIPE get
197 // raised. This hurts when gnuplot has not been installed on a Unix-like host.
198 // The new arrangement leads to somewhat silent failure to plot in that
199 // case, but is probably better than having an abrupt exit from the system.
200 // I know that these days I am asked to use sigaction rather than signal, but
201 // even on recent Linux variants that seems only just available...
202     std::signal(SIGPIPE, SIG_IGN);
203     return popen(command, direction);
204 #endif // WIN32
205 #endif // EMBEDDED
206 }
207 
208 #ifndef WIN32
my_pipe_putc(int c,std::FILE * f)209 int my_pipe_putc(int c, std::FILE *f)
210 {   return PUTC(c, f);
211 }
212 
my_pipe_flush(std::FILE * f)213 int my_pipe_flush(std::FILE *f)
214 {   return std::fflush(f);
215 }
216 
my_pclose(std::FILE * stream)217 void my_pclose(std::FILE *stream)
218 {   pclose(stream);
219 }
220 
221 #endif // WIN32
222 
223 // Map file-names to expand references to shell variables etc.
224 // and to provide portability of names across operating systems.
225 
226 
look_in_lisp_variable(char * o,int prefix)227 char *look_in_lisp_variable(char *o, int prefix)
228 {   LispObject var;
229 // I will start by tagging a '$' (or whatever) on in front of the
230 // parameter name.
231     o[0] = static_cast<char>(prefix);
232     var = make_undefined_symbol(o);
233 // make_undefined_symbol() could fail either if we had utterly run out
234 // of memory or if somebody generated an interrupt (eg ^C) around now. Ugh.
235 //
236 // If the variable $name was undefined then I use an empty replacement
237 // text for it. Otherwise I need to look harder at its value.
238     if (qvalue(var) == unset_var) return o;
239     else
240     {   intptr_t len;
241         var = qvalue(var);
242 // Mostly I expect that the value will be a string or symbol.
243 #ifdef COMMON
244         if (complex_stringp(var)) var = simplify_string(var);
245 #endif // COMMON
246         if (symbolp(var)) var = get_pname(var);
247         else if (!is_vector(var) || !is_string(var)) return nullptr;
248         len = length_of_byteheader(vechdr(var)) - CELL;
249 // Copy the characters from the string or from the name of the variable
250 // into the file-name buffer. There could at present be a crash here
251 // if the expansion was very very long and overflowed my buffer. Tough
252 // luck for now - people doing that (maybe) get what they (maybe) deserve.
253         std::memcpy(o, reinterpret_cast<char *>(var) + (CELL - TAG_VECTOR),
254                     (size_t)len);
255         o = o + len;
256         return o;
257     }
258 }
259 
260 
261 // What follows can be replaced by stuff from the C++ chrono:: package.
262 
263 #if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_THREAD_CPUTIME_ID
264 
265 // Where possible I read the time used by the current thread... I return
266 // a value expressed in microseconds, but of course there is no guarantee that
267 // I will have anything like that as my actual granularity!
268 
read_clock_microsecond()269 uint64_t read_clock_microsecond()
270 {   struct std::timespec tt;
271     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tt);
272     double w1 = static_cast<double>(tt.tv_sec) + static_cast<double>
273                 (tt.tv_nsec)/1000000000.0;
274     return (uint64_t)(1000000.0*w1);
275 }
276 
277 
278 #elif defined HAVE_SYS_TIME_H && defined HAVE_TIMES && !defined WIN32 && !defined EMBEDDED
279 
280 // This is a BSD-style clock facility, possibly giving a resolution of
281 // only 1/100 second.
282 
283 double unix_ticks = 0.0;
284 
read_clock()285 uint64_t read_clock()
286 {   struct tms tmsbuf;
287     times(&tmsbuf);
288     std::clock_t w1 = tmsbuf.tms_utime;   // User time in UNIX_TIMES ticks
289 #ifdef HAVE_UNISTD_H
290     if (unix_ticks == 0.0) unix_ticks =
291         static_cast<double>(sysconf(_SC_CLK_TCK));
292 #endif
293     if (unix_ticks == 0.0) unix_ticks = 100.0;
294     return (uint64_t)((1000000.0/unix_ticks) * static_cast<double>(w1));
295 }
296 
297 #elif defined WIN32
298 #else
299 
300 // In cases where clock_t is a 32-bit data type this fallback version
301 // will wraps round after around 20 minutes of CPU time!
302 
read_clock()303 uint64_t read_clock()
304 {   return static_cast<uint64_t>((1000000.0/CLOCKS_PER_SEC)*
305                                  static_cast<double>(std::clock()));
306 }
307 
308 #endif
309 
batchp()310 int batchp()
311 {   return !isatty(fileno(stdin));
312 }
313 
314 // The next procedure is responsible for establishing information about
315 // where the main checkpoint image should be recovered from, and where
316 // and fasl files should come from.
317 //
318 // On the Macintosh if the path to my executable indicated that I am
319 // within an "Application Bundle" I will look for the image file there.
320 //
321 // Otherwise I will look in two places! If the path to the executable ends
322 // up rather like BINDIR then I will check PKGDATADIR. The idea behind this
323 // is that if the files have been put in place using "make install" then
324 // the executable may be in say "...../bin/reduce" and the corresponding
325 // image would the be "..../share/reduce/reduce.img". I accept this if there
326 // is an image file in the location so suggested.
327 //
328 // Finally I look for an image file adjacent to the executable.
329 
330 #ifndef BINDIR
331 #define BINDIR /usr/local/bin
332 #endif
333 
334 #ifndef PKGDATADIR
335 #define PKGDATADIR /usr/local/share/reduce
336 #endif
337 
338 #define stringify(s) stringify_sub(s)
339 #define stringify_sub(s) #s
340 
find_image_directory(int argc,const char * argv[])341 const char *find_image_directory(int argc, const char *argv[])
342 {   int n;
343     char *w;
344     char xname[LONGEST_LEGAL_FILENAME];
345     std::memset(xname, 0, sizeof(xname));
346 #ifdef MACINTOSH
347 // There is a special oddity on the Macintosh (with the wxWidgets version
348 // where windowed versions are set up as "applications" in a directory that
349 // forms an "application bundle". The programDir here can then refer to
350 // ./reduce.app/Contents/MacOS/reduce (or whatever) and it is probably good
351 // to make the default image location be reduce.app/Contents/MacOS too.
352 // But then the vanilla console mode version is liable to
353 // be just ./reduce, and I want one image file to be used for both versions.
354 // Furthermore some kind person may have launched the executable that is
355 // within the application bundle directly from a console so that it is not
356 // really an application after all. I will do a load of rather curious
357 // tests here that are intended to detect the above cases and do special
358 // things! My tests will be based on file names and paths.
359     std::sprintf(xname, "/%s.app/Contents/MacOS", programName);
360     n = std::strlen(programDir) - std::strlen(xname);
361     if (n>=0 && std::strcmp(programDir+n, xname) == 0)
362     {   // Seem to be being executed from within application bundle.
363 // This dates from when I thought I would put the image in merely Contents not
364 // in Contents/MacOS.
365         std::sprintf(xname, "%.*s/%s.img",
366                      static_cast<int>(std::strlen(programDir)), programDir, programName);
367     }
368     else
369     {   struct stat buf;
370 // If I am NOT within an application bundle but there is one next to me I
371 // will put the image file in the application directory. Of there is no
372 // such bundle I will put the image file in the location I would have used
373 // with Windows of X11.
374         std::sprintf(xname, "%s/%s.app/Contents/MacOS", programDir,
375                      programName);
376         if (stat(xname, &buf) == 0 &&
377             (buf.st_mode & S_IFDIR) != 0)
378         {   std::sprintf(xname, "%s/%s.app/Contents/MacOS/%s.img",
379                          programDir, programName, programName);
380         }
381         else std::sprintf(xname, "%s/%s.img", programDir, programName);
382 
383     }
384 #else
385     {   const char *bin  = stringify(BINDIR);
386         const char *data = stringify(PKGDATADIR);
387 // I will strip initial directory names from bin and pkgdatadir so long as
388 // they match. So if they start off as (eg) /usr/local/bin and
389 // /usr/local/share/reduce I will remove "/usr/local" from each leaving just
390 // "/bin" and "/share/reduce". The purpose of this is so that if (despite the
391 // use of "make install") somebody has copied the tree that contains Reduce
392 // to somewhere else I might still find my resources.
393         int i, j;
394         struct stat buf;
395         const char *pn = programName;
396 #if defined WIN32 || defined __CYGWIN__
397 // On Windows I can have reduce.exe, cygwin-reduce.exe and cygwin64-reduce.exe
398 // all present, and for immediate purposes I want them all to be treated as
399 // if merely called "reduce".
400         if (std::strncmp(pn, "cygwin-", 7) == 0) pn += 7;
401         else if (std::strncmp(pn, "cygwin64-", 9) == 0) pn += 9;
402 #endif // WIN32
403         for (;;)
404         {   i = j = 0;
405             if (*bin == '/') while (bin[++i] != 0 && bin[i] != '/');
406             if (*data == '/') while (data[++j] != 0 && data[j] != '/');
407             if (i != 0 && i == j && std::strncmp(bin, data, i) == 0)
408             {   bin += i;
409                 data += i;
410             }
411             else break;
412         }
413         i = std::strlen(bin);
414         j = std::strlen(programDir);
415         if (j>=i && std::strcmp(programDir+j-i, bin)==0)
416         {   std::sprintf(xname, "%.*s%s/%s.img", j-i, programDir, data, pn);
417         }
418 
419 // If the name I just created does not correspond to a file I will fall
420 // back and use the older location, adjacent to my binary. Hmmm this is
421 // all interesting as regards building an image file for the first time.
422 // I think it tells us that you had better not try doing that using the
423 // installed version - do that with a copy that sits in your own private
424 // writable are of disc.
425         if (stat(xname, &buf) != 0)
426             std::sprintf(xname, "%s/%s.img", programDir, pn);
427     }
428 #endif
429     n = std::strlen(xname)+1;
430     w = new (std::nothrow) char[n];
431     if (w == nullptr) my_abort();
432     std::strcpy(w, xname);
433     return w;
434 }
435 
436 #ifdef WIN32
437 #define GPNAME  "wgnuplot.exe"
438 #define DIRCHAR '\\'
439 #else
440 #define GPNAME  "gnuplot"
441 #define DIRCHAR '/'
442 #endif
443 
444 
445 // When Reduce wants to invoke gnuplot it needs a command-line to pass to
446 // "pipe-open". This procedure creates on (if it can), The idea is
447 // to try three possibilities in turn:
448 // (1) If an environment variable GNUPLOT is set then that should be set to
449 //     a path within which the gnuplot executable exists. So eg if the
450 //     value of GNUPLOT is "/usr/extras/gnuplotfiles" then the result
451 //     here is liable to be "/usr/extras/gnuplotfiles/gnuplot".
452 // (2) If a file called "gnuplot" (or "wgnuplot.exe in the windows case) is
453 //     present in the directory where the Reduce executable was found then
454 //     it will be used.
455 // (3) If a file called "gnuplot" (or "wgnuplot.exe in the windows case) is
456 //     present in the directory where the Reduce image would (by default)
457 //     be found then it will be used.  In some cases this is actually the
458 //     same as (2) above, but it can differ if the executable is in
459 //     .../bin and the image in .../share/reduce or some such.
460 // (4) A search will be made in the "standard place". For Windows this will
461 //     involve scanning the registry to seek an installation of gnuplot,
462 //     while otherwise it will be expected that the ordinary PATH will
463 //     provide access.
464 // (5) Failing all else I will just hand back the name of the executable and
465 //     hope that it is on a PATH.
466 
executable_file(const char * name)467 int executable_file(const char *name)
468 {   struct stat buf;
469     if (stat(name, &buf) == -1) return 0;
470 #ifndef S_ISUSR
471     return 1;
472 #else
473     return (buf.st_mode & S_IXUSR);
474 #endif
475 }
476 
find_gnuplot(char * name)477 int find_gnuplot(char *name)
478 {   const char *w = std::getenv("GNUPLOT");
479     size_t len;
480     if (w != nullptr && (len = std::strlen(w)) > 0)
481     {   if (w[len-1] == '/' ||
482             w[len-1] == '\\') len--;
483         std::sprintf(name, "%.*s%c%s", static_cast<int>(len), w, DIRCHAR,
484                      GPNAME);
485         if (executable_file(name)) return 1;
486     }
487     std::strcpy(name, programDir);
488     len = std::strlen(name);
489     while (len-- > 0 &&
490            name[len] != '/' &&
491            name[len] != '\\');
492     if (len != 0)
493     {   std::strcpy(&name[len+1], GPNAME);
494         if (executable_file(name)) return 1;
495     }
496     std::strcpy(name, standard_directory);
497     len = std::strlen(name);
498     while (len-- > 0 &&
499            name[len] != '/' &&
500            name[len] != '\\');
501     if (len != 0)
502     {   std::strcpy(&name[len+1], GPNAME);
503         if (executable_file(name)) return 1;
504     }
505 #ifdef __CYGWIN__
506 // As usual Cygwin is an odd case. If DISPLAY is set and /usr/bin/gnuplot
507 // exists then I will try for an X11 usage of gnuplot. That should be
508 // the "natural" case!
509     w = std::getenv("DISPLAY");
510     if (w!=nullptr && *w!=0 &&
511         executable_file("/usr/bin/gnuplot.exe"))
512     {   std::strcpy(name, "/usr/bin/gnuplot.exe");
513         return 1;
514     }
515 // ... well actually people may be using the cygwin version of Reduce because
516 // they ran the console mode version of Reduce under Cygwin's mintty. But
517 // they may not have X11 available. In the case perhaps I should try for
518 // a native Windows version of gnuplot for them... I look for gnuplot.exe here
519 // rather than wgnuplot.exe and will use it via a pipe rather than using
520 // the windows-special interface method.
521     if (windowsFindGnuplot2(name) != 0) return 1;
522 #endif
523 #ifdef WIN32
524     if (windowsFindGnuplot1(name) != 0) return 1;
525 #endif // WIN32
526     std::strcpy(name, GPNAME);
527     return 1;
528 }
529 
530 // The following function controls memory allocation policy
531 
ok_to_grab_memory(int32_t current)532 int32_t ok_to_grab_memory(int32_t current)
533 {   return 3*current + 2;
534 }
535 
536 // I will provide a function that reports how many processors are
537 // available. This may be of importance for multi-core systems where I
538 // could exploit around that many threads to especial benefit. In cases when
539 // I can not obtain the information I will merely report "1" which should
540 // be a fail-safe fallback. Issues such as the user setting processor
541 // affinities etc may make the information obtained indicative rather than
542 // definitive!
543 //
544 // Well in the Old Days this was a mess with much system-dependent code,
545 // but C++11 provides just what I need directly!
546 
547 
number_of_processors()548 unsigned int number_of_processors()
549 {   return std::thread::hardware_concurrency();
550 }
551 
552 static int tmpSerial = 0;
553 
554 static char tempname[LONGEST_LEGAL_FILENAME];
555 
CSLtmpdir()556 const char *CSLtmpdir()
557 {
558 #ifdef __CYGWIN__
559 // First try the Windows path to "/tmp"
560     char *p;
561     if (cygwin_conv_path(CCP_POSIX_TO_WIN_A,
562                          "/tmp", tempname, sizeof(tempname)) != 0)
563     {   size_t n = windowsGetTempPath(sizeof(tempname), tempname);
564         if (n == 0 || n > sizeof(tempname)) return ".";
565         tempname[n-1] = 0; // Remove trailing "\"
566     }
567     for (p=tempname; *p!=0; p++)
568         if (*p == '\\') *p = '/';
569     return tempname;
570 #else
571 #if defined WIN32
572     size_t n = windowsGetTempPath(sizeof(tempname), tempname);
573     if (n == 0 || n > sizeof(tempname)) return ".";
574     tempname[n-1] = 0; // Remove trailing "\"
575     return tempname;
576 #else
577     return "/tmp";
578 #endif
579 #endif
580 }
581 
CSLtmpnam(const char * suffix,size_t suffixlen)582 const char *CSLtmpnam(const char *suffix, size_t suffixlen)
583 {   std::time_t t0 = std::time(nullptr);
584     std::clock_t c0 = std::clock();
585     unsigned long taskid;
586     char fname[LONGEST_LEGAL_FILENAME];
587     char tt[32];
588     char *s;
589 #ifdef WIN32
590     size_t len;
591     std::memset(fname, 0, sizeof(fname));
592     len = windowsGetTempPath(LONGEST_LEGAL_FILENAME, tempname);
593     if ((int64_t)len <= 0) return nullptr;
594 // I want to avoid name clashes fairly often, so I will use the current
595 // time of day and information about the current process as a seed for the
596 // generated file-name so that (with luck) clashes are at least not
597 // incredibly probable. I will also use my source of random numbers, which
598 // adds variation that changes each time I call this function.
599     taskid = static_cast<unsigned long>(GetCurrentThreadId())*169 +
600              static_cast<unsigned long>(GetCurrentProcessId());
601 #else
602     std::memset(fname, 0, sizeof(fname));
603     std::strcpy(tempname, "/tmp/");
604     taskid = static_cast<unsigned long>(getpid())*169 +
605              static_cast<unsigned long>(getuid());
606 #endif
607     taskid = 169*taskid + static_cast<unsigned long>(t0);
608     taskid = 169*taskid + static_cast<unsigned long>(c0);
609     taskid = 169 * taskid + tmpSerial++;
610 // The information I have gathered thus far may not change terribly rapidly,
611 // since the process id is static form any one instance of my code and the
612 // clock may tick very slowly compared with the CPU's activity.
613     for (;;)
614     {   unsigned long n;
615         int i;
616 // The next line reduces taskid modulo the largest prime under 2^32, which
617 // may be a sensible thing to do of "unsigned long" had been a 64-bit
618 // data type.
619         n = taskid % 0xfffffffbUL;
620 // At this stage I have at most 32-bits of information, derived from the
621 // clock and process identification. I will combine in info from the
622 // random number generator I have elsewhere in this code, and do that in
623 // such a way that I can generate 8 characters of file-name.
624         s = tempname + std::strlen(tempname);
625         for (i=0; i<7; i++)
626         {   int d = static_cast<int>(n % 36);
627             n = n / 36;
628             if (i == 1) n ^= static_cast<unsigned long>(Crand());
629             if (d < 10) d += '0';
630             else d += ('a' - 10);   // now 0-9 or 1-z
631             *s++ = d;
632         }
633         n = n % 36;
634         if (n < 10) *s++ = '0' + static_cast<int>(n);
635         else *s++ = 'a' + static_cast<int>(n - 10);
636         if (suffix != nullptr)
637         {   std::sprintf(s, ".%.*s", static_cast<int>(suffixlen), suffix);
638         }
639         else *s = 0;
640 // If the file whose name I have just invented already exists I need to
641 // try again. I will count of the "random" sequence from Crand to propose
642 // an alternative name for me.
643         if (file_exists(fname, tempname, std::strlen(tempname), tt))
644         {   taskid ^= n;
645             continue;
646         }
647         break;
648     }
649     return tempname;
650 }
651 
652 // The following functions are best described as delicate, and they are only
653 // present for debugging purposes. It is not clear to me how much performance
654 // penalty they introduce, and certainly in a multi-threaded context the
655 // state of availabaility of memory to one thread can be changed by a
656 // different thread, leaving any result found here out of date. The Windows
657 // code also hints at issues where pages are marked in a special manner to
658 // let them act as guard pages - and potential consequences of that (eg
659 // wrt stack extension) are not handled carefully here.
660 
661 #if defined __CYGWIN__ || defined __unix__ || defined MACINTOSH
662 
663 // This code is intended to discover whether the pointer that is passed
664 // is a valid address. This is JUST intended for debugging so that I can
665 // go things like
666 //   my_assert(valid_address(p), [=]{ ... });
667 // with my own custom diagnostics in the code that reports trouble.
668 // The failure is not expected to arise except when I have an internal
669 // error in CSL.
670 // The write() function never causes an exception, but instead returns
671 // -1 if it fails, and sets errno to EFAULT if the buffer address that it
672 // is passed offends it.
673 
674 #include <unistd.h>
675 #include <fcntl.h>
676 
677 static std::FILE *file_handle;
678 static int fd_handle;
679 static bool file_handle_set = false;
680 
valid_address(void * pointer)681 bool valid_address(void *pointer)
682 {   if (!file_handle_set)
683     {   if ((file_handle = std::tmpfile()) == nullptr) return false;
684         fd_handle = fileno(file_handle);
685         file_handle_set = true;   // I will open the fd just once.
686     }
687 // I will not bother to check errno, and just take any failure as
688 // indicating a bad memory address in the pointer.
689     return (write(fd_handle, pointer, 1) != -1);
690 }
691 
692 #elif defined WIN32
693 #else
694 
valid_address(void * pointer)695 bool valid_address(void *pointer)
696 {   return true;
697 }
698 
699 #endif
700 
valid_address(uintptr_t pointer)701 bool valid_address(uintptr_t pointer)  // an overload to accept integer types
702 {   return valid_address(reinterpret_cast<void *>(pointer));
703 }
704 
705 // end of sysfwin.cpp
706