xref: /openbsd/gnu/usr.bin/cvs/os2/popen.c (revision 3d8817e4)
1 /* popen.c -- popen/pclose for OS/2. */
2 
3 /* Set to 0 for distribution.
4    Search for "DIAGNOSTIC" in the code to see what it's for. */
5 #define DIAGNOSTIC 0
6 
7 #include    <process.h>
8 
9 #include    <stdio.h>
10 #include    <stdlib.h>
11 #include    <sys/types.h>
12 #include    <sys/stat.h>
13 #include    <ctype.h>
14 #include    <string.h>
15 #include    <fcntl.h>
16 
17 #include    "config.h"
18 #include    "os2inc.h"
19 
20 #define LL_VAL ULONG
21 #define LL_KEY PID    /* also ULONG, really */
22 
23 #define STDIN       0
24 #define STDOUT      1
25 #define STDERR      2
26 
27 /* ********************************************************************* *
28  *                                                                       *
29  *   First, a little linked-list library to help keep track of pipes:    *
30  *                                                                       *
31  * ********************************************************************* */
32 
33 /* Map integer PID's onto integer termination codes. */
34 struct pid_list
35 {
36   HFILE pid;              /* key */
37   ULONG term_code;        /* val */
38   struct pid_list *next;  /* duh */
39 };
40 
41 static struct pid_list *pid_ll = (struct pid_list *) NULL;
42 
43 /* The ll_*() functions all make use of the global var `pid_ll'. */
44 
45 void
46 ll_insert (HFILE key, ULONG val)
47 {
48   struct pid_list *new;
49   new = (struct pid_list *) malloc (sizeof (*new));
50 
51   new->pid       = key;
52   new->term_code = val;
53   new->next      = pid_ll;
54 
55   pid_ll = new;
56 }
57 
58 
59 void
60 ll_delete (int key)
61 {
62   struct pid_list *this, *last;
63 
64   this = pid_ll;
65   last = (struct pid_list *) NULL;
66 
67   while (this)
68     {
69       if (this->pid == key)
70         {
71           /* Delete this node and leave. */
72           if (last)
73             last->next = this->next;
74           else
75             pid_ll = this->next;
76           free (this);
77           return;
78         }
79 
80       /* Else no match, so try the next one. */
81       last = this;
82       this = this->next;
83     }
84 }
85 
86 ULONG
87 ll_lookup (HFILE key)
88 {
89   struct pid_list *this = pid_ll;
90 
91   while (this)
92     {
93       if (this->pid == key)
94         return this->term_code;
95 
96       /* Else no match, so try the next one. */
97       this = this->next;
98     }
99 
100   /* Zero is special in this context anyway. */
101   return 0;
102 }
103 
104 #if DIAGNOSTIC
105 ULONG
106 ll_length ()
107 {
108   struct pid_list *this = pid_ll;
109   unsigned long int len;
110 
111   for (len = 0; this; len++)
112     this = this->next;
113 
114   return len;
115 }
116 
117 ULONG
118 ll_print ()
119 {
120   struct pid_list *this = pid_ll;
121   unsigned long int i;
122 
123   for (i = 0; this; i++)
124     {
125       printf ("pid_ll[%d] == (%5d --> %5d)\n",
126 			  i, this->pid, this->term_code);
127       this = this->next;
128     }
129 
130   if (i == 0)
131     printf ("No entries.\n");
132 
133   return i;
134 }
135 #endif /* DIAGNOSTIC */
136 
137 /* ********************************************************************* *
138  *                                                                       *
139  *       End of linked-list library, beginning of popen/pclose:          *
140  *                                                                       *
141  * ********************************************************************* */
142 
143 /*
144  *  Routine: popen
145  *  Returns: FILE pointer to pipe.
146  *  Action : Exec program connected via pipe, connect a FILE * to the
147  *           pipe and return it.
148  *  Params : Command - Program to run
149  *           Mode    - Mode to open pipe.  "r" implies pipe is connected
150  *                     to the programs stdout, "w" connects to stdin.
151  */
152 FILE *
153 popen (const char *Command, const char *Mode)
154 {
155     HFILE End1, End2, Std, Old1, Old2, Tmp;
156 
157     FILE *File;
158 
159     char    Fail[256],
160             *Args,
161             CmdLine[256],
162             *CmdExe;
163 
164     RESULTCODES
165             Result;
166 
167     int     Rc;
168 
169     if (DosCreatePipe (&End1, &End2, 4096))
170         return NULL;
171 
172     Std = (*Mode == 'w') ? STDIN : STDOUT ;
173     if (Std == STDIN)
174     {
175         Tmp = End1; End1 = End2; End2 = Tmp;
176     }
177 
178     Old1 = -1; /* save stdin or stdout */
179     DosDupHandle (Std, &Old1);
180     DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
181     Tmp = Std; /* redirect stdin or stdout */
182     DosDupHandle (End2, &Tmp);
183 
184     if (Std == 1)
185     {
186         Old2 = -1; /* save stderr */
187         DosDupHandle (STDERR, &Old2);
188         DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
189         Tmp = STDERR;
190         DosDupHandle (End2, &Tmp);
191     }
192 
193     DosClose (End2);
194     DosSetFHState (End1, OPEN_FLAGS_NOINHERIT);
195 
196     if ((CmdExe = getenv ("COMSPEC")) == NULL )
197         CmdExe = "cmd.exe";
198 
199     strcpy (CmdLine, CmdExe);
200     Args = CmdLine + strlen (CmdLine) + 1; /* skip zero */
201     strcpy (Args, "/c ");
202     strcat (Args, Command);
203     Args[strlen (Args) + 1] = '\0'; /* two zeroes */
204     Rc = DosExecPgm (Fail, sizeof (Fail), EXEC_ASYNCRESULT,
205                      (unsigned char *) CmdLine, 0, &Result,
206                      (unsigned char *) CmdExe);
207 
208     Tmp = Std; /* restore stdin or stdout */
209     DosDupHandle (Old1, &Tmp);
210     DosClose (Old1);
211 
212     if (Std == STDOUT)
213     {
214         Tmp = STDERR;   /* restore stderr */
215         DosDupHandle (Old2, &Tmp);
216         DosClose (Old2);
217     }
218 
219     if (Rc)
220     {
221         DosClose (End1);
222         return NULL;
223     }
224 
225 #ifdef __WATCOMC__
226     /* Watcom does not allow mixing operating system handles and
227      * C library handles, so we have to convert.
228      */
229     File = fdopen (_hdopen (End1, *Mode == 'w'? O_WRONLY : O_RDONLY), Mode);
230 #else
231     File = fdopen (End1, Mode);
232 #endif
233     ll_insert ((LL_KEY) End1, (LL_VAL) Result.codeTerminate);
234 
235     return File;
236 }
237 
238 
239 /*
240  *  Routine: popenRW
241  *  Returns: PID of child process
242  *  Action : Exec program connected via pipe, connect int fd's to
243  *           both the stdin and stdout of the process.
244  *  Params : Command - Program to run
245  *           Pipes   - Array of 2 ints to store the pipe descriptors
246  *                     Pipe[0] writes to child's stdin,
247  *                     Pipe[1] reads from child's stdout/stderr
248  */
249 int
250 popenRW (const char **argv, int *pipes)
251 {
252     HFILE Out1, Out2, In1, In2;
253     HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp;
254 
255     int pid;
256 
257     if (DosCreatePipe (&Out2, &Out1, 4096))
258         return FALSE;
259 
260     if (DosCreatePipe (&In1, &In2, 4096))
261     {
262         DosClose (Out1);
263         DosClose (Out2);
264         return FALSE;
265     }
266 
267     /* Save std{in,out,err} */
268     DosDupHandle (STDIN, &Old0);
269     DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
270     DosDupHandle (STDOUT, &Old1);
271     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
272     DosDupHandle (STDERR, &Old2);
273     DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
274 
275     /* Redirect std{in,out,err} */
276     Tmp = STDIN;
277     DosDupHandle (In1, &Tmp);
278     Tmp = STDOUT;
279     DosDupHandle (Out1, &Tmp);
280     Tmp = STDERR;
281     DosDupHandle (Out1, &Tmp);
282 
283     /* Close file handles not needed in child */
284 
285     DosClose (In1);
286     DosClose (Out1);
287     DosSetFHState (In2, OPEN_FLAGS_NOINHERIT);
288     DosSetFHState (Out2, OPEN_FLAGS_NOINHERIT);
289 
290     /* Spawn we now our hoary brood. */
291     pid = spawnvp (P_NOWAIT, argv[0], argv);
292 
293     /* Restore std{in,out,err} */
294     Tmp = STDIN;
295     DosDupHandle (Old0, &Tmp);
296     DosClose (Old0);
297     Tmp = STDOUT;
298     DosDupHandle (Old1, &Tmp);
299     DosClose (Old1);
300     Tmp = STDERR;
301     DosDupHandle (Old2, &Tmp);
302     DosClose (Old2);
303 
304     if(pid < 0)
305       {
306         DosClose (In2);
307         DosClose (Out2);
308         return -1;
309       }
310 
311     pipes[0] = In2;
312     _setmode (pipes[0], O_BINARY);
313     pipes[1] = Out2;
314     _setmode (pipes[1], O_BINARY);
315 
316     /* Save ID of write-to-child pipe for pclose() */
317     ll_insert ((LL_KEY) In2, (LL_VAL) pid);
318 
319     return pid;
320 }
321 
322 
323 /*
324  *  Routine: pclose
325  *  Returns: TRUE on success
326  *  Action : Close a pipe opened with popen();
327  *  Params : Pipe - pipe to close
328  */
329 int
330 pclose (FILE *Pipe)
331 {
332     RESULTCODES rc;
333     PID pid, pid1;
334     int Handle = fileno (Pipe);
335 
336     fclose (Pipe);
337 
338     rc.codeTerminate = -1;
339 
340     pid1 = (PID) ll_lookup ((LL_KEY) Handle);
341     /* if pid1 is zero, something's seriously wrong */
342     if (pid1 != 0)
343       {
344         DosWaitChild (DCWA_PROCESSTREE, DCWW_WAIT, &rc, &pid, pid1);
345         ll_delete ((LL_KEY) Handle);
346       }
347     return rc.codeTerminate == 0 ? rc.codeResult : -1;
348 }
349 
350 
351 #if DIAGNOSTIC
352 void
353 main ()
354 {
355   FILE *fp1, *fp2, *fp3;
356   int c;
357 
358   ll_print ();
359   fp1 = popen ("gcc --version", "r");
360   ll_print ();
361   fp2 = popen ("link386 /?", "r");
362   ll_print ();
363   fp3 = popen ("dir", "r");
364   ll_print ();
365 
366   while ((c = getc (fp1)) != EOF)
367     printf ("%c", c);
368 
369   while ((c = getc (fp2)) != EOF)
370     printf ("%c", c);
371 
372   while ((c = getc (fp3)) != EOF)
373     printf ("%c", c);
374 
375   pclose (fp1);
376   ll_print ();
377   pclose (fp2);
378   ll_print ();
379   pclose (fp3);
380   ll_print ();
381 
382   return;
383 }
384 
385 #endif /* DIAGNOSTIC */
386