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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 *
|
---|
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
|
---|
35 | pclose( FILE *current )
|
---|
36 | {
|
---|
37 | fatal(" Internal error ('pclose' not implemented)");
|
---|
38 | /* NOT REACHED */
|
---|
39 | return -1;
|
---|
40 | }
|
---|
41 |
|
---|
42 | int
|
---|
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 *
|
---|
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
|
---|
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
|
---|
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
|
---|
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
|
---|
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 *
|
---|
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
|
---|
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*/
|
---|