--- C:\GNU\gnumake-src\make-3.81\w32\subproc\sub_proc.c 2006-03-10 04:20:46.000000000 +-0300 +++ C:\GNU\gnumake-src\make-3.81patched\w32\subproc\sub_proc.c 2006-08-15 02:52:28.000000000 +-0300 @@ -1,6 +1,13 @@ +// patch by pavel_a@writeme.com 14-aug-06 +//+pa01 Replaced OpenFile() with SearchPath to support max win32 path length +//+pa01 to do: detection of shell & other scripts? +//+pa02 Fixed declaraton for threadex style thread functions (stdcall...) +//+pa02 to do: why 0 used in place of INVALID_HANDLE_VALUE ? + + /* Process handling for Windows. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Make. GNU Make is free software; you can redistribute it and/or modify it under the @@ -248,18 +255,18 @@ pipes[1] = ((sub_process *)proc)->sv_stdout[0]; pipes[2] = ((sub_process *)proc)->sv_stderr[0]; return; } */ - HANDLE +HANDLE process_init() { sub_process *pproc; /* - * open file descriptors for attaching stdin/stdout/sterr + * open file descriptors for attaching stdin/stdout/stderr */ HANDLE stdin_pipes[2]; HANDLE stdout_pipes[2]; HANDLE stderr_pipes[2]; SECURITY_ATTRIBUTES inherit; BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH]; @@ -321,13 +328,13 @@ pproc->lerrno = 0; return((HANDLE)pproc); } - HANDLE +HANDLE process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh) { sub_process *pproc; pproc = malloc(sizeof(*pproc)); memset(pproc, 0, sizeof(*pproc)); @@ -343,61 +350,82 @@ pproc->last_err = pproc->lerrno = 0; return((HANDLE)pproc); } -static HANDLE -find_file(char *exec_path, LPOFSTRUCT file_info) -{ - HANDLE exec_handle; - char *fname; - char *ext; - - fname = malloc(strlen(exec_path) + 5); - strcpy(fname, exec_path); - ext = fname + strlen(fname); - - strcpy(ext, ".exe"); - if ((exec_handle = (HANDLE)OpenFile(fname, file_info, - OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { - free(fname); - return(exec_handle); - } - - strcpy(ext, ".cmd"); - if ((exec_handle = (HANDLE)OpenFile(fname, file_info, - OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { - free(fname); - return(exec_handle); - } - - strcpy(ext, ".bat"); - if ((exec_handle = (HANDLE)OpenFile(fname, file_info, - OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { - free(fname); - return(exec_handle); - } - - /* should .com come before this case? */ - if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info, - OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { - free(fname); - return(exec_handle); - } - - strcpy(ext, ".com"); - if ((exec_handle = (HANDLE)OpenFile(fname, file_info, - OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) { - free(fname); - return(exec_handle); - } - - free(fname); - return(exec_handle); -} +//+pa01 +/* Find the executable file name ( by Windows rules ) + * and determine if the file is a script, and it's associated application. + * Returns: + * TRUE if exec_path successfully resolved, and: + * *file_path: the resolved executable path (allocated; caller must free) or NULL + * *shell_name: NULL or shell prog path (allocated; caller must free) + * NOTE: In Windows, .cmd files are "executable", + * that is, CreateProcess will automatically start cmd.exe for .cmd or .bat file + * => for cmd|bat files we return NULL as shell app name. + * For other script types (.pl, .js, .vbs, unixy sh ...) we'll need to figure out + * their app name. + * PATH will be searched only if exec_path has NO path components at all. + * Otherwise, the path part of exec_path is used to further search for known extensions. + * (If the given path is relative, it will be resolved using the current dir). + * ---------------------------------------------- + * !Check for scripts NOT IMPLEMENTED - TO DO LATER - pa01 + * !For now, run scripts explicitly (via "cmd /c", "perl" and so on) + * !Cmd.exe knows executable extensions from PATHEXT environment variable, and will search in PATH. + * !Decide whether we want to rely on cmd.exe or roll our own here. + */ +static int +find_exefile( const char *exec_path, char **file_path, char **shell_name ) +{ + DWORD n; + PCHAR pFilePart; + + *shell_name = NULL; // not implemented, return "none" -- pa01 + + // Check whether exec_path exists as is; if yes, return. + // This case covers a fully specified filename + n = GetFileAttributes( exec_path ); + if( INVALID_FILE_ATTRIBUTES != n ) { + // TODO: revise when implementing script files... + // - if ext present: must be exe cmd bat, else check for other scripts + *file_path = strdup( exec_path ); + return TRUE; + } + + // Not found as is: + *file_path = malloc( MAX_PATH + 2 ); + if( NULL == *file_path ) { + return FALSE; + } + + // Find in PATH, with default EXE extension: + n = SearchPath( NULL, exec_path, ".exe", MAX_PATH + 1, *file_path, &pFilePart ); + if( (n != 0) && (n < MAX_PATH) ) { + return TRUE; // Found + } + + // TODO: if ext was present: don't continue, return FALSE. Already searched in path. + + // Same, try .cmd and .bat: + n = SearchPath( NULL, exec_path, ".cmd", MAX_PATH + 1, *file_path, &pFilePart ); + if( (n != 0) && (n < MAX_PATH) ) { + return TRUE; // Found + } + + n = SearchPath( NULL, exec_path, ".bat", MAX_PATH + 1, *file_path, &pFilePart ); + if( (n != 0) && (n < MAX_PATH) ) { + return TRUE; // Found + } + + // To do: check for common script exts (.pl and so on) + + // Nothing found + free( *file_path ); + return FALSE; +} /* * Description: Create the child process to be helped * * Returns: success <=> 0 @@ -410,182 +438,128 @@ char **argv, char **envp, char *exec_path, char *as_user) { sub_process *pproc = (sub_process *)proc; - char *shell_name = 0; + char *shell_name = 0; // must free int file_not_found=0; - HANDLE exec_handle; - char buf[256]; - DWORD bytes_returned; DWORD flags; char *command_line; STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; char *envblk=NULL; - OFSTRUCT file_info; - + char *file_path = NULL; // must free - /* - * Shell script detection... if the exec_path starts with #! then - * we want to exec shell-script-name exec-path, not just exec-path - * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not - * hard-code the path to the shell or perl or whatever: Instead, we - * assume it's in the path somewhere (generally, the NT tools - * bin directory) - * We use OpenFile here because it is capable of searching the Path. - */ - - exec_handle = find_file(exec_path, &file_info); - - /* - * If we couldn't open the file, just assume that Windows32 will be able - * to find and execute it. - */ - if (exec_handle == (HANDLE)HFILE_ERROR) { - file_not_found++; - } - else { - /* Attempt to read the first line of the file */ - if (ReadFile( exec_handle, - buf, sizeof(buf) - 1, /* leave room for trailing NULL */ - &bytes_returned, 0) == FALSE || bytes_returned < 2) { - - pproc->last_err = GetLastError(); - pproc->lerrno = E_IO; - CloseHandle(exec_handle); - return(-1); - } - if (buf[0] == '#' && buf[1] == '!') { - /* - * This is a shell script... Change the command line from - * exec_path args to shell_name exec_path args - */ - char *p; - - /* Make sure buf is NULL terminated */ - buf[bytes_returned] = 0; - /* - * Depending on the file system type, etc. the first line - * of the shell script may end with newline or newline-carriage-return - * Whatever it ends with, cut it off. - */ - p= strchr(buf, '\n'); - if (p) - *p = 0; - p = strchr(buf, '\r'); - if (p) - *p = 0; - - /* - * Find base name of shell - */ - shell_name = strrchr( buf, '/'); - if (shell_name) { - shell_name++; - } else { - shell_name = &buf[2];/* skipping "#!" */ - } - - } - CloseHandle(exec_handle); - } + if (as_user) { + pproc->last_err = 0; + pproc->lerrno = E_NOTIMPL; + return -1; /* not supported? */ + } + + file_not_found = !find_exefile(exec_path, &file_path, &shell_name); + + /* + * If we couldn't resolve the file, just assume that CreateProcess will be able + * to find and execute it (maybe give up here? --pa01) + */ flags = 0; - if (file_not_found) - command_line = make_command_line( shell_name, exec_path, argv); - else - command_line = make_command_line( shell_name, file_info.szPathName, - argv); + command_line = make_command_line( shell_name, file_not_found? exec_path : file_path, argv ); if ( command_line == NULL ) { pproc->last_err = 0; pproc->lerrno = E_NO_MEM; + free( shell_name ); /* free(NULL) is ok in MSVC -- pa01 */ + free( file_path ); return(-1); } if (envp) { if (arr2envblk(envp, &envblk) ==FALSE) { pproc->last_err = 0; pproc->lerrno = E_NO_MEM; free( command_line ); + free( shell_name ); + free( file_path ); return(-1); } } if ((shell_name) || (file_not_found)) { - exec_path = 0; /* Search for the program in %Path% */ + exec_path = 0; /* Rely on CreateProcess to find the program (system dir, %Path% and so on) */ } else { - exec_path = file_info.szPathName; + exec_path = file_path; } /* * Set up inherited stdin, stdout, stderr for child */ + GetStartupInfo(&startInfo); startInfo.dwFlags = STARTF_USESTDHANDLES; startInfo.lpReserved = 0; startInfo.cbReserved2 = 0; startInfo.lpReserved2 = 0; startInfo.lpTitle = shell_name ? shell_name : exec_path; startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1]; startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1]; startInfo.hStdError = (HANDLE)pproc->sv_stderr[1]; - if (as_user) { - if (envblk) free(envblk); - return -1; - } else { - DB (DB_JOBS, ("CreateProcess(%s,%s,...)\n", - exec_path ? exec_path : "NULL", - command_line ? command_line : "NULL")); - if (CreateProcess( + DB (DB_JOBS, ("CreateProcess(%s,%s,...)\n", + exec_path ? exec_path : "NULL", + command_line ? command_line : "NULL")); + if (CreateProcess( exec_path, command_line, NULL, 0, /* default security attributes for thread */ TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */ flags, envblk, 0, /* default starting directory */ &startInfo, &procInfo) == FALSE) { pproc->last_err = GetLastError(); pproc->lerrno = E_FORK; fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path ? exec_path : "NULL", command_line); + if (envblk) free(envblk); free( command_line ); + free( shell_name ); + free( file_path ); return(-1); - } } pproc->pid = (int)procInfo.hProcess; /* Close the thread handle -- we'll just watch the process */ CloseHandle(procInfo.hThread); /* Close the halves of the pipes we don't need */ - CloseHandle((HANDLE)pproc->sv_stdin[1]); - CloseHandle((HANDLE)pproc->sv_stdout[1]); - CloseHandle((HANDLE)pproc->sv_stderr[1]); - pproc->sv_stdin[1] = 0; - pproc->sv_stdout[1] = 0; - pproc->sv_stderr[1] = 0; + CloseHandle((HANDLE)pproc->sv_stdin[1]); + CloseHandle((HANDLE)pproc->sv_stdout[1]); + CloseHandle((HANDLE)pproc->sv_stderr[1]); + /* ?? why 0? should be INVALID_HANDLE_VALUE ? -- pa02 */ + pproc->sv_stdin[1] = 0; + pproc->sv_stdout[1] = 0; + pproc->sv_stderr[1] = 0; free( command_line ); if (envblk) free(envblk); + if (shell_name) free(shell_name); + if (file_path) free(file_path); pproc->lerrno=0; return 0; } - -static DWORD +//+pa02 this and following thread funcs must be stdcall for beginthreadex +static UINT __stdcall proc_stdin_thread(sub_process *pproc) { DWORD in_done; for (;;) { if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt, &in_done, NULL) == FALSE) @@ -599,13 +573,13 @@ _endthreadex(0); } } return 0; // for compiler warnings only.. not reached } -static DWORD +static UINT __stdcall proc_stdout_thread(sub_process *pproc) { DWORD bufsize = 1024; char c; DWORD nread; pproc->outp = malloc(bufsize); @@ -631,13 +605,13 @@ } pproc->outp[pproc->outcnt++] = c; } return 0; } -static DWORD +static UINT __stdcall proc_stderr_thread(sub_process *pproc) { DWORD bufsize = 1024; char c; DWORD nread; pproc->errp = malloc(bufsize); @@ -700,31 +674,25 @@ pproc->incnt = stdin_data_len; if (!pproc->inp) { stdin_eof = TRUE; CloseHandle((HANDLE)pproc->sv_stdin[0]); pproc->sv_stdin[0] = 0; } else { - tStdin = (HANDLE) _beginthreadex( 0, 1024, - (unsigned (__stdcall *) (void *))proc_stdin_thread, - pproc, 0, &dwStdin); + tStdin = (HANDLE) _beginthreadex( 0, 1024, proc_stdin_thread, pproc, 0, &dwStdin); if (tStdin == 0) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; } } /* * Assume child will produce stdout and stderr */ - tStdout = (HANDLE) _beginthreadex( 0, 1024, - (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0, - &dwStdout); - tStderr = (HANDLE) _beginthreadex( 0, 1024, - (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0, - &dwStderr); + tStdout = (HANDLE) _beginthreadex( 0, 1024, proc_stdout_thread, pproc, 0, &dwStdout); + tStderr = (HANDLE) _beginthreadex( 0, 1024, proc_stderr_thread, pproc, 0, &dwStderr); if (tStdout == 0 || tStderr == 0) { pproc->last_err = GetLastError(); pproc->lerrno = E_SCALL; goto done; @@ -764,12 +732,13 @@ ready_hand = wait_list[wait_return - WAIT_OBJECT_0]; if (ready_hand == tStdin) { CloseHandle((HANDLE)pproc->sv_stdin[0]); pproc->sv_stdin[0] = 0; + /* ?? why 0? should be INVALID_HANDLE_VALUE ? -- pa02 */ CloseHandle(tStdin); tStdin = 0; stdin_eof = TRUE; } else if (ready_hand == tStdout) {