home *** CD-ROM | disk | FTP | other *** search
- // hacked this out of the NT port of Perl, which is copylefted by
- // Clark Williams of Intergraph Corp.
-
- /*
- * Copyright (c) 1993, Intergraph Corporation
- *
- * You may distribute under the terms of either the GNU General Public
- * License or the Artistic License, as specified in the perl README file.
- *
- * Various Unix compatibility functions and NT specific functions.
- *
- * Some of this code was derived from the MSDOS port(s) and the OS/2 port.
- *
- * I pulled this out of the PERL NT port (the memory functions came
- * from PERL itself (v 4.036). Clark Williams of Intergraph wrote
- * the original of this, and I apologize to him in advance for hacking
- * it all up. :-)
- *
- */
-
-
- /* $Log: ntpipe.c,v $
- * Revision 1.4 1993/09/16 21:16:31 ESullivan
- * Added a function to get the pid from a process and a wait_for(pid)
- * function.
- *
- * Revision 1.3 1993/09/16 01:10:14 ESullivan
- * Added an $Id $ section.
- *
- * Revision 1.2 1993/09/07 02:09:30 ESullivan
- * added a $Log area.
- *
- */
-
- char ntpipever[] = "$Id: ntpipe.c,v 1.4 1993/09/16 21:16:31 ESullivan Exp ESullivan $";
- #include "nt.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <io.h>
- #include <fcntl.h>
- #include <process.h>
- #include <sys/stat.h>
- #include <assert.h>
- #include <errno.h>
-
-
- // popen stuff
-
- //
- // use these so I can remember which index is which
- //
-
- #define NtPipeRead 0 // index of pipe read descriptor
- #define NtPipeWrite 1 // index of pipe write descriptor
-
- #define NtPipeSize 1024 // size of pipe buffer
-
- #define MYPOPENSIZE 256 // size of book keeping structure
-
- struct {
- int inuse;
- int pid;
- FILE *pipe;
- } MyPopenRecord[MYPOPENSIZE];
-
- FILE *
- ntpopen (char *cmd, char *mode)
- {
- FILE *fp;
- int saved, reading;
- int pipemode;
- int pipes[2];
- int pid;
- int slot;
- static initialized = 0;
-
- //
- // if first time through, intialize our book keeping structure
- //
-
- if (!initialized++) {
- for (slot = 0; slot < MYPOPENSIZE; slot++)
- MyPopenRecord[slot].inuse = FALSE;
- }
-
- //
- // find a free popen slot
- //
-
- for (slot = 0; slot < MYPOPENSIZE && MyPopenRecord[slot].inuse; slot++)
- ;
-
- if (slot > MYPOPENSIZE) {
- return NULL;
- }
-
- //
- // Figure out what we\'re doing...
- //
-
- reading = (*mode == 'r') ? TRUE : FALSE;
- pipemode = (*(mode+1) == 'b') ? O_BINARY : O_TEXT;
-
- //
- // Now get a pipe
- //
-
- if (_pipe(pipes, NtPipeSize, pipemode) == -1) {
- return NULL;
- }
-
- if (reading) {
-
- //
- // we\'re reading from the pipe, so we must hook up the
- // write end of the pipe to the new processes stdout.
- // To do this we must save our file handle from stdout
- // by _dup\'ing it, then setting our stdout to be the pipe\'s
- // write descriptor. We must also make the write handle
- // inheritable so the new process can use it.
-
- if ((saved = _dup(fileno(stdout))) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- if (_dup2 (pipes[NtPipeWrite], fileno(stdout)) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
- else {
- //
- // must be writing to the new process. Do the opposite of
- // the above, i.e. hook up the processes stdin to the read
- // end of the pipe.
- //
-
- if ((saved = _dup(fileno(stdin))) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- if (_dup2(pipes[NtPipeRead], fileno(stdin)) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
-
- //
- // Start the new process. Must set _fileinfo to non-zero value
- // for file descriptors to be inherited. Reset after the process
- // is started.
- //
-
- if (NtHasRedirection(cmd)) {
- docmd:
- pid = spawnlpe(_P_NOWAIT, "cmd.exe", "/c", cmd, 0, environ);
- if (pid == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
- else {
- char **vec;
- int vecc = NtMakeCmdVector(cmd, &vec, FALSE);
-
- pid = spawnvpe (_P_NOWAIT, vec[0], vec, environ);
- if (pid == -1) {
- goto docmd;
- }
- Safefree (vec);
- }
-
- if (reading) {
-
- //
- // We need to close our instance of the inherited pipe write
- // handle now that it's been inherited so that it will actually close
- // when the child process ends.
- //
-
- if (_close(pipes[NtPipeWrite]) == -1) {
- _close(pipes[NtPipeRead]);
- return NULL;
- }
- if (_dup2 (saved, fileno(stdout)) == -1) {
- _close(pipes[NtPipeRead]);
- return NULL;
- }
- _close(saved);
-
- //
- // Now get a stream pointer to return to the calling program.
- //
-
- if ((fp = (FILE *) fdopen(pipes[NtPipeRead], mode)) == NULL) {
- return NULL;
- }
- }
- else {
-
- //
- // need to close our read end of the pipe so that it will go
- // away when the write end is closed.
- //
-
- if (_close(pipes[NtPipeRead]) == -1) {
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- if (_dup2 (saved, fileno(stdin)) == -1) {
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- _close(saved);
-
- //
- // Now get a stream pointer to return to the calling program.
- //
-
- if ((fp = (FILE *) fdopen(pipes[NtPipeWrite], mode)) == NULL) {
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
-
- //
- // do the book keeping
- //
-
- MyPopenRecord[slot].inuse = TRUE;
- MyPopenRecord[slot].pipe = fp;
- MyPopenRecord[slot].pid = pid;
-
- return fp;
- }
-
- FILE *
- ntpopenEx (char *cmd, char *mode, int *cpipes)
- {
- FILE *fp;
- int saved, reading;
- int pipemode;
- int pipes[2];
- int pid;
- int slot;
- static initialized = 0;
-
- //
- // if first time through, intialize our book keeping structure
- //
-
- if (!initialized++) {
- for (slot = 0; slot < MYPOPENSIZE; slot++)
- MyPopenRecord[slot].inuse = FALSE;
- }
-
- //
- // find a free popen slot
- //
-
- for (slot = 0; slot < MYPOPENSIZE && MyPopenRecord[slot].inuse; slot++)
- ;
-
- if (slot > MYPOPENSIZE) {
- return NULL;
- }
-
- //
- // Figure out what we\'re doing...
- //
-
- reading = (*mode == 'r') ? TRUE : FALSE;
- pipemode = (*(mode+1) == 'b') ? O_BINARY : O_TEXT;
-
- //
- // Now get a pipe
- //
-
- if (_pipe(pipes, NtPipeSize, pipemode) == -1) {
- return NULL;
- }
-
- cpipes[NtPipeRead] = pipes[NtPipeRead];
- cpipes[NtPipeWrite] = pipes[NtPipeWrite];
- if (reading) {
-
- //
- // we\'re reading from the pipe, so we must hook up the
- // write end of the pipe to the new processes stdout.
- // To do this we must save our file handle from stdout
- // by _dup\'ing it, then setting our stdout to be the pipe\'s
- // write descriptor. We must also make the write handle
- // inheritable so the new process can use it.
-
- if ((saved = _dup(fileno(stdout))) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- if (_dup2 (pipes[NtPipeWrite], fileno(stdout)) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
- else {
- //
- // must be writing to the new process. Do the opposite of
- // the above, i.e. hook up the processes stdin to the read
- // end of the pipe.
- //
-
- if ((saved = _dup(fileno(stdin))) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- if (_dup2(pipes[NtPipeRead], fileno(stdin)) == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
-
- //
- // Start the new process. Must set _fileinfo to non-zero value
- // for file descriptors to be inherited. Reset after the process
- // is started.
- //
-
- if (NtHasRedirection(cmd)) {
- docmd:
- pid = spawnlpe(_P_NOWAIT, "cmd.exe", "/c", cmd, 0, environ);
- if (pid == -1) {
- _close(pipes[NtPipeRead]);
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
- else {
- char **vec;
- int vecc = NtMakeCmdVector(cmd, &vec, FALSE);
-
- pid = spawnvpe (_P_NOWAIT, vec[0], vec, environ);
- if (pid == -1) {
- goto docmd;
- }
- Safefree (vec);
- }
-
- if (reading) {
-
-
- //
- // Now get a stream pointer to return to the calling program.
- //
-
- if ((fp = (FILE *) fdopen(pipes[NtPipeRead], mode)) == NULL) {
- return NULL;
- }
- }
- else {
-
-
- //
- // Now get a stream pointer to return to the calling program.
- //
-
- if ((fp = (FILE *) fdopen(pipes[NtPipeWrite], mode)) == NULL) {
- _close(pipes[NtPipeWrite]);
- return NULL;
- }
- }
-
- //
- // do the book keeping
- //
-
- MyPopenRecord[slot].inuse = TRUE;
- MyPopenRecord[slot].pipe = fp;
- MyPopenRecord[slot].pid = pid;
- cpipes[NtPipeRead] = pipes[NtPipeRead];
- cpipes[NtPipeWrite] = pipes[NtPipeWrite];
-
- return fp;
- }
-
- int
- ntpclose(FILE *fp)
- {
- int i;
- int exitcode;
-
- for (i = 0; i < MYPOPENSIZE; i++) {
- if (MyPopenRecord[i].inuse && MyPopenRecord[i].pipe == fp)
- break;
- }
- if (i >= MYPOPENSIZE) {
- fprintf(stderr,"Invalid file pointer passed to mypclose!\n");
- abort();
- }
-
- //
- // get the return status of the process
- //
-
- if (_cwait(&exitcode, MyPopenRecord[i].pid, WAIT_CHILD) == -1) {
- if (errno == ECHILD) {
- fprintf(stderr, "mypclose: nosuch child as pid %x\n",
- MyPopenRecord[i].pid);
- }
- }
-
- //
- // close the pipe
- //
-
- fclose(fp);
-
- //
- // free this slot
- //
-
- MyPopenRecord[i].inuse = FALSE;
-
- return exitcode;
- }
-
- /*
- * The following code is based on the do_exec and do_aexec functions
- * in file doio.c
- */
-
-
- typedef struct _NtCmdLineElement {
- struct _NtCmdLineElement *next, *prev;
- char *str;
- int len;
- int flags;
- } NtCmdLineElement;
-
- //
- // Possible values for flags
- //
-
- #define NTGLOB 0x1 // element contains a wildcard
- #define NTMALLOC 0x2 // string in element was malloc'ed
- #define NTSTRING 0x4 // element contains a quoted string
-
- NtCmdLineElement *NtCmdHead = NULL, *NtCmdTail = NULL;
-
- void
- NtFreeCmdLine(void)
- {
- NtCmdLineElement *ptr;
-
- while(NtCmdHead) {
- ptr = NtCmdHead;
- NtCmdHead = NtCmdHead->next;
- Safefree(ptr);
- }
- NtCmdHead = NtCmdTail = NULL;
- }
-
- //
- // This function expands wild card characters that were spotted
- // during the parse phase. The idea here is to call FindFirstFile and
- // FindNextFile with the wildcard pattern specified, and splice in the
- // resulting list of new names. If the wildcard pattern doesn\'t match
- // any existing files, just leave it in the list.
- //
-
- void
- NtCmdGlob (NtCmdLineElement *patt)
- {
- WIN32_FIND_DATA fd;
- HANDLE fh;
- char buffer[512];
- NtCmdLineElement *tmphead, *tmptail, *tmpcurr;
-
- strncpy(buffer, patt->str, patt->len);
- buffer[patt->len] = '\0';
- if ((fh = FindFirstFile (buffer, &fd)) == INVALID_HANDLE_VALUE) {
- return;
- }
- tmphead = tmptail = NULL;
- do {
- New (1301, tmpcurr, 1 , NtCmdLineElement);
- if (tmpcurr == NULL) {
- fprintf(stderr, "Out of Memory in globbing!\n");
- while (tmphead) {
- tmpcurr = tmphead;
- tmphead = tmphead->next;
- Safefree(tmpcurr->str);
- Safefree(tmpcurr);
- }
- return;
- }
- memset (tmpcurr, 0, sizeof(*tmpcurr));
- tmpcurr->len = strlen(fd.cFileName);
- New(1301, tmpcurr->str, tmpcurr->len+1, char);
- if (tmpcurr->str == NULL) {
- fprintf(stderr, "Out of Memory in globbing!\n");
- while (tmphead) {
- tmpcurr = tmphead;
- tmphead = tmphead->next;
- Safefree(tmpcurr->str);
- Safefree(tmpcurr);
- }
- return;
- }
- strcpy(tmpcurr->str, fd.cFileName);
- tmpcurr->flags |= NTMALLOC;
- if (tmptail) {
- tmptail->next = tmpcurr;
- tmpcurr->prev = tmptail;
- tmptail = tmpcurr;
- }
- else {
- tmptail = tmphead = tmpcurr;
- }
- } while(FindNextFile(fh, &fd));
-
- //
- // ok, now we\'ve got a list of files that matched the wildcard
- // specification. Put it in place of the pattern structure.
- //
-
- tmphead->prev = patt->prev;
- tmptail->next = patt->next;
-
- if (tmphead->prev)
- tmphead->prev->next = tmphead;
-
- if (tmptail->next)
- tmptail->next->prev = tmptail;
-
- //
- // Now get rid of the pattern structure
- //
-
- if (patt->flags & NTMALLOC)
- Safefree(patt->str);
- Safefree(patt);
- }
-
- //
- // Check a command string to determine if it has I/O redirection
- // characters that require it to be executed by a command interpreter
- //
-
- bool
- NtHasRedirection (char *cmd)
- {
- int inquote = 0;
- char quote = '\0';
- char *ptr ;
-
- //
- // Scan the string, looking for redirection (< or >) or pipe
- // characters (|) that are not in a quoted string
- //
-
- for (ptr = cmd; *ptr; ptr++) {
-
- switch (*ptr) {
-
- case '\'':
- case '\"':
- if (inquote) {
- if (quote == *ptr) {
- inquote = 0;
- quote = '\0';
- }
- }
- else {
- quote = *ptr;
- inquote++;
- }
- break;
-
- case '>':
- case '<':
-
- if (!inquote)
- return TRUE;
- }
- }
- return FALSE;
- }
-
-
- int
- NtMakeCmdVector (char *cmdline, char ***vec, int InputCmd)
- {
- int cmdlen = strlen(cmdline);
- int done, instring, globbing, quoted, len;
- int newline, need_free = 0, i;
- int elements, strsz;
- int slashes = 0;
- char *ptr, *base, *buffer;
- char **vptr;
- char quote;
- NtCmdLineElement *curr;
-
- //
- // just return if we don\'t have a command line
- //
-
- if (cmdlen == 0) {
- *vec = NULL;
- return 0;
- }
-
- //
- // strip trailing white space
- //
-
- ptr = cmdline+(cmdlen - 1);
- while(ptr >= cmdline && isspace(*ptr))
- --ptr;
- *++ptr = '\0';
-
- //
- // check for newlines and formfeeds. If we find any, make a new
- // command string that replaces them with escaped sequences (\n or \f)
- //
-
- for (ptr = cmdline, newline = 0; *ptr; ptr++) {
- if (*ptr == '\n' || *ptr == '\f')
- newline++;
- }
-
- if (newline) {
- New (1200, base, strlen(cmdline) + 1 + newline + slashes, char);
- if (base == NULL) {
- fprintf(stderr, "malloc failed!\n");
- return 0;
- }
- for (i = 0, ptr = base; (unsigned) i < strlen(cmdline); i++) {
- switch (cmdline[i]) {
- case '\n':
- *ptr++ = '\\';
- *ptr++ = 'n';
- break;
- default:
- *ptr++ = cmdline[i];
- }
- }
- *ptr = '\0';
- cmdline = base;
- need_free++;
- }
-
- //
- // Ok, parse the command line, building a list of CmdLineElements.
- // When we\'ve finished, and it\'s an input command (meaning that it\'s
- // the processes argv), we\'ll do globing and then build the argument
- // vector.
- // The outer loop does one interation for each element seen.
- // The inner loop does one interation for each character in the element.
- //
-
- for (done = 0, ptr = cmdline; *ptr;) {
-
- //
- // zap any leading whitespace
- //
-
- while(isspace(*ptr))
- ptr++;
- base = ptr;
-
- for (done = newline = globbing = instring = quoted = 0;
- *ptr && !done; ptr++) {
-
- //
- // Switch on the current character. We only care about the
- // white-space characters, the wild-card characters, and the
- // quote characters.
- //
-
- switch (*ptr) {
- case ' ':
- case '\t':
- #if 0
- case '/': // have to do this for NT/DOS option strings
-
- //
- // check to see if we\'re parsing an option switch
- //
-
- if (*ptr == '/' && base == ptr)
- continue;
- #endif
- //
- // if we\'re not in a string, then we\'re finished with this
- // element
- //
-
- if (!instring)
- done++;
- break;
-
- case '*':
- case '?':
-
- //
- // record the fact that this element has a wildcard character
- // N.B. Don\'t glob if inside a single quoted string
- //
-
- if (!(instring && quote == '\''))
- globbing++;
- break;
-
- case '\n':
-
- //
- // If this string contains a newline, mark it as such so
- // we can replace it with the two character sequence "\n"
- // (cmd.exe doesn\'t like raw newlines in strings...sigh).
- //
-
- newline++;
- break;
-
- case '\'':
- case '\"':
-
- //
- // if we\'re already in a string, see if this is the
- // terminating close-quote. If it is, we\'re finished with
- // the string, but not neccessarily with the element.
- // If we\'re not already in a string, start one.
- //
-
- if (instring) {
- if (quote == *ptr) {
- instring = 0;
- quote = '\0';
- }
- }
- else {
- instring++;
- quote = *ptr;
- quoted++;
- }
- break;
- }
- }
-
- //
- // need to back up ptr by one due to last increment of for loop
- // (if we got out by seeing white space)
- //
-
- if (*ptr)
- ptr--;
-
- //
- // when we get here, we\'ve got a pair of pointers to the element,
- // base and ptr. Base points to the start of the element while ptr
- // points to the character following the element.
- //
-
- New (1201, curr, 1, NtCmdLineElement);
- if (curr == NULL) {
- NtFreeCmdLine();
- fprintf(stderr, "Out of memory!!\n");
- *vec = NULL;
- return 0;
- }
- memset (curr, 0, sizeof(*curr));
-
- len = ptr - base;
-
- //
- // if it\'s an input vector element and it\'s enclosed by quotes,
- // we can remove them.
- //
-
- if (InputCmd &&
- ((base[0] == '\"' && base[len-1] == '\"') ||
- (base[0] == '\'' && base[len-1] == '\''))) {
- base++;
- len -= 2;
- }
-
- curr->str = base;
- curr->len = len;
- curr->flags |= (globbing ? NTGLOB : 0);
-
- //
- // Now put it in the list of elements
- //
- if (NtCmdTail) {
- NtCmdTail->next = curr;
- curr->prev = NtCmdTail;
- NtCmdTail = curr;
- }
- else {
- NtCmdHead = NtCmdTail = curr;
- }
- }
-
- if (InputCmd) {
-
- //
- // When we get here we\'ve finished parsing the command line. Now
- // we need to run the list, expanding any globbing patterns.
- //
-
- for(curr = NtCmdHead; curr; curr = curr->next) {
- if (curr->flags & NTGLOB) {
- NtCmdGlob(curr);
- }
- }
- }
-
- //
- // Almost done!
- // Count up the elements, then allocate space for a vector of pointers
- // (argv) and a string table for the elements.
- //
-
- for (elements = 0, strsz = 0, curr = NtCmdHead; curr; curr = curr->next) {
- elements++;
- strsz += (curr->len + 1);
- }
-
- len = (elements+1)*sizeof(char *) + strsz;
- New (1202, buffer, len, char);
- if (buffer == NULL) {
- fprintf(stderr, "Out of memory!!\n");
- NtFreeCmdLine();
- *vec = NULL;
- return 0;
- }
-
- memset (buffer, 0, len);
-
- //
- // make vptr point to the start of the buffer
- // and ptr point to the area we\'ll consider the string table.
- //
-
- vptr = (char **) buffer;
-
- ptr = buffer + (elements+1) * sizeof(char *);
-
- for (curr = NtCmdHead; curr; curr = curr->next) {
- strncpy (ptr, curr->str, curr->len);
- ptr[curr->len] = '\0';
- *vptr++ = ptr;
- ptr += curr->len + 1;
- }
- NtFreeCmdLine();
- *vec = (char **) buffer;
- return elements;
- }
-
-
- // added this function because I needed to get the pid
- int ntGetPid( FILE *fp)
- {
- int i;
-
- for (i = 0; i < MYPOPENSIZE; i++) {
- if (MyPopenRecord[i].inuse && MyPopenRecord[i].pipe == fp)
- break;
- }
- if (i >= MYPOPENSIZE) {
- fprintf(stderr,"Invalid file pointer passed to ntpclose!\n");
- abort();
- }
- else
- {
- return MyPopenRecord[i].pid;
- }
- }
-
- int ntwait_for( int pid)
- {
- int i;
- // int exitcode;
-
- for (i = 0; i < MYPOPENSIZE; i++) {
- if (MyPopenRecord[i].inuse && MyPopenRecord[i].pid == pid)
- break;
- }
- if (i >= MYPOPENSIZE) {
- fprintf(stderr,"Invalid pid passed to ntwait_for!\n");
- abort();
- }
- else {
- return ntpclose( MyPopenRecord[i].pipe);
- }
- }
-
-