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