00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "pex-common.h"
00023
00024 #include <windows.h>
00025
00026 #ifdef HAVE_STDLIB_H
00027 #include <stdlib.h>
00028 #endif
00029 #ifdef HAVE_STRING_H
00030 #include <string.h>
00031 #endif
00032 #ifdef HAVE_UNISTD_H
00033 #include <unistd.h>
00034 #endif
00035 #ifdef HAVE_SYS_WAIT_H
00036 #include <sys/wait.h>
00037 #endif
00038
00039 #include <assert.h>
00040 #include <process.h>
00041 #include <io.h>
00042 #include <fcntl.h>
00043 #include <signal.h>
00044 #include <sys/stat.h>
00045 #include <errno.h>
00046 #include <ctype.h>
00047
00048
00049
00050 #ifndef _P_WAIT
00051 # define _P_WAIT 0
00052 # define _P_NOWAIT 1
00053 # define _P_OVERLAY 2
00054 # define _P_NOWAITO 3
00055 # define _P_DETACH 4
00056
00057 # define WAIT_CHILD 0
00058 # define WAIT_GRANDCHILD 1
00059 #endif
00060
00061 #define MINGW_NAME "Minimalist GNU for Windows"
00062 #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
00063
00064 extern char *stpcpy (char *dst, const char *src);
00065
00066
00067
00068
00069
00070
00071
00072 static void
00073 backslashify (char *s)
00074 {
00075 while ((s = strchr (s, '/')) != NULL)
00076 *s = '\\';
00077 return;
00078 }
00079
00080 static int pex_win32_open_read (struct pex_obj *, const char *, int);
00081 static int pex_win32_open_write (struct pex_obj *, const char *, int);
00082 static long pex_win32_exec_child (struct pex_obj *, int, const char *,
00083 char * const *, char * const *,
00084 int, int, int, int,
00085 const char **, int *);
00086 static int pex_win32_close (struct pex_obj *, int);
00087 static int pex_win32_wait (struct pex_obj *, long, int *,
00088 struct pex_time *, int, const char **, int *);
00089 static int pex_win32_pipe (struct pex_obj *, int *, int);
00090 static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
00091 static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
00092
00093
00094
00095 const struct pex_funcs funcs =
00096 {
00097 pex_win32_open_read,
00098 pex_win32_open_write,
00099 pex_win32_exec_child,
00100 pex_win32_close,
00101 pex_win32_wait,
00102 pex_win32_pipe,
00103 pex_win32_fdopenr,
00104 pex_win32_fdopenw,
00105 NULL
00106 };
00107
00108
00109
00110 struct pex_obj *
00111 pex_init (int flags, const char *pname, const char *tempbase)
00112 {
00113 return pex_init_common (flags, pname, tempbase, &funcs);
00114 }
00115
00116
00117
00118 static int
00119 pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
00120 int binary)
00121 {
00122 return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
00123 }
00124
00125
00126
00127 static int
00128 pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
00129 int binary)
00130 {
00131
00132
00133 return _open (name,
00134 (_O_WRONLY | _O_CREAT | _O_TRUNC
00135 | (binary ? _O_BINARY : _O_TEXT)),
00136 _S_IREAD | _S_IWRITE);
00137 }
00138
00139
00140
00141 static int
00142 pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
00143 {
00144 return _close (fd);
00145 }
00146
00147 #ifdef USE_MINGW_MSYS
00148 static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
00149
00150
00151
00152 static const char *
00153 tack_on_executable (char *buf, const char *executable)
00154 {
00155 char *p = strchr (buf, '\0');
00156 if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
00157 p[-1] = '\0';
00158 backslashify (strcat (buf, executable));
00159 return buf;
00160 }
00161
00162
00163 static HKEY
00164 openkey (HKEY hStart, const char *keys[])
00165 {
00166 HKEY hKey, hTmp;
00167 for (hKey = hStart; *keys; keys++)
00168 {
00169 LONG res;
00170 hTmp = hKey;
00171 res = RegOpenKey (hTmp, *keys, &hKey);
00172
00173 if (hTmp != HKEY_LOCAL_MACHINE)
00174 RegCloseKey (hTmp);
00175
00176 if (res != ERROR_SUCCESS)
00177 return NULL;
00178 }
00179 return hKey;
00180 }
00181
00182
00183 static const char *
00184 mingw_rootify (const char *executable)
00185 {
00186 HKEY hKey, hTmp;
00187 DWORD maxlen;
00188 char *namebuf, *foundbuf;
00189 DWORD i;
00190 LONG res;
00191
00192
00193 hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
00194
00195
00196 if (!hKey)
00197 return executable;
00198
00199
00200
00201 if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
00202 NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
00203 {
00204 RegCloseKey (hKey);
00205 return executable;
00206 }
00207 namebuf = XNEWVEC (char, ++maxlen);
00208 foundbuf = XNEWVEC (char, maxlen);
00209 foundbuf[0] = '\0';
00210 if (!namebuf || !foundbuf)
00211 {
00212 RegCloseKey (hKey);
00213 if (namebuf)
00214 free (namebuf);
00215 if (foundbuf)
00216 free (foundbuf);
00217 return executable;
00218 }
00219
00220
00221
00222
00223 for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
00224 {
00225 int match = strcasecmp (namebuf, MINGW_NAME);
00226 if (match < 0)
00227 continue;
00228 if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
00229 continue;
00230 if (strcasecmp (namebuf, foundbuf) > 0)
00231 strcpy (foundbuf, namebuf);
00232 }
00233 free (namebuf);
00234
00235
00236 if (!foundbuf[0])
00237 {
00238 free (foundbuf);
00239 RegCloseKey (hKey);
00240 return executable;
00241 }
00242
00243
00244 res = RegOpenKey (hKey, foundbuf, &hTmp);
00245 RegCloseKey (hKey);
00246 free (foundbuf);
00247
00248
00249 if (res != ERROR_SUCCESS)
00250 return executable;
00251
00252 maxlen = 0;
00253
00254 if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
00255 &maxlen) != ERROR_SUCCESS || maxlen == 0)
00256 {
00257 RegCloseKey (hTmp);
00258 return executable;
00259 }
00260
00261
00262 foundbuf = XNEWVEC (char, maxlen + strlen (executable));
00263 if (!foundbuf)
00264 {
00265 free (foundbuf);
00266 RegCloseKey (hTmp);
00267 }
00268
00269
00270 res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
00271 &maxlen);
00272 RegCloseKey (hTmp);
00273 if (res != ERROR_SUCCESS)
00274 {
00275 free (foundbuf);
00276 return executable;
00277 }
00278
00279
00280
00281 return tack_on_executable (foundbuf, executable);
00282 }
00283
00284
00285
00286 static const char *
00287 msys_rootify (const char *executable)
00288 {
00289 size_t bufsize = 64;
00290 size_t execlen = strlen (executable) + 1;
00291 char *buf;
00292 DWORD res = 0;
00293 for (;;)
00294 {
00295 buf = XNEWVEC (char, bufsize + execlen);
00296 if (!buf)
00297 break;
00298 res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
00299 buf, bufsize, "msys.ini");
00300 if (!res)
00301 break;
00302 if (strlen (buf) < bufsize)
00303 break;
00304 res = 0;
00305 free (buf);
00306 bufsize *= 2;
00307 if (bufsize > 65536)
00308 {
00309 buf = NULL;
00310 break;
00311 }
00312 }
00313
00314 if (res)
00315 return tack_on_executable (buf, executable);
00316
00317
00318 if (buf)
00319 free (buf);
00320 return executable;
00321 }
00322 #endif
00323
00324
00325
00326
00327 static char *
00328 argv_to_cmdline (char *const *argv)
00329 {
00330 char *cmdline;
00331 char *p;
00332 size_t cmdline_len;
00333 int i, j, k;
00334
00335 cmdline_len = 0;
00336 for (i = 0; argv[i]; i++)
00337 {
00338
00339
00340
00341
00342
00343 for (j = 0; argv[i][j]; j++)
00344 {
00345 if (argv[i][j] == '"')
00346 {
00347
00348 for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00349 cmdline_len++;
00350
00351 cmdline_len++;
00352 }
00353 }
00354
00355
00356 for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00357 cmdline_len++;
00358 cmdline_len += j;
00359 cmdline_len += 3;
00360 }
00361 cmdline = xmalloc (cmdline_len);
00362 p = cmdline;
00363 for (i = 0; argv[i]; i++)
00364 {
00365 *p++ = '"';
00366 for (j = 0; argv[i][j]; j++)
00367 {
00368 if (argv[i][j] == '"')
00369 {
00370 for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00371 *p++ = '\\';
00372 *p++ = '\\';
00373 }
00374 *p++ = argv[i][j];
00375 }
00376 for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
00377 *p++ = '\\';
00378 *p++ = '"';
00379 *p++ = ' ';
00380 }
00381 p[-1] = '\0';
00382 return cmdline;
00383 }
00384
00385 static const char *const
00386 std_suffixes[] = {
00387 ".com",
00388 ".exe",
00389 ".bat",
00390 ".cmd",
00391 0
00392 };
00393 static const char *const
00394 no_suffixes[] = {
00395 "",
00396 0
00397 };
00398
00399
00400
00401
00402 static char *
00403 find_executable (const char *program, BOOL search)
00404 {
00405 char *full_executable;
00406 char *e;
00407 size_t fe_len;
00408 const char *path = 0;
00409 const char *const *ext;
00410 const char *p, *q;
00411 size_t proglen = strlen (program);
00412 int has_extension = !!strchr (program, '.');
00413 int has_slash = (strchr (program, '/') || strchr (program, '\\'));
00414 HANDLE h;
00415
00416 if (has_slash)
00417 search = FALSE;
00418
00419 if (search)
00420 path = getenv ("PATH");
00421 if (!path)
00422 path = "";
00423
00424 fe_len = 0;
00425 for (p = path; *p; p = q)
00426 {
00427 q = p;
00428 while (*q != ';' && *q != '\0')
00429 q++;
00430 if ((size_t)(q - p) > fe_len)
00431 fe_len = q - p;
00432 if (*q == ';')
00433 q++;
00434 }
00435 fe_len = fe_len + 1 + proglen + (has_extension ? 1 : 5);
00436 full_executable = xmalloc (fe_len);
00437
00438 p = path;
00439 do
00440 {
00441 q = p;
00442 while (*q != ';' && *q != '\0')
00443 q++;
00444
00445 e = full_executable;
00446 memcpy (e, p, q - p);
00447 e += (q - p);
00448 if (q - p)
00449 *e++ = '\\';
00450 strcpy (e, program);
00451
00452 if (*q == ';')
00453 q++;
00454
00455 for (e = full_executable; *e; e++)
00456 if (*e == '/')
00457 *e = '\\';
00458
00459
00460
00461 for (ext = has_extension ? no_suffixes : std_suffixes; *ext; ext++)
00462 {
00463
00464 *e = '\0';
00465
00466 strcat (full_executable, *ext);
00467
00468
00469 h = CreateFile (full_executable, GENERIC_READ,
00470 FILE_SHARE_READ | FILE_SHARE_WRITE,
00471 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
00472 if (h != INVALID_HANDLE_VALUE)
00473 goto found;
00474 }
00475 p = q;
00476 }
00477 while (*p);
00478 free (full_executable);
00479 return 0;
00480
00481 found:
00482 CloseHandle (h);
00483 return full_executable;
00484 }
00485
00486
00487
00488 static int
00489 env_compare (const void *a_ptr, const void *b_ptr)
00490 {
00491 const char *a;
00492 const char *b;
00493 unsigned char c1;
00494 unsigned char c2;
00495
00496 a = *(const char **) a_ptr;
00497 b = *(const char **) b_ptr;
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508 do
00509 {
00510 c1 = (unsigned char) tolower (*a++);
00511 c2 = (unsigned char) tolower (*b++);
00512
00513 if (c1 == '=')
00514 c1 = '\0';
00515
00516 if (c2 == '=')
00517 c2 = '\0';
00518 }
00519 while (c1 == c2 && c1 != '\0');
00520
00521 return c1 - c2;
00522 }
00523
00524 static long
00525 win32_spawn (const char *executable,
00526 BOOL search,
00527 char *const *argv,
00528 char *const *env,
00529 DWORD dwCreationFlags,
00530 LPSTARTUPINFO si,
00531 LPPROCESS_INFORMATION pi)
00532 {
00533 char *full_executable;
00534 char *cmdline;
00535 char **env_copy;
00536 char *env_block = NULL;
00537
00538 full_executable = NULL;
00539 cmdline = NULL;
00540
00541 if (env)
00542 {
00543 int env_size;
00544
00545
00546 for (env_size = 0; env[env_size]; env_size++)
00547 continue;
00548
00549
00550
00551
00552 if (env_size > 0)
00553 {
00554 int var;
00555 int total_size = 1;
00556 char *bufptr;
00557
00558
00559
00560 env_copy = alloca (sizeof (char *) * env_size);
00561 memcpy (env_copy, env, sizeof (char *) * env_size);
00562 qsort (env_copy, env_size, sizeof (char *), env_compare);
00563
00564 for (var = 0; var < env_size; var++)
00565 total_size += strlen (env[var]) + 1;
00566
00567 env_block = malloc (total_size);
00568 bufptr = env_block;
00569 for (var = 0; var < env_size; var++)
00570 bufptr = stpcpy (bufptr, env_copy[var]) + 1;
00571
00572 *bufptr = '\0';
00573 }
00574 }
00575
00576 full_executable = find_executable (executable, search);
00577 if (!full_executable)
00578 goto error;
00579 cmdline = argv_to_cmdline (argv);
00580 if (!cmdline)
00581 goto error;
00582
00583
00584 if (!CreateProcess (full_executable, cmdline,
00585 NULL,
00586 NULL,
00587 TRUE,
00588 dwCreationFlags,
00589 (LPVOID) env_block,
00590 NULL,
00591 si,
00592 pi))
00593 {
00594 if (env_block)
00595 free (env_block);
00596
00597 free (full_executable);
00598
00599 return -1;
00600 }
00601
00602
00603 CloseHandle (pi->hThread);
00604 free (full_executable);
00605 if (env_block)
00606 free (env_block);
00607
00608 return (long) pi->hProcess;
00609
00610 error:
00611 if (env_block)
00612 free (env_block);
00613 if (cmdline)
00614 free (cmdline);
00615 if (full_executable)
00616 free (full_executable);
00617
00618 return -1;
00619 }
00620
00621 static long
00622 spawn_script (const char *executable, char *const *argv,
00623 char* const *env,
00624 DWORD dwCreationFlags,
00625 LPSTARTUPINFO si,
00626 LPPROCESS_INFORMATION pi)
00627 {
00628 int pid = -1;
00629 int save_errno = errno;
00630 int fd = _open (executable, _O_RDONLY);
00631
00632 if (fd >= 0)
00633 {
00634 char buf[MAX_PATH + 5];
00635 int len = _read (fd, buf, sizeof (buf) - 1);
00636 _close (fd);
00637 if (len > 3)
00638 {
00639 char *eol;
00640 buf[len] = '\0';
00641 eol = strchr (buf, '\n');
00642 if (eol && strncmp (buf, "#!", 2) == 0)
00643 {
00644 char *executable1;
00645 const char ** avhere = (const char **) --argv;
00646 do
00647 *eol = '\0';
00648 while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
00649 for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
00650 continue;
00651
00652 backslashify (executable1);
00653 *avhere = executable1;
00654 #ifndef USE_MINGW_MSYS
00655 executable = strrchr (executable1, '\\') + 1;
00656 if (!executable)
00657 executable = executable1;
00658 pid = win32_spawn (executable, TRUE, argv, env,
00659 dwCreationFlags, si, pi);
00660 #else
00661 if (strchr (executable1, '\\') == NULL)
00662 pid = win32_spawn (executable1, TRUE, argv, env,
00663 dwCreationFlags, si, pi);
00664 else if (executable1[0] != '\\')
00665 pid = win32_spawn (executable1, FALSE, argv, env,
00666 dwCreationFlags, si, pi);
00667 else
00668 {
00669 const char *newex = mingw_rootify (executable1);
00670 *avhere = newex;
00671 pid = win32_spawn (newex, FALSE, argv, env,
00672 dwCreationFlags, si, pi);
00673 if (executable1 != newex)
00674 free ((char *) newex);
00675 if (pid < 0)
00676 {
00677 newex = msys_rootify (executable1);
00678 if (newex != executable1)
00679 {
00680 *avhere = newex;
00681 pid = win32_spawn (newex, FALSE, argv, env,
00682 dwCreationFlags, si, pi);
00683 free ((char *) newex);
00684 }
00685 }
00686 }
00687 #endif
00688 }
00689 }
00690 }
00691 if (pid < 0)
00692 errno = save_errno;
00693 return pid;
00694 }
00695
00696
00697
00698 static long
00699 pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
00700 const char *executable, char * const * argv,
00701 char* const* env,
00702 int in, int out, int errdes,
00703 int toclose ATTRIBUTE_UNUSED,
00704 const char **errmsg,
00705 int *err)
00706 {
00707 long pid;
00708 HANDLE stdin_handle;
00709 HANDLE stdout_handle;
00710 HANDLE stderr_handle;
00711 DWORD dwCreationFlags;
00712 OSVERSIONINFO version_info;
00713 STARTUPINFO si;
00714 PROCESS_INFORMATION pi;
00715
00716 stdin_handle = INVALID_HANDLE_VALUE;
00717 stdout_handle = INVALID_HANDLE_VALUE;
00718 stderr_handle = INVALID_HANDLE_VALUE;
00719
00720 stdin_handle = (HANDLE) _get_osfhandle (in);
00721 stdout_handle = (HANDLE) _get_osfhandle (out);
00722 if (!(flags & PEX_STDERR_TO_STDOUT))
00723 stderr_handle = (HANDLE) _get_osfhandle (errdes);
00724 else
00725 stderr_handle = stdout_handle;
00726
00727
00728 version_info.dwOSVersionInfoSize = sizeof (version_info);
00729 GetVersionEx (&version_info);
00730 if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
00731
00732
00733 dwCreationFlags = 0;
00734 else
00735 {
00736 HANDLE conout_handle;
00737
00738
00739 conout_handle = CreateFile("CONOUT$",
00740 GENERIC_WRITE,
00741 FILE_SHARE_WRITE,
00742 NULL,
00743 OPEN_EXISTING,
00744 FILE_ATTRIBUTE_NORMAL,
00745 NULL);
00746 if (conout_handle == INVALID_HANDLE_VALUE)
00747
00748
00749
00750
00751
00752 dwCreationFlags = CREATE_NO_WINDOW;
00753 else
00754 {
00755
00756
00757
00758
00759
00760
00761 CloseHandle(conout_handle);
00762 dwCreationFlags = 0;
00763 }
00764 }
00765
00766
00767
00768
00769
00770
00771
00772 memset (&si, 0, sizeof (si));
00773 si.cb = sizeof (si);
00774 si.dwFlags = STARTF_USESTDHANDLES;
00775 si.hStdInput = stdin_handle;
00776 si.hStdOutput = stdout_handle;
00777 si.hStdError = stderr_handle;
00778
00779
00780 pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
00781 argv, env, dwCreationFlags, &si, &pi);
00782 if (pid == -1)
00783 pid = spawn_script (executable, argv, env, dwCreationFlags,
00784 &si, &pi);
00785 if (pid == -1)
00786 {
00787 *err = ENOENT;
00788 *errmsg = "CreateProcess";
00789 }
00790
00791
00792
00793 if (out != STDOUT_FILENO)
00794 obj->funcs->close (obj, out);
00795 if (errdes != STDERR_FILENO)
00796 obj->funcs->close (obj, errdes);
00797
00798 return pid;
00799 }
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809 static int
00810 pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, long pid,
00811 int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
00812 const char **errmsg, int *err)
00813 {
00814 DWORD termstat;
00815 HANDLE h;
00816
00817 if (time != NULL)
00818 memset (time, 0, sizeof *time);
00819
00820 h = (HANDLE) pid;
00821
00822
00823
00824 if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
00825 {
00826 CloseHandle (h);
00827 *err = ECHILD;
00828 *errmsg = "WaitForSingleObject";
00829 return -1;
00830 }
00831
00832 GetExitCodeProcess (h, &termstat);
00833 CloseHandle (h);
00834
00835
00836
00837
00838 if (termstat == 3)
00839 *status = SIGABRT;
00840 else
00841 *status = (termstat & 0xff) << 8;
00842
00843 return 0;
00844 }
00845
00846
00847
00848 static int
00849 pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
00850 int binary)
00851 {
00852 return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
00853 }
00854
00855
00856
00857 static FILE *
00858 pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
00859 int binary)
00860 {
00861 return fdopen (fd, binary ? "rb" : "r");
00862 }
00863
00864 static FILE *
00865 pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
00866 int binary)
00867 {
00868 HANDLE h = (HANDLE) _get_osfhandle (fd);
00869 if (h == INVALID_HANDLE_VALUE)
00870 return NULL;
00871 if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
00872 return NULL;
00873 return fdopen (fd, binary ? "wb" : "w");
00874 }
00875
00876 #ifdef MAIN
00877 #include <stdio.h>
00878
00879 int
00880 main (int argc ATTRIBUTE_UNUSED, char **argv)
00881 {
00882 char const *errmsg;
00883 int err;
00884 argv++;
00885 printf ("%ld\n", pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err));
00886 exit (0);
00887 }
00888 #endif