1 /* [.vms]vms_popen.c -- substitute routines for missing pipe calls.
2
3 Copyright (C) 1991-1993, 1996 the Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19 #ifndef NO_VMS_PIPES
20
21 #include "awk.h" /* really "../awk.h" */
22 #include <stdio.h>
23
24 #ifndef PIPES_SIMULATED
25
26 FILE *
popen(const char * command,const char * mode)27 popen( const char *command, const char *mode )
28 {
29 fatal(" Cannot open pipe `%s' (not implemented)", command);
30 /* NOT REACHED */
31 return 0;
32 }
33
34 int
pclose(FILE * current)35 pclose( FILE *current )
36 {
37 fatal(" Internal error ('pclose' not implemented)");
38 /* NOT REACHED */
39 return -1;
40 }
41
42 int
fork(void)43 fork( void )
44 {
45 fatal(" Internal error ('fork' not implemented)");
46 /* NOT REACHED */
47 return -1;
48 }
49
50 #else /*PIPES_SIMULATED*/
51 /*
52 * Simulate pipes using temporary files; hope that the user
53 * doesn't expect pipe i/o to be interleaved with other i/o ;-}.
54 *
55 * This was initially based on the MSDOS version, but cannot
56 * use a static array to hold pipe info, because there's no
57 * fixed limit on the range of valid 'fileno's. Another
58 * difference is that redirection is handled using LIB$SPAWN
59 * rather than constructing a command for system() which uses
60 * '<' or '>'.
61 */
62 #include "vms.h"
63 #include <errno.h>
64 #include <lnmdef.h> /* logical name definitions */
65
66 #ifndef STDC_HEADERS
67 extern int strcmp P((const char*, const char *));
68 #endif
69 extern char *mktemp P((char *));
70
71 static void push_logicals P((void));
72 static void pop_logicals P((void));
73 static Itm *save_translation P((const Dsc *));
74 static void restore_translation P((const Dsc *, const Itm *));
75
76 typedef enum { unopened = 0, reading, writing } pipemode;
77 typedef struct pipe_info {
78 char *command;
79 char *name;
80 pipemode pmode;
81 } PIPE;
82 static PIPE *pipes;
83 static int pipes_lim = 0;
84
85 #define psize(n) ((n) * sizeof(PIPE))
86 #define expand_pipes(k) do { PIPE *new_p; \
87 int new_p_lim = ((k) / _NFILE + 1) * _NFILE; \
88 emalloc(new_p, PIPE *, psize(new_p_lim), "expand_pipes"); \
89 if (pipes_lim > 0) \
90 memcpy(new_p, pipes, psize(pipes_lim)), free(pipes); \
91 memset(new_p + psize(pipes_lim), 0, psize(new_p_lim - pipes_lim)); \
92 pipes = new_p, pipes_lim = new_p_lim; } while(0)
93
94 FILE *
popen(const char * command,const char * mode)95 popen( const char *command, const char *mode )
96 {
97 FILE *current;
98 char *name;
99 int cur;
100 pipemode curmode;
101
102 if (strcmp(mode, "r") == 0)
103 curmode = reading;
104 else if (strcmp(mode, "w") == 0)
105 curmode = writing;
106 else
107 return NULL;
108
109 /* make a name for the temporary file */
110 if ((name = mktemp(strdup("sys$scratch:gawk-pipe_XXXXXX.tmp"))) == 0)
111 return NULL;
112
113 if (curmode == reading) {
114 /* an input pipe reads a temporary file created by the command */
115 vms_execute(command, (char *)0, name); /* 'command >tempfile' */
116 }
117 if ((current = fopen(name, mode, "mbc=24", "mbf=2")) == NULL) {
118 free(name);
119 return NULL;
120 }
121 cur = fileno(current);
122 if (cur >= pipes_lim) expand_pipes(cur);
123 /* assert( cur >= 0 && cur < pipes_lim ); */
124 pipes[cur].name = name;
125 pipes[cur].pmode = curmode;
126 pipes[cur].command = strdup(command);
127 return current;
128 }
129
130 int
pclose(FILE * current)131 pclose( FILE *current )
132 {
133 int rval, cur = fileno(current);
134
135 /* assert( cur >= 0 && cur < pipes_lim ); */
136 if (pipes[cur].pmode == unopened)
137 return -1; /* should never happen */
138
139 rval = fclose(current); /* close temp file; if reading, we're done */
140 if (pipes[cur].pmode == writing) {
141 /* an output pipe feeds the temporary file to the other program */
142 rval = vms_execute(pipes[cur].command, pipes[cur].name, (char *)0);
143 }
144 /* clean up */
145 unlink(pipes[cur].name); /* get rid of the temporary file */
146 pipes[cur].pmode = unopened;
147 free(pipes[cur].name), pipes[cur].name = 0;
148 free(pipes[cur].command), pipes[cur].command = 0;
149 return rval;
150 }
151
152 /*
153 * Create a process and execute a command in it. This is essentially
154 * the same as system() but allows us to specify SYS$INPUT (stdin)
155 * and/or SYS$OUTPUT (stdout) for the process.
156 * [With more work it could truly simulate a pipe using mailboxes.]
157 */
158 int
vms_execute(const char * command,const char * input,const char * output)159 vms_execute( const char *command, const char *input, const char *output )
160 {
161 Dsc cmd, in, out, *in_p, *out_p;
162 U_Long sts, cmpltn_sts;
163
164 cmd.len = strlen(cmd.adr = (char *)command);
165 if (input)
166 in.len = strlen(in.adr = (char *)input), in_p = ∈
167 else
168 in_p = 0;
169 if (output)
170 out.len = strlen(out.adr = (char *)output), out_p = &out;
171 else
172 out_p = 0;
173
174 push_logicals(); /* guard against user-mode definitions of sys$Xput */
175 sts = lib$spawn(&cmd, in_p, out_p, (U_Long *)0,
176 (Dsc *)0, (U_Long *)0, &cmpltn_sts);
177 pop_logicals(); /* restore environment */
178
179 if (vmswork(sts) && vmsfail(cmpltn_sts)) sts = cmpltn_sts;
180 if (vmsfail(sts)) {
181 errno = EVMSERR, vaxc$errno = sts;
182 return -1;
183 } else
184 return 0;
185 }
186
187 /*----*
188 This rigmarole is to guard against interference from the current
189 environment. User-mode definitions of SYS$INPUT and/or SYS$OUTPUT
190 will interact with spawned subprocesses--including LIB$SPAWN with
191 explicit input and/or output arguments specified--if they were
192 defined without the 'CONFINED' attribute. The definitions created
193 in vms_args.c as part of command line I/O redirection happened to
194 fall into this category :-(, but even though that's been fixed,
195 there's still the possibility of the user doing something like
196 |$ define/user sys$output foo.out
197 prior to starting the program. Without ``/name_attr=confine'',
198 that will really screw up pipe simulation, so we've got to work-
199 around it here. This is true whether pipes are implemented via
200 mailboxes or temporary files, as long as lib$spawn() is being used.
201
202 push_logicals() calls save_translation() the first time it's
203 invoked; the latter allocates some memory to hold a full logical
204 name translation and uses $trnlnm to fill that in. Then if either
205 sys$input or sys$output has a user-mode, non-confined translation,
206 push_logicals() will delete the definition(s) using $dellnm.
207 After the spawned command has returned, pop_logicals() is called;
208 it calls restore_translation() for any deleted values; the latter
209 uses $crllnm or $crelog to recreate the original definition.
210
211 SYS$ERROR is currently ignored; perhaps it should receive the same
212 treatment...
213 *----*/
214
215 /* logical name table, and names of interest; these are all constant */
216 static const Descrip(lnmtable,"LNM$PROCESS_TABLE");
217 static const Descrip(sys_input,"SYS$INPUT");
218 static const Descrip(sys_output,"SYS$OUTPUT");
219 static const unsigned char acmode = PSL$C_USER; /* only care about user-mode */
220
221 /* macros for simplfying the code a bunch */
222 #define DelTrans(l) sys$dellnm(&lnmtable, (l), &acmode)
223 #define GetTrans(l,i) sys$trnlnm((U_Long *)0, &lnmtable, (l), &acmode, (i))
224 #define SetTrans(l,i) sys$crelnm((U_Long *)0, &lnmtable, (l), &acmode, (i))
225 /* itemlist manipulation macros; separate versions for aggregate and scalar */
226 #define SetItmA(i,c,p,r) ((i).code = (c), (i).len = sizeof (p),\
227 (i).buffer = (p), (i).retlen = (U_Short *)(r))
228 #define SetItmS(i,c,p) ((i).code = (c), (i).len = sizeof *(p),\
229 (i).buffer = (p), (i).retlen = (U_Short *)0)
230 #define EndItm0(i) ((i).code = (i).len = 0)
231
232 /* translate things once, then hold the results here for multiple re-use */
233 static Itm *input_definition, *output_definition;
234
235 static void
push_logicals(void)236 push_logicals( void ) /* deassign sys$input and/or sys$output */
237 {
238 static int init_done = 0;
239
240 if (!init_done) { /* do logical name lookups one-time only */
241 input_definition = save_translation(&sys_input);
242 output_definition = save_translation(&sys_output);
243 init_done = 1;
244 }
245 if (input_definition) DelTrans(&sys_input); /* kill sys$input */
246 if (output_definition) DelTrans(&sys_output); /* and sys$output */
247 }
248
249 static void
pop_logicals(void)250 pop_logicals( void ) /* redefine sys$input and/or sys$output */
251 {
252 if (input_definition) restore_translation(&sys_input, input_definition);
253 if (output_definition) restore_translation(&sys_output, output_definition);
254 }
255
256 static Itm *
save_translation(const Dsc * logname)257 save_translation( const Dsc *logname )
258 {
259 Itm trans[4], *itmlst;
260 long trans_attr, max_trans_indx; /* 0-based translation index count */
261 unsigned char trans_acmode; /* translation's access mode */
262 unsigned itmlst_size;
263 register int i, j;
264
265 itmlst = 0;
266 /* Want translation index count for non-confined, user-mode definition;
267 unfortunately, $trnlnm does not provide that much control. Try to
268 fetch several values of interest, then decide based on the result.
269 */
270 SetItmS(trans[0], LNM$_MAX_INDEX, &max_trans_indx), max_trans_indx = -1;
271 SetItmS(trans[1], LNM$_ACMODE, &trans_acmode), trans_acmode = 0;
272 SetItmS(trans[2], LNM$_ATTRIBUTES, &trans_attr), trans_attr = 0;
273 EndItm0(trans[3]);
274 if (vmswork(GetTrans(logname, trans)) && max_trans_indx >= 0
275 && trans_acmode == PSL$C_USER && !(trans_attr & LNM$M_CONFINE)) {
276 /* Now know that definition of interest exists;
277 allocate and initialize an item list and associated buffers;
278 use three entries for each translation.
279 */
280 itmlst_size = (3 * (max_trans_indx + 1) + 1) * sizeof(Itm);
281 emalloc(itmlst, Itm *, itmlst_size, "save_translation");
282 for (i = 0; i <= max_trans_indx; i++) {
283 struct def { U_Long indx, attr; U_Short len;
284 char str[LNM$C_NAMLENGTH], eos; } *wrk;
285 emalloc(wrk, struct def *, sizeof (struct def), "save_translation");
286 wrk->indx = (U_Long)i; /* this one's an input value for $trnlnm */
287 SetItmS(itmlst[3*i+0], LNM$_INDEX, &wrk->indx);
288 SetItmS(itmlst[3*i+1], LNM$_ATTRIBUTES, &wrk->attr), wrk->attr = 0;
289 SetItmA(itmlst[3*i+2], LNM$_STRING, &wrk->str, &wrk->len), wrk->len = 0;
290 }
291 EndItm0(itmlst[3*i]); /* assert( i == max_trans_indx+1 ); */
292 /* Time to perform full logical name translation,
293 then update item list for subsequent restoration.
294 If there are any holes [don't know whether that's possible]
295 collapse them out of the list; don't want them at restore time.
296 */
297 if (vmswork(GetTrans(logname, itmlst))) {
298 for (i = 0, j = -1; i <= max_trans_indx; i++) {
299 U_Long *attr_p;
300 attr_p = itmlst[3*i+1].buffer; /* copy (void *) to true type */
301 if (*attr_p & LNM$M_EXISTS) {
302 *attr_p &= ~LNM$M_EXISTS; /* must clear this bit */
303 if (++j < i) itmlst[3*j+0] = itmlst[3*i+0],
304 itmlst[3*j+1] = itmlst[3*i+1],
305 itmlst[3*j+2] = itmlst[3*i+2];
306 if (itmlst[3*j+2].retlen) { /* fixup buffer length */
307 itmlst[3*j+2].len = *itmlst[3*j+2].retlen;
308 itmlst[3*j+2].retlen = (U_Short *)0;
309 }
310 }
311 }
312 if (++j < i) EndItm0(itmlst[3*j]);
313 } else /* should never happen; tolerate potential memory leak */
314 free(itmlst), itmlst = 0; /*('wrk' buffer(s) will become lost)*/
315 }
316 return itmlst;
317 }
318
319 static void
restore_translation(const Dsc * logname,const Itm * itemlist)320 restore_translation( const Dsc *logname, const Itm *itemlist )
321 {
322 Dsc trans_val;
323 U_Long *attr_p;
324 # define LOG_PROCESS_TABLE 2 /* <obsolete> */
325 # define LOG_USERMODE PSL$C_USER
326
327 /* assert( itemlist[1].code == LNM$_ATTRIBUTES ); */
328 attr_p = itemlist[1].buffer; /* copy (void *) to (U_Long *) */
329 if (*attr_p & LNM$M_CRELOG) { /* check original creation method */
330 /* $crelog values can have only one translation;
331 so it'll be the first string entry in the itemlist.
332 */
333 /* assert( itemlist[2].code == LNM$_STRING ); */
334 trans_val.adr = itemlist[2].buffer;
335 trans_val.len = itemlist[2].len;
336 (void) sys$crelog(LOG_PROCESS_TABLE, logname, &trans_val, LOG_USERMODE);
337 } else {
338 /* $crelnm definition; itemlist could specify multiple translations,
339 but has already been setup properly for use as-is.
340 */
341 (void) SetTrans(logname, itemlist);
342 }
343 }
344
345 #endif /*PIPES_SIMULATED*/
346
347 #endif /*!NO_VMS_PIPES*/
348