first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / library / application / windoze_helper.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : windoze_helper                                                    *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 1994-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "windoze_helper.h"
16 ///#include "array.h"
17 ///#include "build_configuration.h"
18 ///#include "convert_utf.h"
19 ///#include "function.h"
20 ///#include "mutex.h"
21 ///#include "portable.h"
22 ///#include "set.h"
23 ///#include "version_record.h"
24 #include <configuration/application_configuration.h>
25
26 ///#include <errno.h>
27 ///#include <stdlib.h>
28
29 /*
30 #ifdef __UNIX__
31   #include <limits.h>
32   #include <stdio.h>
33   #include <string.h>
34   #include <sys/poll.h>
35   #include <sys/times.h>
36   #include <sys/utsname.h>
37   #include <sys/wait.h>
38   #include <time.h>
39   #include <unistd.h>
40 #endif
41 #ifdef __XWINDOWS__
42 //hmmm: need code for the wait cursor stuff.
43 #endif
44 #ifdef __WIN32__
45   #include <mmsystem.h>
46   #include <process.h>
47   #include <shellapi.h>
48   #include <shlobj.h>
49 #endif
50 */
51
52 using namespace basis;
53 using namespace structures;
54 using namespace configuration;
55
56 #undef static_class_name
57 #define static_class_name() "windoze_helper"
58
59 /*
60 //#define DEBUG_PORTABLE
61   // uncomment for noisy debugging.
62
63 //#define DEBUG_UPTIME
64   // uncomment to get noisier reporting about system and rolling uptime.
65
66 //#define JUMP_TIME_49_DAYS
67   // uncomment to make our uptimes start just before the 32 bit uptime rollover.
68 //#define JUMP_TIME_497_DAYS
69   // uncomment to make our uptimes start just before the jiffies rollover.
70
71 //#define ENABLE_ROLLOVER_BUG
72   // uncomment to get old behavior back where the uptime was not rolling
73   // over properly.  this turns off our remainder calculation and leaves the
74   // conversion as a simple cast, which will fail and get stuck at 2^32-1.
75
76 #define SUPPORT_SHELL_EXECUTE
77   // if this is not commented out, then the ShellExecute version of launch_
78   // -process() is available.  when commented out, ShellExecute is turned off.
79   // disabling this support is the most common because the ShellExecute method
80   // in win32 was only supported for wk203 and wxp, that is only after
81   // windows2000 was already available.  since nt and w2k don't support this,
82   // we just usually don't mess with it.  it didn't answer a single one of our
83   // issues on windows vista (wfista) anyway, so it's not helping.
84
85 // ensure we always have debugging turned on if the jump is enabled.
86 #ifdef JUMP_TIME_49_DAYS
87   #undef DEBUG_UPTIME
88   #define DEBUG_UPTIME
89 #endif
90 #ifdef JUMP_TIME_497_DAYS
91   #undef DEBUG_UPTIME
92   #define DEBUG_UPTIME
93 #endif
94 // the JUMP..DAYS macros are mutually exclusive.  use none or one, not both.
95 #ifdef JUMP_TIME_497_DAYS
96   #ifdef JUMP_TIME_49_DAYS
97     #error One cannot use both 497 day and 49 day bug inducers
98   #endif
99 #endif
100 #ifdef JUMP_TIME_49_DAYS
101   #ifdef JUMP_TIME_497_DAYS
102     #error One cannot use both 49 day and 497 day bug inducers
103   #endif
104 #endif
105 */
106
107
108 namespace application {
109
110 /*
111 mutex BASIS_EXTERN &__uptime_synchronizer();
112   // used by our low-level uptime methods to protect singleton variables.
113 mutex BASIS_EXTERN &__process_synchronizer();
114   // used for synchronizing our records of child processes.
115 #ifdef __UNIX__
116 int_set BASIS_EXTERN &__our_kids();
117   // the static list of processes we've started.
118 #endif
119
120 const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE;
121   // maximum command line that we'll deal with here.
122
123 #ifdef __UNIX__
124   const char *UPTIME_REPORT_FILE = "/tmp/uptime_report.log";
125 #endif
126 #ifdef __WIN32__
127   const char *UPTIME_REPORT_FILE = "c:/uptime_report.log";
128 #endif
129
130 #undef LOG
131 #define LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger(), s);
132
133 #define COMPLAIN(to_print) { \
134   guards::write_to_console((isprintf("basis/portable::%s: ", func) + to_print).s()); \
135 }
136
137 static const double __rollover_point = 2.0 * double(MAXINT);
138   // this number is our rollover point for 32 bit integers.
139
140 double rolling_uptime()
141 {
142   auto_synchronizer l(__uptime_synchronizer());
143     // protect our rollover records.
144
145   static u_int __last_ticks = 0;
146   static int __rollovers = 0;
147
148   u_int ticks_up = system_uptime();
149     // acquire the current uptime as a 32 bit unsigned int.
150
151   if (ticks_up < __last_ticks) {
152     // rollover happened.  increment our tracker.
153     __rollovers++;
154   }
155   __last_ticks = ticks_up;
156
157   double to_return = double(__rollovers) * __rollover_point + double(ticks_up);
158
159 #ifdef DEBUG_UPTIME
160   #ifdef __WIN32__
161   static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "a+b");
162   static double old_value = 0;
163   if (absolute_value(old_value - to_return) > 9.9999) {
164     // only report when the time changes by more than 10 ms.
165     fprintf(__outfile, "-> uptime=%.0f\n", to_return);
166     fflush(__outfile);
167     old_value = to_return;
168     if (__rollover_point - to_return <= 40.00001) {
169       fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n");
170       fflush(__outfile);
171     }
172   }
173   #endif
174 #endif
175
176   return to_return;
177 }
178
179 u_int system_uptime()
180 {
181 #ifdef __WIN32__
182   return timeGetTime();
183 #else
184   auto_synchronizer l(__uptime_synchronizer());
185
186   static clock_t __ctps = sysconf(_SC_CLK_TCK);  // clock ticks per second.
187   static const double __multiplier = 1000.0 / double(__ctps);
188     // the multiplier gives us our full range for the tick counter.
189
190 #ifdef DEBUG_UPTIME
191   static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "wb");
192   if (__multiplier - u_int(__multiplier) > 0.000001) {
193     fprintf(__outfile, "uptime multiplier is "
194         "non-integral (%f)!\n", __multiplier);
195     fflush(__outfile);
196   }
197 #endif
198
199   // read uptime info from the OS.
200   tms uptime;
201   u_int real_ticks = times(&uptime);
202
203 #ifdef JUMP_TIME_497_DAYS
204   static u_int old_value_497 = 0;
205   bool report_497 = (absolute_value(real_ticks - old_value_497) > 99);
206   if (report_497) {
207     old_value_497 = real_ticks;  // update before changing it.
208     fprintf(__outfile, "pre kludge497 tix=%u\n", real_ticks);
209     fflush(__outfile);
210   }
211   real_ticks += u_int(49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms));
212   if (report_497) {
213     fprintf(__outfile, "post kludge497 tix=%u\n", real_ticks);
214     fflush(__outfile);
215   }
216 #endif
217
218   // now turn this into the number of milliseconds.
219   double ticks_up = (double)real_ticks;
220   ticks_up = ticks_up * __multiplier;  // convert to time here.
221
222 #ifdef JUMP_TIME_497_DAYS
223   #ifndef ENABLE_ROLLOVER_BUG
224     // add in additional time so we don't have to wait forever.  we make the
225     // big number above rollover, but that's still got 27.5 or so minutes before
226     // we really rollover.  so we add that part of the fraction (lost in the
227     // haze of the multiplier) in here.  we don't add this in unless they are
228     // not exercising the rollover bug, because we already know that the 497
229     // day bug will show without the addition.  but when we're already fixing
230     // the uptime, we jump time a bit forward so we only have to wait a couple
231     // minutes instead of 27.
232     ticks_up += 25.0 * MINUTE_ms;
233   #endif
234 #endif
235
236 #ifdef JUMP_TIME_49_DAYS
237   static u_int old_value_49 = 0;
238   bool report_49 = (absolute_value(u_int(ticks_up) - old_value_49) > 999);
239   if (report_49) {
240     old_value_49 = u_int(ticks_up);  // update before changing it.
241     fprintf(__outfile, "pre kludge49 up=%f\n", ticks_up);
242     fflush(__outfile);
243   }
244   ticks_up += 49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms);
245   if (report_49) {
246     fprintf(__outfile, "post kludge49 up=%f\n", ticks_up);
247     fflush(__outfile);
248   }
249 #endif
250
251 #ifndef ENABLE_ROLLOVER_BUG
252   // fix the return value if is has actually gone over the 2^32 limit for uint.
253   // casting a double larger than 2*MAXINT to a u_int on some platforms does
254   // not calculate a rolled-over value, but instead leaves the int at 2*MAXINT.
255   // thus we make sure it will be correct, as long as there are no more than
256   // 2^32-1 rollovers, which would be about 584,542 millenia.  it's unlikely
257   // earth will last that long, so this calculation seems safe.
258   u_int divided = u_int(ticks_up / __rollover_point);
259   double to_return = ticks_up - (double(divided) * __rollover_point);
260 #else
261   // we use the previous version of this calculation, which expected a u_int
262   // to double conversion to provide a modulo operation rather than just leaving
263   // the u_int at its maximum value (2^32-1).  however, that expectation is not
264   // guaranteed on some platforms (e.g., ARM processor with floating point
265   // emulation) and thus it becomes a bug around 49 days and 17 hours into
266   // OS uptime because the value gets stuck at 2^32-1 and never rolls over.
267   double to_return = ticks_up;
268 #endif
269
270 #ifdef DEBUG_UPTIME
271   static u_int old_value = 0;
272   int to_print = int(u_int(to_return));
273   if (absolute_value(int(old_value) - to_print) > 9) {
274     // only report when the time changes by more than 10 ms.
275     fprintf(__outfile, "-> uptime=%u\n", to_print);
276     fflush(__outfile);
277     old_value = u_int(to_print);
278     if (__rollover_point - to_return <= 40.00001) {
279       fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n");
280       fflush(__outfile);
281     }
282   }
283 #endif
284
285   return u_int(to_return);
286 #endif
287 }
288
289 void sleep_ms(u_int msec)
290 {
291 #ifdef __UNIX__
292   usleep(msec * 1000);
293 #endif
294 #ifdef __WIN32__
295   Sleep(msec);
296 #endif
297 }
298
299 istring null_device()
300 {
301 #ifdef __WIN32__
302   return "null:";
303 #else
304   return "/dev/null";
305 #endif
306 }
307
308 #ifdef __WIN32__
309 bool event_poll(MSG &message)
310 {
311   message.hwnd = 0;
312   message.message = 0;
313   message.wParam = 0;
314   message.lParam = 0;
315   if (!PeekMessage(&message, NIL, 0, 0, PM_REMOVE))
316     return false;
317   TranslateMessage(&message);
318   DispatchMessage(&message);
319   return true;
320 }
321 #endif
322
323 // makes a complaint about a failure.
324 #ifndef EMBEDDED_BUILD
325   #define COMPLAIN_QUERY(to_print) { \
326     unlink(tmpfile.s()); \
327     COMPLAIN(to_print); \
328   }
329 #else
330   #define COMPLAIN_QUERY(to_print) { \
331     COMPLAIN(to_print); \
332   }
333 #endif
334
335 #ifdef __UNIX__
336 istring get_cmdline_from_proc()
337 {
338   FUNCDEF("get_cmdline_from_proc");
339   isprintf cmds_filename("/proc/%d/cmdline", portable::process_id());
340   FILE *cmds_file = fopen(cmds_filename.s(), "r");
341   if (!cmds_file) {
342     COMPLAIN("failed to open our process's command line file.\n");
343     return "unknown";
344   }
345   size_t size = 2000;
346   char *buff = new char[size + 1];
347   ssize_t chars_read = getline(&buff, &size, cmds_file);
348     // read the first line, giving ample space for how long it might be.
349   fclose(cmds_file);  // drop the file again.
350   if (!chars_read || negative(chars_read)) {
351     COMPLAIN("failed to get any characters from our process's cmdline file.\n");
352     return "unknown";
353   }
354   istring buffer = buff;
355   delete [] buff;
356   // clean out quote characters from the name.
357   for (int i = buffer.length() - 1; i >= 0; i--) {
358     if (buffer[i] == '"') buffer.zap(i, i);
359   }
360   return buffer;
361 }
362
363 // deprecated; better to use the /proc/pid/cmdline file.
364 istring query_for_process_info()
365 {
366   FUNCDEF("query_for_process_info");
367   istring to_return = "unknown";
368   // we ask the operating system about our process identifier and store
369   // the results in a temporary file.
370   chaos rando;
371   isprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", portable::process_id(),
372       rando.inclusive(0, 128000));
373   isprintf cmd("ps h --format \"%%a\" %d >%s", portable::process_id(),
374       tmpfile.s());
375   // run the command to locate our process info.
376   int sysret = system(cmd.s());
377   if (negative(sysret)) {
378     COMPLAIN_QUERY("failed to run ps command to get process info");
379     return to_return;
380   }
381   // open the output file for reading.
382   FILE *output = fopen(tmpfile.s(), "r");
383   if (!output) {
384     COMPLAIN_QUERY("failed to open the ps output file");
385     return to_return;
386   }
387   // read the file's contents into a string buffer.
388   char buff[MAXIMUM_COMMAND_LINE];
389   size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output);
390   if (size_read > 0) {
391     // success at finding some text in the file at least.
392     while (size_read > 0) {
393       const char to_check = buff[size_read - 1];
394       if ( !to_check || (to_check == '\r') || (to_check == '\n')
395           || (to_check == '\t') )
396         size_read--;
397       else break;
398     }
399     to_return.reset(istring::UNTERMINATED, buff, size_read);
400   } else {
401     // couldn't read anything.
402     COMPLAIN_QUERY("could not read output of process list");
403   }
404   unlink(tmpfile.s());
405   return to_return;
406 }
407 #endif
408
409 // used as a return value when the name cannot be determined.
410 #ifndef EMBEDDED_BUILD
411   #define SET_BOGUS_NAME(error) { \
412     COMPLAIN(error); \
413     if (output) { \
414       fclose(output); \
415       unlink(tmpfile.s()); \
416     } \
417     istring home_dir = env_string("HOME"); \
418     to_return = home_dir + "/failed_to_determine.exe"; \
419   }
420 #else
421   #define SET_BOGUS_NAME(error) { \
422     COMPLAIN(error); \
423     to_return = "unknown"; \
424   }
425 #endif
426
427 istring application_name()
428 {
429   FUNCDEF("application_name");
430   istring to_return;
431 #ifdef __UNIX__
432   to_return = get_cmdline_from_proc();
433 #elif defined(__WIN32__)
434   flexichar low_buff[MAX_ABS_PATH + 1];
435   GetModuleFileName(NIL, low_buff, MAX_ABS_PATH - 1);
436   istring buff = from_unicode_temp(low_buff);
437   buff.to_lower();  // we lower-case the name since windows seems to UC it.
438   to_return = buff;
439 #elif defined(EMBEDDED_BUILD)
440   SET_BOGUS_NAME("embedded_exe");
441 #else
442   #pragma error("hmmm: no means of finding app name is implemented.")
443   SET_BOGUS_NAME("not_implemented_for_this_OS");
444 #endif
445   return to_return;
446 }
447
448 istring module_name(const void *module_handle)
449 {
450 #ifdef __UNIX__
451 //hmmm: implement module name for linux if that makes sense.
452   if (module_handle) {}
453   return application_name();
454 #elif defined(__WIN32__)
455   flexichar low_buff[MAX_ABS_PATH + 1];
456   GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1);
457   istring buff = from_unicode_temp(low_buff);
458   buff.to_lower();
459   return buff;
460 #else
461   #pragma message("module_name unknown for this operating system.")
462   return application_name();
463 #endif
464 }
465
466 u_int system_error()
467 {
468 #if defined(__UNIX__)
469   return errno;
470 #elif defined(__WIN32__)
471   return GetLastError();
472 #else
473   #pragma error("hmmm: no code for error number for this operating system")
474   return 0;
475 #endif
476 }
477
478 istring system_error_text(u_int to_name)
479 {
480 #if defined(__UNIX__)
481   return strerror(to_name);
482 #elif defined(__WIN32__)
483   char error_text[1000];
484   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NIL, to_name,
485       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text,
486       sizeof(error_text) - 1, NIL);
487   istring to_return = error_text;
488   // trim off the ridiculous carriage return they add.
489   while ( (to_return[to_return.end()] == '\r')
490       || (to_return[to_return.end()] == '\n') )
491     to_return.zap(to_return.end(), to_return.end());
492   return to_return;
493 #else
494   #pragma error("hmmm: no code for error text for this operating system")
495   return "";
496 #endif
497 }
498
499 #ifdef __WIN32__
500
501 bool is_address_valid(const void *address, int size_expected, bool writable)
502 {
503   return address && !IsBadReadPtr(address, size_expected)
504       && !(writable && IsBadWritePtr((void *)address, size_expected));
505 }
506
507 #endif // win32
508
509 version get_OS_version()
510 {
511   version to_return;
512 #ifdef __UNIX__
513   utsname kernel_parms;
514   uname(&kernel_parms);
515   to_return = version(kernel_parms.release);
516 #elif defined(__WIN32__)
517   OSVERSIONINFO info;
518   info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
519   ::GetVersionEx(&info);
520   to_return = version(isprintf("%u.%u.%u.%u", u_short(info.dwMajorVersion),
521       u_short(info.dwMinorVersion), u_short(info.dwPlatformId),
522       u_short(info.dwBuildNumber)));
523 #elif defined(EMBEDDED_BUILD)
524   // no version support.
525 #else
526   #pragma error("hmmm: need version info for this OS!")
527 #endif
528   return to_return;
529 }
530
531 // for non-win32 and non-unix OSes, this might not work.
532 #if defined(__UNIX__) || defined(__WIN32__)
533   u_int process_id() { return getpid(); }
534 #else
535   #pragma error("hmmm: need process id implementation for this OS!")
536   u_int process_id() { return 0; }
537 #endif
538
539 istring current_directory()
540 {
541   istring to_return;
542 #ifdef __UNIX__
543   char buff[MAX_ABS_PATH];
544   getcwd(buff, MAX_ABS_PATH - 1);
545   to_return = buff;
546 #elif defined(__WIN32__)
547   flexichar low_buff[MAX_ABS_PATH + 1];
548   GetCurrentDirectory(MAX_ABS_PATH, low_buff);
549   to_return = from_unicode_temp(low_buff);
550 #else
551   #pragma error("hmmm: need support for current directory on this OS.")
552   to_return = ".";
553 #endif
554   return to_return;
555 }
556
557 istring env_string(const istring &variable_name)
558 {
559 #ifdef __WIN32__
560   char *value = getenv(variable_name.upper().observe());
561     // dos & os/2 require upper case for the name, so we just do it that way.
562 #else
563   char *value = getenv(variable_name.observe());
564     // reasonable OSes support mixed-case environment variables.
565 #endif
566   istring to_return;
567   if (value)
568     to_return = istring(value);
569   return to_return;
570 }
571
572 bool set_environ(const istring &variable_name, const istring &value)
573 {
574   int ret = 0;
575 #ifdef __WIN32__
576   ret = putenv((variable_name + "=" + value).s());
577 #else
578   ret = setenv(variable_name.s(), value.s(), true);
579 #endif
580   return !ret;
581 }
582
583 timeval fill_timeval_ms(int duration)
584 {
585   timeval time_out;  // timeval has tv_sec=seconds, tv_usec=microseconds.
586   if (!duration) {
587     // duration is immediate for the check; just a quick poll.
588     time_out.tv_sec = 0;
589     time_out.tv_usec = 0;
590 #ifdef DEBUG_PORTABLE
591 //    LOG("no duration specified");
592 #endif
593   } else {
594     // a non-zero duration means we need to compute secs and usecs.
595     time_out.tv_sec = duration / 1000;
596     // set the number of seconds from the input in milliseconds.
597     duration -= time_out.tv_sec * 1000;
598     // now take out the chunk we've already recorded as seconds.
599     time_out.tv_usec = duration * 1000;
600     // set the number of microseconds from the remaining milliseconds.
601 #ifdef DEBUG_PORTABLE
602 //    LOG(isprintf("duration of %d ms went to %d sec and %d usec.", duration,
603 //        time_out.tv_sec, time_out.tv_usec));
604 #endif
605   }
606   return time_out;
607 }
608
609 //hmmm: this doesn't seem to account for quoting properly at all?
610 basis::char_star_array break_line(istring &app, const istring &parameters)
611 {
612   basis::char_star_array to_return;
613   int_array posns;
614   int num = 0;
615   // find the positions of the spaces and count them.
616   for (int j = 0; j < parameters.length(); j++) {
617     if (parameters[j] == ' ') {
618       num++;
619       posns += j;
620     }
621   }
622   // first, add the app name to the list of parms.
623   to_return += new char[app.length() + 1];
624   app.stuff(to_return[0], app.length());
625   int last_posn = 0;
626   // now add each space-separated parameter to the list.
627   for (int i = 0; i < num; i++) {
628     int len = posns[i] - last_posn;
629     to_return += new char[len + 1];
630     parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len);
631     last_posn = posns[i] + 1;
632   }
633   // catch anything left after last separator.
634   if (last_posn < parameters.length() - 1) {
635     int len = parameters.length() - last_posn;
636     to_return += new char[len + 1];
637     parameters.substring(last_posn, parameters.length() - 1)
638         .stuff(to_return[to_return.last()], len);
639   }
640   // add the sentinel to the list of strings.
641   to_return += NIL;
642 #ifdef DEBUG_PORTABLE
643   for (int q = 0; to_return[q]; q++) {
644     printf("%d: %s\n", q, to_return[q]);
645   }
646 #endif
647   // now a special detour; fix the app name to remove quotes, which are
648   // not friendly to pass to exec.
649   if (app[0] == '"') app.zap(0, 0);
650   if (app[app.end()] == '"') app.zap(app.end(), app.end());
651   return to_return;
652 }
653
654 #ifdef __UNIX__
655
656 void exiting_child_signal_handler(int sig_num)
657 {
658   FUNCDEF("exiting_child_signal_handler");
659   if (sig_num != SIGCHLD) {
660     // uhhh, this seems wrong.
661   }
662   auto_synchronizer l(__process_synchronizer());
663   for (int i = 0; i < __our_kids().length(); i++) {
664     int status;
665     pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG);
666     if ( (exited == -1) || (exited == __our_kids()[i]) ) {
667       // negative one denotes an error, which we are going to assume means the
668       // process has exited via some other method than our wait.  if the value
669       // is the same as the process we waited for, that means it exited.
670       __our_kids().zap(i, i);
671       i--;
672     } else if (exited != 0) {
673       // zero would be okay; this result we do not understand.
674 #ifdef DEBUG_PORTABLE
675       printf("unknown result %d waiting for process %d", exited,
676           __our_kids()[i]);
677 #endif
678     }
679   }
680 }
681 #endif
682
683 u_int launch_process(const istring &app_name_in, const istring &command_line,
684     int flag, u_int &child_id)
685 {
686   child_id = 0;
687   istring app_name = app_name_in;
688   if (app_name[0] != '"')
689     app_name.insert(0, "\"");
690   if (app_name[app_name.end()] != '"')
691     app_name += "\"";
692 #ifdef __UNIX__
693   // unix / linux implementation.
694   if (flag & RETURN_IMMEDIATELY) {
695     // they want to get back right away.
696     pid_t kid_pid = fork();
697 #ifdef DEBUG_PORTABLE
698     printf("launch fork returned %d\n", kid_pid);
699 #endif
700     if (!kid_pid) {
701       // this is the child; we now need to launch into what we were asked for.
702 #ifdef DEBUG_PORTABLE
703       printf((isprintf("process %d execing ", process_id()) + app_name
704           + " parms " + command_line + "\n").s());
705 #endif
706       basis::char_star_array parms = break_line(app_name, command_line);
707       execv(app_name.s(), parms.observe());
708       // oops.  failed to exec if we got to here.
709 #ifdef DEBUG_PORTABLE
710       printf((isprintf("child of fork (pid %d) failed to exec, "
711           "error is ", process_id()) + system_error_text(system_error())
712           + "\n").s());
713 #endif
714       exit(0);  // leave since this is a failed child process.
715     } else {
716       // this is the parent.  let's see if the launch worked.
717       if (kid_pid == -1) {
718         // failure.
719         u_int to_return = system_error();
720 #ifdef DEBUG_PORTABLE
721         printf((isprintf("parent %d is returning after failing to create, "
722             "error is ", process_id()) + system_error_text(to_return)
723             + "\n").s());
724 #endif
725         return to_return;
726       } else {
727         // yes, launch worked okay.
728         child_id = kid_pid;
729         {
730           auto_synchronizer l(__process_synchronizer());
731           __our_kids() += kid_pid;
732         }
733         // hook in our child exit signal handler.
734         signal(SIGCHLD, exiting_child_signal_handler);
735
736 #ifdef DEBUG_PORTABLE
737         printf((isprintf("parent %d is returning after successfully "
738             "creating %d ", process_id(), kid_pid) + app_name
739             + " parms " + command_line + "\n").s());
740 #endif
741         return 0;
742       }
743     }
744   } else {
745     // assume they want to wait.
746     return system((app_name + " " + command_line).s());
747   }
748 #elif defined(__WIN32__)
749
750 //checking on whether we have admin rights for the launch.
751 //if (IsUserAnAdmin()) {
752 //  MessageBox(0, (istring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
753 //} else {
754 //  MessageBox(0, (istring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK);
755 //}
756
757   PROCESS_INFORMATION process_info;
758   ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
759
760 #ifdef SUPPORT_SHELL_EXECUTE
761   if (flag & SHELL_EXECUTE) {
762     // new code for using shell execute method--required on vista for proper
763     // launching with the right security tokens.
764     int show_cmd = 0;
765     if (flag & HIDE_APP_WINDOW) {
766       // magic that hides a console window for mswindows.
767       show_cmd = SW_HIDE;
768     } else {
769       show_cmd = SW_SHOWNORMAL;
770     }
771
772     SHELLEXECUTEINFO exec_info;
773     ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO));
774     exec_info.cbSize = sizeof(SHELLEXECUTEINFO);
775     exec_info.fMask = SEE_MASK_NOCLOSEPROCESS  // get the process info.
776         | SEE_MASK_FLAG_NO_UI;  // turn off any visible error dialogs.
777     exec_info.hwnd = GetDesktopWindow();
778 //hmmm: is get desktop window always appropriate?
779     to_unicode_persist(temp_verb, "open");
780 //does "runas" work on xp also?  or anywhere?
781     exec_info.lpVerb = temp_verb;
782     to_unicode_persist(temp_file, app_name);
783     exec_info.lpFile = temp_file;
784     to_unicode_persist(temp_parms, command_line);
785     exec_info.lpParameters = temp_parms;
786     exec_info.nShow = show_cmd;
787 //    exec_info.hProcess = &process_info;
788
789     BOOL worked = ShellExecuteEx(&exec_info);
790     if (!worked)
791       return system_error();
792     // copy out the returned process handle.
793     process_info.hProcess = exec_info.hProcess;
794     process_info.dwProcessId = GetProcessId(exec_info.hProcess);
795   } else {
796 #endif //shell exec
797     // standard windows implementation using CreateProcess.
798     STARTUPINFO startup_info;
799     ZeroMemory(&startup_info, sizeof(STARTUPINFO));
800     startup_info.cb = sizeof(STARTUPINFO);
801     int create_flag = 0;
802     if (flag & HIDE_APP_WINDOW) {
803       // magic that hides a console window for mswindows.
804 //      version ver = portable::get_OS_version();
805 //      version vista_version(6, 0);
806 //      if (ver < vista_version) {
807 //        // we suspect that this flag is hosing us in vista.
808         create_flag = CREATE_NO_WINDOW;
809 //      }
810     }
811     istring parms = app_name + " " + command_line;
812     bool success = CreateProcess(NIL, to_unicode_temp(parms), NIL, NIL, false,
813         create_flag, NIL, NIL, &startup_info, &process_info);
814     if (!success)
815       return system_error();
816     // success then, merge back into stream.
817
818 #ifdef SUPPORT_SHELL_EXECUTE
819   }
820 #endif //shell exec
821
822   // common handling for CreateProcess and ShellExecuteEx.
823   child_id = process_info.dwProcessId;
824   u_long retval = 0;
825   if (flag & AWAIT_VIA_POLLING) {
826     // this type of waiting is done without blocking on the process.
827     while (true) {
828       MSG msg;
829       event_poll(msg);
830       // check if the process is gone yet.
831       BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval);
832       if (!ret) {
833         break;
834       } else {
835         // if they aren't saying it's still active, then we will leave.
836         if (retval != STILL_ACTIVE)
837           break;
838       }
839       sleep_ms(14);
840     }
841   } else if (flag & AWAIT_APP_EXIT) {
842     // they want to wait for the process to exit.
843     WaitForInputIdle(process_info.hProcess, INFINITE); 
844     WaitForSingleObject(process_info.hProcess, INFINITE);
845     GetExitCodeProcess(process_info.hProcess, &retval);
846   }
847   // drop the process and thread handles.
848   if (process_info.hProcess)
849     CloseHandle(process_info.hProcess);
850   if (process_info.hThread)
851     CloseHandle(process_info.hThread);
852   return (u_int)retval;
853 #else
854   #pragma error("hmmm: launch_process: no implementation for this OS.")
855 #endif
856   return 0;
857 }
858
859 ////////////////////////////////////////////////////////////////////////////
860
861 #ifdef __UNIX__
862
863 char *itoa(int to_convert, char *buffer, int radix)
864 {
865   // slow implementation; may need enhancement for serious speed.
866   // ALSO: only supports base 10 and base 16 currently.
867   const char *formatter = "%d";
868   if (radix == 16) formatter = "%x";
869   isprintf printed(formatter, to_convert);
870   printed.stuff(buffer, printed.length() + 1);
871   return buffer;
872 }
873
874 #endif
875 */
876
877 #ifdef __WIN32__
878
879 /*
880 void show_wait_cursor() { SetCursor(LoadCursor(NULL, IDC_WAIT)); }
881
882 void show_normal_cursor() { SetCursor(LoadCursor(NULL, IDC_ARROW)); }
883
884 istring rc_string(UINT id, application_instance instance)
885 {
886   flexichar temp[MAX_ABS_PATH + 1];
887   int ret = LoadString(instance, id, temp, MAX_ABS_PATH);
888   if (!ret) return istring();
889   return istring(from_unicode_temp(temp));
890 }
891
892 istring rc_string(u_int id) { return rc_string(id, GET_INSTANCE_HANDLE()); }
893 */
894
895 const char *opsystem_name(known_operating_systems which)
896 {
897   switch (which) {
898     case WIN_95: return "WIN_95";
899     case WIN_NT: return "WIN_NT";
900     case WIN_2K: return "WIN_2K";
901     case WIN_XP: return "WIN_XP";
902     case WIN_SRV2K3: return "WIN_SRV2K3";
903     case WIN_VISTA: return "WIN_VISTA";
904     case WIN_SRV2K8: return "WIN_SRV2K8";
905     default: return "UNKNOWN_OS";
906   }
907 }
908
909 known_operating_systems determine_OS()
910 {
911   version osver = application_configuration::get_OS_version();
912   if ( (osver.v_major() == 4) && (osver.v_minor() == 0) ) {
913     if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) return WIN_95;
914     if (osver.v_revision() == VER_PLATFORM_WIN32_NT) return WIN_NT;
915   } else if ( (osver.v_major() == 5) && (osver.v_minor() == 0) ) {
916     return WIN_2K;
917   } else if ( (osver.v_major() == 5) && (osver.v_minor() == 1) ) {
918     return WIN_XP;
919   } else if ( (osver.v_major() == 5) && (osver.v_minor() == 2) ) {
920     return WIN_SRV2K3;
921   } else if ( (osver.v_major() == 6) && (osver.v_minor() == 0) ) {
922     return WIN_VISTA;
923   } else if ( (osver.v_major() == 6) && (osver.v_minor() == 1) ) {
924     return WIN_SRV2K8;
925   }
926   return UNKNOWN_OS;
927 }
928
929 #endif // win32
930
931 } // namespace.
932
933 #undef static_class_name
934