--- ray/src/common/win_popen.c 2003/10/27 10:19:31 1.2 +++ ray/src/common/win_popen.c 2004/03/28 20:33:12 1.3 @@ -1,5 +1,5 @@ #ifndef lint -static const char RCSid[] = "$Id: win_popen.c,v 1.2 2003/10/27 10:19:31 schorsch Exp $"; +static const char RCSid[] = "$Id: win_popen.c,v 1.3 2004/03/28 20:33:12 schorsch Exp $"; #endif /* Replacement for the posix popen() on Windows @@ -22,17 +22,22 @@ ignoring any | within. Quotes don't nest. #include /* _open_osfhandle() */ #include /* _O_RDONLY */ -#include "paths.h" #include "rtio.h" #include "rterror.h" #define RAD_MAX_PIPES 32 /* maximum number of pipes */ -static int parse_pipes(char*, char**, int); +#define R_MODE 1 +#define W_MODE 2 +#define A_MODE 3 + +static int parse_pipes(char *s, char *lines[], char **infn, char **outfn, +int *append, int maxl); static BOOL createPipes(HANDLE*, HANDLE*, HANDLE*, HANDLE*); static BOOL runChild(char*, char*, HANDLE, HANDLE, HANDLE); static void resetStdHandles(HANDLE stdoutOrig, HANDLE stdinOrig); +HANDLE newFile(char *fn, int mode); int @@ -54,30 +59,57 @@ char* type { char *execfile, *args; char *cmdlines[RAD_MAX_PIPES]; - char executable[512]; - int n, i; + char *infn = NULL, *outfn = NULL; + char executable[512], estr[512]; + int n, i, mode = 0, append = 0; int ncmds = 0; + FILE *inf = NULL; HANDLE stdoutRd = NULL, stdoutWr = NULL; HANDLE stdinRd = NULL, stdinWr = NULL; HANDLE stderrWr = NULL; HANDLE stdoutOrig, stdinOrig; + if (strchr(type, 'w')) { + mode = W_MODE; + } else if (strchr(type, 'r')) { + mode = R_MODE; + } else { + _snprintf(estr, sizeof(estr), + "Invalid mode \"%s\" for win_popen().", type); + eputs(estr); + return NULL; + } + stdoutOrig = GetStdHandle(STD_OUTPUT_HANDLE); stdinOrig = GetStdHandle(STD_INPUT_HANDLE); /* if we have a console, use it for error output */ stderrWr = GetStdHandle(STD_ERROR_HANDLE); - if((ncmds = parse_pipes(command, cmdlines, RAD_MAX_PIPES)) <= 0) { + ncmds = parse_pipes(command,cmdlines,&infn,&outfn,&append,RAD_MAX_PIPES); + if(ncmds <= 0) { eputs("Too many pipes or malformed command."); goto error; } + if (infn != NULL) { + stdoutRd = newFile(infn, mode); + } + for(n = 0; n < ncmds; ++n) { if(!createPipes(&stdoutRd, &stdoutWr, &stdinRd, &stdinWr)) { eputs("Error creating pipe"); goto error; } + if (outfn != NULL && n == ncmds - 1) { + CloseHandle(stdoutWr); + CloseHandle(stdoutRd); + stdoutWr = newFile(outfn, mode); + } + if (n == 0 && mode == W_MODE) { + /* create a standard C file pointer for writing to the input */ + inf = _fdopen(_open_osfhandle((long)stdinWr, _O_RDONLY), "w"); + } /* find the executable on the PATH */ args = nextword(executable, sizeof(executable), cmdlines[n]); if (args == NULL) { @@ -86,14 +118,12 @@ char* type } execfile = getpath(executable, getenv("PATH"), X_OK); if(execfile == NULL) { - char estr[512]; _snprintf(estr, sizeof(estr), "Can't find executable for \"%s\".", executable); eputs(estr); goto error; } if(!runChild(execfile, cmdlines[n], stdinRd, stdoutWr, stderrWr)) { - char estr[512]; _snprintf(estr, sizeof(estr), "Unable to execute executable \"%s\".", executable); eputs(estr); @@ -103,20 +133,64 @@ char* type or the final read will block */ CloseHandle(stdoutWr); } + /* clean up */ resetStdHandles(stdinOrig, stdoutOrig); for (i = 0; i < ncmds; i++) free(cmdlines[i]); + if (infn != NULL) free(infn); + if (outfn != NULL) free(outfn); - /* return a standard C file pointer for reading the output */ - return _fdopen(_open_osfhandle((long)stdoutRd, _O_RDONLY), "r"); + if (mode == R_MODE) { + /* return a standard C file pointer for reading the output */ + return _fdopen(_open_osfhandle((long)stdoutRd, _O_RDONLY), "r"); + } else if (mode == W_MODE) { + /* return a standard C file pointer for writing to the input */ + return inf; + } error: resetStdHandles(stdinOrig, stdoutOrig); for (i = 0; i < ncmds; i++) free(cmdlines[i]); + if (infn != NULL) free(infn); + if (outfn != NULL) free(outfn); return NULL; } +HANDLE +newFile(char *fn, int mode) +{ + SECURITY_ATTRIBUTES sAttr; + HANDLE fh; + + sAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + sAttr.bInheritHandle = TRUE; + sAttr.lpSecurityDescriptor = NULL; + + if (mode == R_MODE) { + fh = CreateFile(fn, + GENERIC_READ, + FILE_SHARE_READ, + &sAttr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } else if (mode == W_MODE || mode == A_MODE) { + fh = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_WRITE, + &sAttr, + mode==W_MODE?CREATE_ALWAYS:OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + } + if (fh == NULL) { + int e = GetLastError(); + } + return fh; +} + + static BOOL createPipes( /* establish matching pipes for a subprocess */ HANDLE* stdoutRd, @@ -187,20 +261,26 @@ HANDLE stdinOrig static int parse_pipes( /* split a shell command pipe sequence */ -char* s, -char* lines[], +char *s, +char *lines[], +char **infn, +char **outfn, +int *append, int maxl ) { int n = 0, i; char *se, *ws; + char *curs; int llen = 0; int quote = 0; + int last = 0; if (maxl<= 0) return 0; if (s == NULL) { return 0; } + *infn = *outfn = NULL; while (isspace(*s)) s++; /* leading whitespace */ se = s; while (n < maxl) { @@ -213,23 +293,50 @@ int maxl if (quote == '\'') quote = 0; else if (quote == 0) quote = '\''; break; + case '<': + case '>': case '|': case '\0': - if (*se == '|' && quote) + if (*se != '\0' && quote) break; llen = se - s; - lines[n] = malloc(llen+1); - strncpy(lines[n], s, llen); + curs = malloc(llen+1); + strncpy(curs, s, llen); /* remove unix style line-end escapes */ - while((ws = strstr(lines[n], "\\\n")) != NULL) + while((ws = strstr(curs, "\\\n")) != NULL) *ws = *(ws+1) = ' '; /* remove DOS style line-end escapes */ - while((ws = strstr(lines[n], "\\\r\n")) != NULL) + while((ws = strstr(curs, "\\\r\n")) != NULL) *ws = *(ws+1) = *(ws+2) = ' '; - while (isspace(*(lines[n] + llen - 1))) + while (isspace(*(curs + llen - 1))) llen--; /* trailing whitespace */ - lines[n][llen] = '\0'; - n++; + curs[llen] = '\0'; + + if (last == '|' || last == 0) { /* first or pipe */ + lines[n] = curs; + n++; + curs = NULL; + } else if (last == '<') { /* input file */ + if (*infn != NULL) { + eputs("win_popen(): ambiguous input redirection"); + goto error; + } + *infn = curs; + curs = NULL; + } else if (last == '>') { /* output file */ + if (*outfn != NULL) { + eputs("win_popen(): ambiguous out redirection"); + goto error; + } + *outfn = curs; + curs = NULL; + if (*se != '\0' && *se+1 == '>') { /* >> */ + *append = 1; + se++; + } + } + last = *se; + if (*se == '\0') return n; s = se + 1; while (isspace(*s)) s++; /* leading whitespace */ @@ -241,7 +348,11 @@ int maxl se++; } /* more jobs than slots */ +error: for (i = 0; i < n; i++) free(lines[i]); + if (*infn != NULL) free(*infn); + if (*outfn != NULL) free(*outfn); + if (curs != NULL) free(curs); return -1; }