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