1 /* popen/pclose:
2  *
3  * simple MS-DOS piping scheme to imitate UNIX pipes
4  */
5 
6 #include "EXTERN.h"
7 #include "common.h"
8 #include <setjmp.h>
9 #include "util2.h"
10 #include "util3.h"
11 
12 #ifndef	_NFILE
13 # define _NFILE 5			/* Number of open files */
14 #endif	_NFILE
15 
16 #define READIT	1			/* Read pipe */
17 #define WRITEIT	2			/* Write pipe */
18 
19 static char* prgname[_NFILE];		/* program name if write pipe */
20 static int pipetype[_NFILE];		/* 1=read 2=write */
21 static char* pipename[_NFILE];		/* pipe file name */
22 
23 /*
24  *------------------------------------------------------------------------
25  * run: Execute command via SHELL or COMSPEC
26  *------------------------------------------------------------------------
27  */
28 
29 static int
run(char * command)30 run(char* command)
31 {
32     jmp_buf panic;			/* How to recover from errors */
33     char* shell;			/* Command processor */
34     char* s = NULL;			/* Holds the command */
35     int s_is_malloced = 0;		/* True if need to free 's' */
36     static char* command_com = "COMMAND.COM";
37     int status;				/* Return codes */
38     char* shellpath;			/* Full command processor path */
39     char* bp;				/* Generic string pointer */
40     static char dash_c[] = "/c";
41 
42     s = savestr(command);
43     /* Determine the command processor */
44     if ((shell = getenv("SHELL")) == NULL
45      && (shell = getenv("COMSPEC")) == NULL)
46 	shell = command_com;
47     strupr(shell);
48     shellpath = shell;
49     /* Strip off any leading backslash directories */
50     shell = rindex(shellpath, '\\');
51     if (shell != NULL)
52 	shell++;
53     else
54 	shell = shellpath;
55     /* Strip off any leading slash directories */
56     bp = rindex(shell, '/');
57     if (bp != NULL)
58 	shell = ++bp;
59     if (strcmp(shell, command_com) != 0) {
60 	/* MKS Shell needs quoted argument */
61 	char* bp;
62 	bp = s = safemalloc(strlen(command) + 3);
63 	*bp++ = '\'';
64 	while ((*bp++ = *command++) != '\0') ;
65 	*(bp - 1) = '\'';
66 	*bp = '\0';
67 	s_is_malloced = 1;
68     } else
69 	s = command;
70     /* Run the program */
71     status = spawnl(P_WAIT, shellpath, shell, dash_c, s, NULL);
72     if (s_is_malloced)
73 	free(s);
74     return status;
75 }
76 
77 /*
78  *------------------------------------------------------------------------
79  * uniquepipe: returns a unique file name
80  *------------------------------------------------------------------------
81  */
82 
83 static char*
uniquepipe(void)84 uniquepipe(void)
85 {
86     static char name[14];
87     static short int num = 0;
88     (void) sprintf(name, "pipe%04d.tmp", num++);
89     return name;
90 }
91 
92 /*
93  *------------------------------------------------------------------------
94  * resetpipe: Private routine to cancel a pipe
95  *------------------------------------------------------------------------
96  */
97 static void
resetpipe(int fd)98 resetpipe(int fd)
99 {
100     char* bp;
101     if (fd >= 0 && fd < _NFILE) {
102 	pipetype[fd] = 0;
103 	if ((bp = pipename[fd]) != NULL) {
104 	    (void) unlink(bp);
105 	    free(bp);
106 	    pipename[fd] = NULL;
107 	}
108 	if ((bp = prgname[fd]) != NULL) {
109 	    free(bp);
110 	    prgname[fd] = NULL;
111 	}
112     }
113 }
114 
115 /*
116  *------------------------------------------------------------------------
117  * popen: open a pipe
118  *------------------------------------------------------------------------
119  */
popen(prg,type)120 FILE* popen(prg, type)
121 char* prg;			/* The command to be run */
122 char* type;			/* "w" or "r" */
123 {
124     FILE* p = NULL;		/* Where we open the pipe */
125     int ostdin;			/* Where our stdin is now */
126     int pipefd = -1;		/* fileno(p) -- for convenience */
127     char* tmpfile;		/* Holds name of pipe file */
128     jmp_buf panic;		/* Where to go if there's an error */
129     int lineno;			/* Line number where panic happened */
130 
131     /* Get a unique pipe file name */
132     tmpfile = filexp("%Y/");
133     strcat(tmpfile, uniquepipe());
134     if ((lineno = setjmp(panic)) != 0) {
135 	/* An error has occurred, so clean up */
136 	int E = errno;
137 	if (p != NULL)
138 	    (void) fclose(p);
139 	resetpipe(pipefd);
140 	errno = E;
141 	lineno = lineno;
142 	return NULL;
143     }
144     if (strcmp(type, "w") == 0) {
145 	/* for write style pipe, pclose handles program execution */
146 	if ((p = fopen(tmpfile, "w")) != NULL) {
147 	    pipefd = fileno(p);
148 	    pipetype[pipefd] = WRITEIT;
149 	    pipename[pipefd] = savestr(tmpfile);
150 	    prgname[pipefd]  = savestr(prg);
151 	}
152     } else if (strcmp(type, "r") == 0) {
153 	/* read pipe must create tmp file, set up stdout to point to the temp
154 	 * file, and run the program.  note that if the pipe file cannot be
155 	 * opened, it'll return a condition indicating pipe failure, which is
156 	 * fine. */
157 	if ((p = fopen(tmpfile, "w")) != NULL) {
158 	    int ostdout;
159 	    pipefd = fileno(p);
160 	    pipetype[pipefd]= READIT;
161 	    pipename[pipefd] = savestr(tmpfile);
162 	    /* Redirect stdin for the new command */
163 	    ostdout = dup(fileno(stdout));
164 	    if (dup2(fileno(stdout), pipefd) < 0) {
165 		int E = errno;
166 		(void) dup2(fileno(stdout), ostdout);
167 		errno = E;
168 		longjmp(panic, __LINE__);
169 	    }
170 	    if (run(prg) != 0)
171 		longjmp(panic, __LINE__);
172 	    if (dup2(fileno(stdout), ostdout) < 0)
173 		longjmp(panic, __LINE__);
174 	    if (fclose(p) < 0)
175 		longjmp(panic, __LINE__);
176 	    if ((p = fopen(tmpfile, "r")) == NULL)
177 		longjmp(panic, __LINE__);
178 	}
179     } else {
180 	/* screwy call or unsupported type */
181 	errno = EINVFNC;
182 	longjmp(panic, __LINE__);
183     }
184     return p;
185 }
186 
187 /* close a pipe */
188 int
pclose(p)189 pclose(p)
190 FILE* p;
191 {
192     int pipefd = -1;		/* Fildes where pipe is opened */
193     int ostdout;		/* Where our stdout points now */
194     int ostdin;			/* Where our stdin points now */
195     jmp_buf panic;		/* Context to return to if error */
196     int lineno;			/* Line number where panic happened */
197     if ((lineno = setjmp(panic)) != 0) {
198 	/* An error has occurred, so clean up and return */
199 	int E = errno;
200 	if (p != NULL)
201 	    (void) fclose(p);
202 	resetpipe(pipefd);
203 	errno = E;
204 	lineno = lineno;
205 	return -1;
206     }
207     pipefd = fileno(p);
208     if (fclose(p) < 0)
209 	longjmp(panic, __LINE__);
210     switch (pipetype[pipefd]) {
211     case WRITEIT:
212 	/* open the temp file again as read, redirect stdin from that
213 	 * file, run the program, then clean up. */
214 	if ((p = fopen(pipename[pipefd],"r")) == NULL)
215 	    longjmp(panic, __LINE__);
216 	ostdin = dup(fileno(stdin));
217 	if (dup2(fileno(stdin), fileno(p)) < 0)
218 	    longjmp(panic, __LINE__);
219 	if (run(prgname[pipefd]) != 0)
220 	    longjmp(panic, __LINE__);
221 	if (dup2(fileno(stdin), ostdin) < 0)
222 	    longjmp(panic, __LINE__);
223 	if (fclose(p) < 0)
224 	    longjmp(panic, __LINE__);
225 	resetpipe(pipefd);
226 	break;
227     case READIT:
228 	/* close the temp file and remove it */
229 	resetpipe(pipefd);
230 	break;
231     default:
232 	errno = EINVFNC;
233 	longjmp(panic, __LINE__);
234 	/*NOTREACHED*/
235     }
236     return 0;
237 }
238