You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
9.2 KiB
316 lines
9.2 KiB
/* libc replacement functions for win32. |
|
|
|
Copyright (C) 1998, 99 Free Software Foundation, Inc. |
|
|
|
This library is free software; you can redistribute it and/or |
|
modify it under the terms of the GNU Library General Public |
|
License as published by the Free Software Foundation; either |
|
version 2 of the License, or (at your option) any later version. |
|
|
|
This library is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
Library General Public License for more details. |
|
|
|
You should have received a copy of the GNU Library General Public |
|
License along with this library; if not, write to the Free Software |
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
|
|
|
/* |
|
This does make sense only under WIN32. |
|
Functions: |
|
- popen() rewritten |
|
- pclose() rewritten |
|
- stat() wrapper for _stat(), removing trailing slashes |
|
*/ |
|
|
|
#ifdef WIN32 |
|
|
|
#include <fcntl.h> |
|
#include <kpathsea/config.h> |
|
#include <kpathsea/c-pathch.h> |
|
#include <kpathsea/c-pathmx.h> |
|
#include <kpathsea/concatn.h> |
|
|
|
struct _popen_elt { |
|
FILE *f; /* File stream returned */ |
|
HANDLE hp; /* Handle of associated process */ |
|
struct _popen_elt *next; /* Next list element */ |
|
}; |
|
|
|
static struct _popen_elt _z = { NULL, 0, &_z }; |
|
static struct _popen_elt *_popen_list = &_z; |
|
|
|
FILE *popen P2C(const_string, cmd, const_string, mode) |
|
{ |
|
STARTUPINFO si; |
|
PROCESS_INFORMATION pi; |
|
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; |
|
FILE *f = NULL; |
|
int fno, i; |
|
HANDLE child_in, child_out; |
|
HANDLE father_in, father_out; |
|
HANDLE father_in_dup, father_out_dup; |
|
HANDLE current_in, current_out; |
|
HANDLE current_pid; |
|
char *env_path; |
|
int binary_mode; |
|
char *new_cmd, *app_name = NULL; |
|
char *p, *q; |
|
struct _popen_elt *new_process; |
|
char pname[PATH_MAX], *fp; |
|
char *suffixes[] = { ".bat", ".cmd", ".com", ".exe", NULL }; |
|
char **s; |
|
boolean go_on; |
|
|
|
/* We should look for the application name along the PATH, |
|
and decide to prepend "%COMSPEC% /c " or not to the command line. |
|
Do nothing for the moment. */ |
|
|
|
/* Another way to do that would be to try CreateProcess first without |
|
invoking cmd, and look at the error code. If it fails because of |
|
command not found, try to prepend "cmd /c" to the cmd line. |
|
*/ |
|
|
|
/* Look for the application name */ |
|
for (p = cmd; *p && isspace(*p); p++); |
|
if (*p == '"') { |
|
q = ++p; |
|
while(*p && *p != '"') p++; |
|
if (*p != '\0') { |
|
fprintf(stderr, "popen: malformed command (\" not terminated)\n"); |
|
return NULL; |
|
} |
|
} |
|
else |
|
for (q = p; *p && !isspace(*p); p++); |
|
/* q points to the beginning of appname, p to the last + 1 char */ |
|
if ((app_name = malloc(p - q + 1)) == NULL) { |
|
fprintf(stderr, "xpopen: malloc(app_name) failed.\n"); |
|
return NULL; |
|
} |
|
strncpy(app_name, q, p - q ); |
|
app_name[p - q] = '\0'; |
|
pname[0] = '\0'; |
|
#ifdef TRACE |
|
fprintf(stderr, "popen: app_name = %s\n", app_name); |
|
#endif |
|
|
|
env_path = getenv("PATH"); |
|
env_path = concat(".;", env_path); |
|
|
|
/* Looking for appname on the path */ |
|
for (s = suffixes, go_on = true; go_on; *s++) { |
|
if (SearchPath(env_path, /* Address of search path */ |
|
app_name, /* Address of filename */ |
|
*s, /* Address of extension */ |
|
PATH_MAX, /* Size of destination buffer */ |
|
pname, /* Address of destination buffer */ |
|
&fp) /* File part of app_name */ |
|
!= 0) { |
|
#ifdef TRACE |
|
fprintf(stderr, "%s found with suffix %s\nin %s\n", app_name, *s, pname); |
|
#endif |
|
new_cmd = xstrdup(cmd); |
|
free(app_name); |
|
app_name = xstrdup(pname); |
|
break; |
|
} |
|
go_on = (*s != NULL); |
|
} |
|
if (go_on == false) { |
|
/* the app_name was not found */ |
|
#ifdef TRACE |
|
fprintf(stderr, "%s not found, concatenating comspec\n", app_name); |
|
#endif |
|
new_cmd = concatn(getenv("COMSPEC"), " /c ", cmd, NULL); |
|
free(app_name); |
|
app_name = NULL; |
|
} |
|
else { |
|
} |
|
if (env_path) free(env_path); |
|
#ifdef TRACE |
|
fprintf(stderr, "popen: app_name = %s\n", app_name); |
|
fprintf(stderr, "popen: cmd_line = %s\n", new_cmd); |
|
#endif |
|
|
|
current_in = GetStdHandle(STD_INPUT_HANDLE); |
|
current_out = GetStdHandle(STD_OUTPUT_HANDLE); |
|
current_pid = GetCurrentProcess(); |
|
ZeroMemory( &si, sizeof(STARTUPINFO) ); |
|
si.cb = sizeof(STARTUPINFO); |
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; |
|
si.wShowWindow = SW_HIDE; |
|
|
|
if (strchr(mode, 'b')) |
|
binary_mode = _O_BINARY; |
|
else |
|
binary_mode = _O_TEXT; |
|
|
|
/* Opening the pipe for writing */ |
|
if (strchr(mode, 'w')) { |
|
binary_mode |= _O_WRONLY; |
|
if (CreatePipe(&child_in, &father_out, &sa, 0) == FALSE) { |
|
fprintf(stderr, "popen: error CreatePipe\n"); |
|
return NULL; |
|
} |
|
#if 0 |
|
if (SetStdHandle(STD_INPUT_HANDLE, child_in) == FALSE) { |
|
fprintf(stderr, "popen: error SetStdHandle child_in\n"); |
|
return NULL; |
|
} |
|
#endif |
|
si.hStdInput = child_in; |
|
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); |
|
si.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
|
|
|
if (DuplicateHandle(current_pid, father_out, |
|
current_pid, &father_out_dup, |
|
0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { |
|
fprintf(stderr, "popen: error DuplicateHandle father_out\n"); |
|
return NULL; |
|
} |
|
CloseHandle(father_out); |
|
fno = _open_osfhandle((long)father_out_dup, binary_mode); |
|
f = _fdopen(fno, mode); |
|
i = setvbuf( f, NULL, _IONBF, 0 ); |
|
} |
|
/* Opening the pipe for reading */ |
|
else if (strchr(mode, 'r')) { |
|
binary_mode |= _O_RDONLY; |
|
if (CreatePipe(&father_in, &child_out, &sa, 0) == FALSE) { |
|
fprintf(stderr, "popen: error CreatePipe\n"); |
|
return NULL; |
|
} |
|
#if 0 |
|
if (SetStdHandle(STD_OUTPUT_HANDLE, child_out) == FALSE) { |
|
fprintf(stderr, "popen: error SetStdHandle child_out\n"); |
|
return NULL; |
|
} |
|
#endif |
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); |
|
si.hStdOutput = child_out; |
|
si.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
|
if (DuplicateHandle(current_pid, father_in, |
|
current_pid, &father_in_dup, |
|
0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE) { |
|
fprintf(stderr, "popen: error DuplicateHandle father_in\n"); |
|
return NULL; |
|
} |
|
CloseHandle(father_in); |
|
fno = _open_osfhandle((long)father_in_dup, binary_mode); |
|
f = _fdopen(fno, mode); |
|
i = setvbuf( f, NULL, _IONBF, 0 ); |
|
} |
|
else { |
|
fprintf(stderr, "popen: invalid mode %s\n", mode); |
|
return NULL; |
|
} |
|
|
|
/* creating child process */ |
|
if (CreateProcess(app_name, /* pointer to name of executable module */ |
|
new_cmd, /* pointer to command line string */ |
|
NULL, /* pointer to process security attributes */ |
|
NULL, /* pointer to thread security attributes */ |
|
TRUE, /* handle inheritance flag */ |
|
0, /* creation flags, do not touch this again ! (16/06/98) */ |
|
NULL, /* pointer to environment */ |
|
NULL, /* pointer to current directory */ |
|
&si, /* pointer to STARTUPINFO */ |
|
&pi /* pointer to PROCESS_INFORMATION */ |
|
) == FALSE) { |
|
fprintf(stderr, "popen: CreateProcess %x\n", GetLastError()); |
|
return NULL; |
|
} |
|
|
|
#if 0 |
|
/* Restoring saved values for stdin/stdout */ |
|
if (SetStdHandle(STD_INPUT_HANDLE, current_in) == FALSE) |
|
fprintf(stderr, "popen: error re-redirecting Stdin\n"); |
|
if (SetStdHandle(STD_OUTPUT_HANDLE, current_out) == FALSE) |
|
fprintf(stderr, "popen: error re-redirecting Stdout\n"); |
|
#endif |
|
/* Only the process handle is needed */ |
|
if (CloseHandle(pi.hThread) == FALSE) { |
|
fprintf(stderr, "popen: error closing thread handle\n"); |
|
return NULL; |
|
} |
|
|
|
if (new_cmd) free(new_cmd); |
|
if (app_name) free(app_name); |
|
|
|
/* Add the pair (f, pi.hProcess) to the list */ |
|
if ((new_process = malloc(sizeof(struct _popen_elt))) == NULL) { |
|
fprintf (stderr, "popen: malloc(new_process) error\n"); |
|
return NULL; |
|
} |
|
/* Saving the FILE * pointer, access key for retrieving the process |
|
handle later on */ |
|
new_process->f = f; |
|
/* Closing the unnecessary part of the pipe */ |
|
if (strchr(mode, 'r')) { |
|
CloseHandle(child_out); |
|
} |
|
else if (strchr(mode, 'w')) { |
|
CloseHandle(child_in); |
|
} |
|
/* Saving the process handle */ |
|
new_process->hp = pi.hProcess; |
|
/* Linking it to the list of popen() processes */ |
|
new_process->next = _popen_list; |
|
_popen_list = new_process; |
|
|
|
return f; |
|
|
|
} |
|
|
|
int pclose P1C(FILE *, f) |
|
{ |
|
struct _popen_elt *p, *q; |
|
int exit_code; |
|
|
|
/* Look for f is the access key in the linked list */ |
|
for (q = NULL, p = _popen_list; |
|
p != &_z && p->f != f; |
|
q = p, p = p->next); |
|
|
|
if (p == &_z) { |
|
fprintf(stderr, "pclose: error, file not found."); |
|
return -1; |
|
} |
|
|
|
/* Closing the FILE pointer */ |
|
fclose(f); |
|
|
|
/* Waiting for the process to terminate */ |
|
if (WaitForSingleObject(p->hp, INFINITE) != WAIT_OBJECT_0) { |
|
fprintf(stderr, "pclose: error, process still active\n"); |
|
return -1; |
|
} |
|
|
|
/* retrieving the exit code */ |
|
if (GetExitCodeProcess(p->hp, &exit_code) == 0) { |
|
fprintf(stderr, "pclose: can't get process exit code\n"); |
|
return -1; |
|
} |
|
|
|
/* Closing the process handle, this will cause the system to |
|
remove the process from memory */ |
|
if (CloseHandle(p->hp) == FALSE) { |
|
fprintf(stderr, "pclose: error closing process handle\n"); |
|
return -1; |
|
} |
|
|
|
/* remove the elt from the list */ |
|
if (q != NULL) |
|
q->next = p->next; |
|
else |
|
_popen_list = p->next; |
|
free(p); |
|
|
|
return exit_code; |
|
} |
|
|
|
|
|
#endif
|
|
|