This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
7c71ccc076ef0bf91b010de20756c3e6967ac844
[perl5.git] / os2 / popen.c
1 /*
2  * Pipe support for OS/2.
3  *
4  * WARNING:  I am guilty of chumminess with the runtime library because
5  *           I had no choice.  Details to follow.
6  *
7  */
8
9 #include "EXTERN.h"
10 #include "perl.h"
11 #define INCL_DOSPROCESS
12 #define INCL_DOSQUEUES
13 #define INCL_DOSMISC
14 #define INCL_DOSMEMMGR
15 #include <os2.h>
16
17 extern char **environ;
18
19 /* This mysterious array _osfile is used internally by the runtime
20  * library to remember assorted things about open file handles.
21  * The problem is that we are creating file handles via DosMakePipe,
22  * rather than via the runtime library.  This means that we have
23  * to fake the runtime library into thinking that the handles we've
24  * created are honest file handles.  So just before doing the fdopen,
25  * we poke in a magic value that fools the library functions into
26  * thinking that the handle is already open in text mode.
27  *
28  * This might not work for your compiler, so beware.
29  */
30 extern char _osfile[];
31
32 /* The maximum number of simultaneously open pipes.  We create an
33  * array of this size to record information about each open pipe.
34  */
35 #define MAXPIPES 5
36
37 /* Information to remember about each open pipe.
38  * The (FILE *) that popen returns is stored because that's the only
39  * way we can keep track of the pipes.
40  */
41 typedef struct pipeinfo {
42         FILE *pfId;             /* Which FILE we're talking about */
43         HFILE hfMe;             /* handle I should close at pclose */
44         PID pidChild;           /* Child's PID */
45         CHAR fReading;          /* A read or write pipe? */
46 } PIPEINFO, *PPIPEINFO;         /* pi and ppi */
47
48 static PIPEINFO PipeInfo[MAXPIPES];
49
50 FILE *mypopen(const char *command, const char *t)
51 {
52         typedef char *PSZZ;
53         PSZZ pszzPipeArgs = 0;
54         PSZZ pszzEnviron = 0;
55         PSZ *ppsz;
56         PSZ psz;
57         FILE *f;
58         HFILE hfMe, hfYou;
59         HFILE hf, hfSave;
60         RESULTCODES rc;
61         USHORT us;
62         PPIPEINFO ppi;
63         UINT i;
64
65         /* Validate pipe type */
66         if (*t != 'w' && *t != 'r') fatal("Unknown pipe type");
67
68         /* Room for another pipe? */
69         for (ppi = &PipeInfo[0]; ppi < &PipeInfo[MAXPIPES]; ppi++)
70                 if (ppi->pfId == 0) goto foundone;
71         return NULL;
72
73 foundone:
74
75         /* Make the pipe */
76         if (DosMakePipe(&hfMe, &hfYou, 0)) return NULL;
77
78         /* Build the environment.  First compute its length, then copy
79          * the environment strings into it.
80          */
81         i = 0;
82         for (ppsz = environ; *ppsz; ppsz++) i += 1 + strlen(*ppsz);
83         New(1204, pszzEnviron, 1+i, CHAR);
84
85         psz = pszzEnviron;
86         for (ppsz = environ; *ppsz; ppsz++) {
87                 strcpy(psz, *ppsz);
88                 psz += 1 + strlen(*ppsz);
89         }
90         *psz = 0;
91
92         /* Build the command string to execute.
93          * 6 = length(0 "/c " 0 0)
94          */
95         if (DosScanEnv("COMSPEC", &psz)) psz = "C:\\OS2\\cmd.exe";
96 #if 0
97         New(1203, pszzPipeArgs, strlen(psz) + strlen(command) + 6, CHAR);
98 #else
99 #define pszzPipeArgs buf
100 #endif
101         sprintf(pszzPipeArgs, "%s%c/c %s%c", psz, 0, command, 0);
102
103         /* Now some stuff that depends on what kind of pipe we're doing.
104          * We pull a sneaky trick; namely, that stdin = 0 = false,
105          * and stdout = 1 = true.  The end result is that if the
106          * pipe is a read pipe, then hf = 1; if it's a write pipe, then
107          * hf = 0 and Me and You are reversed.
108          */
109         if (!(hf = (*t == 'r'))) {
110                 /* The meaning of Me and You is reversed for write pipes. */
111                 hfSave = hfYou; hfYou = hfMe; hfMe = hfSave;
112         }
113
114         ppi->fReading = hf;
115
116         /* Trick number 1:  Fooling the runtime library into thinking
117          * that the file handle is legit.
118          *
119          * Trick number 2:  Don't let my handle go over to the child!
120          * Since the child never closes it (why should it?), I'd better
121          * make sure he never sees it in the first place.  Otherwise,
122          * we are in deadlock city.
123          */
124         _osfile[hfMe] = 0x81;           /* Danger, Will Robinson! */
125         if (!(ppi->pfId = fdopen(hfMe, t))) goto no_fdopen;
126         DosSetFHandState(hfMe, OPEN_FLAGS_NOINHERIT);
127
128         /* Save the original handle because we're going to diddle it */
129         hfSave = 0xFFFF;
130         if (DosDupHandle(hf, &hfSave)) goto no_dup_init;
131
132         /* Force the child's handle onto the stdio handle */
133         if (DosDupHandle(hfYou, &hf)) goto no_force_dup;
134         DosClose(hfYou);
135
136         /* Now run the guy servicing the pipe */
137         us = DosExecPgm(NULL, 0, EXEC_ASYNCRESULT, pszzPipeArgs, pszzEnviron,
138                         &rc, pszzPipeArgs);
139
140         /* Restore stdio handle, even if exec failed. */
141         DosDupHandle(hfSave, &hf); close(hfSave);
142
143         /* See if the exec succeeded. */
144         if (us) goto no_exec_pgm;
145
146         /* Remember the child's PID */
147         ppi->pidChild = rc.codeTerminate;
148
149         Safefree(pszzEnviron);
150
151         /* Phew. */
152         return ppi->pfId;
153
154         /* Here is where we clean up after an error. */
155 no_exec_pgm: ;
156 no_force_dup: close(hfSave);
157 no_dup_init: fclose(f);
158 no_fdopen:
159         DosClose(hfMe); DosClose(hfYou);
160         ppi->pfId = 0;
161         Safefree(pszzEnviron);
162         return NULL;
163 }
164
165
166 /* mypclose:  Closes the pipe associated with the file handle.
167  * After waiting for the child process to terminate, its return
168  * code is returned.  If the stream was not associated with a pipe,
169  * we return -1.
170  */
171 int
172 mypclose(FILE *f)
173 {
174         PPIPEINFO ppi;
175         RESULTCODES rc;
176         USHORT us;
177
178         /* Find the pipe this (FILE *) refers to */
179         for (ppi = &PipeInfo[0]; ppi < &PipeInfo[MAXPIPES]; ppi++)
180                 if (ppi->pfId == f) goto foundit;
181         return -1;
182 foundit:
183         if (ppi->fReading && !DosRead(fileno(f), &rc, 1, &us) && us > 0) {
184                 DosKillProcess(DKP_PROCESSTREE, ppi->pidChild);
185         }
186         fclose(f);
187         DosCwait(DCWA_PROCESS, DCWW_WAIT, &rc, &ppi->pidChild, ppi->pidChild);
188         ppi->pfId = 0;
189         return rc.codeResult;
190 }
191
192 /* pipe:  The only tricky thing is letting the runtime library know about
193  * our two new file descriptors.
194  */
195 int pipe(int filedes[2])
196 {
197         HFILE hfRead, hfWrite;
198         USHORT usResult;
199
200         usResult = DosMakePipe(&hfRead, &hfWrite, 0);
201         if (usResult) {
202                 /* Error 4 == ERROR_TOO_MANY_OPEN_FILES */
203                 errno = (usResult == 4) ? ENFILE : ENOMEM;
204                 return -1;
205         }
206         _osfile[hfRead] = _osfile[hfWrite] = 0x81;/* Danger, Will Robinson! */
207         filedes[0] = hfRead;
208         filedes[1] = hfWrite;
209         return 0;
210 }