summaryrefslogtreecommitdiff
path: root/vms/vms_popen.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-16 12:04:45 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-16 12:04:45 +0300
commitdbd583bd2b8a6dd40c622875a4e197360cb5aba7 (patch)
treed9fb7b6595cb44fefb4e32d70af9ac6d057af14a /vms/vms_popen.c
parentb8c608200919aa3f7b3fef289a7bece2d2961412 (diff)
downloadgawk-dbd583bd2b8a6dd40c622875a4e197360cb5aba7.tar.gz
Move to 2.13.3 (from 2.13.tar.gz - sigh).gawk-2.13.3
Diffstat (limited to 'vms/vms_popen.c')
-rw-r--r--vms/vms_popen.c199
1 files changed, 190 insertions, 9 deletions
diff --git a/vms/vms_popen.c b/vms/vms_popen.c
index f0eaa037..654364c4 100644
--- a/vms/vms_popen.c
+++ b/vms/vms_popen.c
@@ -10,8 +10,8 @@
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,7 +20,7 @@
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef NO_VMS_PIPES
@@ -59,21 +59,39 @@ fork()
* Simulate pipes using temporary files; hope that the user
* doesn't expect pipe i/o to be interleaved with other i/o ;-}.
*
- * This is essentially the same as the MSDOS version. The
+ * This was initially based on the MSDOS version, but cannot
+ * use a static array to hold pipe info, because there's no
+ * fixed limit on the range of valid 'fileno's. Another
* difference is that redirection is handled using LIB$SPAWN
* rather than constructing a command for system() which uses
* '<' or '>'.
*/
#include "vms.h"
#include <errno.h>
+#include <lnmdef.h> /* logical name definitions */
+
+static void push_logicals P((void));
+static void pop_logicals P((void));
+static Itm *save_translation P((const Dsc *));
+static void restore_translation P((const Dsc *, const Itm *));
typedef enum { unopened = 0, reading, writing } pipemode;
-static
-struct {
+typedef struct pipe_info {
char *command;
char *name;
pipemode pmode;
-} pipes[_NFILE];
+} PIPE;
+static PIPE *pipes;
+static int pipes_lim = 0;
+
+#define psize(n) ((n) * sizeof(PIPE))
+#define expand_pipes(k) do { PIPE *new_p; \
+ int new_p_lim = ((k) / _NFILE + 1) * _NFILE; \
+ emalloc(new_p, PIPE *, psize(new_p_lim), "expand_pipes"); \
+ if (pipes_lim > 0) \
+ memcpy(new_p, pipes, psize(pipes_lim)), free(pipes); \
+ memset(new_p + psize(pipes_lim), 0, psize(new_p_lim - pipes_lim)); \
+ pipes = new_p, pipes_lim = new_p_lim; } while(0)
FILE *
popen( const char *command, const char *mode )
@@ -91,18 +109,20 @@ popen( const char *command, const char *mode )
return NULL;
/* make a name for the temporary file */
- if ((name = mktemp(strdup("sys$scratch:pipe_XXXX.tmp"))) == 0)
+ if ((name = mktemp(strdup("sys$scratch:gawk-pipe_XXXXXX.tmp"))) == 0)
return NULL;
if (curmode == reading) {
/* an input pipe reads a temporary file created by the command */
vms_execute(command, (char *)0, name); /* 'command >tempfile' */
}
- if ((current = fopen(name, mode)) == NULL) {
+ if ((current = fopen(name, mode, "mbf=2")) == NULL) {
free(name);
return NULL;
}
cur = fileno(current);
+ if (cur >= pipes_lim) expand_pipes(cur);
+ /* assert( cur >= 0 && cur < pipes_lim ); */
pipes[cur].name = name;
pipes[cur].pmode = curmode;
pipes[cur].command = strdup(command);
@@ -114,6 +134,7 @@ pclose( FILE *current )
{
int rval, cur = fileno(current);
+ /* assert( cur >= 0 && cur < pipes_lim ); */
if (pipes[cur].pmode == unopened)
return -1; /* should never happen */
@@ -152,8 +173,10 @@ vms_execute( const char *command, const char *input, const char *output )
else
out_p = 0;
+ push_logicals(); /* guard against user-mode definitions of sys$Xput */
sts = LIB$SPAWN(&cmd, in_p, out_p, (long *)0,
(Dsc *)0, (u_long *)0, &cmpltn_sts);
+ pop_logicals(); /* restore environment */
if (vmswork(sts) && vmsfail(cmpltn_sts)) sts = cmpltn_sts;
if (vmsfail(sts)) {
@@ -163,6 +186,164 @@ vms_execute( const char *command, const char *input, const char *output )
return 0;
}
+/*----*
+ This rigmarole is to guard against interference from the current
+ environment. User-mode definitions of SYS$INPUT and/or SYS$OUTPUT
+ will interact with spawned subprocesses--including LIB$SPAWN with
+ explicit input and/or output arguments specified--if they were
+ defined without the 'CONFINED' attribute. The definitions created
+ in vms_args.c as part of command line I/O redirection happened to
+ fall into this category :-(, but even though that's been fixed,
+ there's still the possibility of the user doing something like
+ |$ define/user sys$output foo.out
+ prior to starting the program. Without ``/name_attr=confine'',
+ that will really screw up pipe simulation, so we've got to work-
+ around it here. This is true whether pipes are implemented via
+ mailboxes or temporary files, as long as lib$spawn() is being used.
+
+ push_logicals() calls save_translation() the first time it's
+ invoked; the latter allocates some memory to hold a full logical
+ name translation and uses $trnlnm to fill that in. Then if either
+ sys$input or sys$output has a user-mode, non-confined translation,
+ push_logicals() will delete the definition(s) using $dellnm.
+ After the spawned command has returned, pop_logicals() is called;
+ it calls restore_translation() for any deleted values; the latter
+ uses $crllnm or $crelog to recreate the original definition.
+
+ SYS$ERROR is currently ignored; perhaps it should receive the same
+ treatment...
+*----*/
+
+ /* logical name table, and names of interest; these are all constant */
+static const Descrip(lnmtable,"LNM$PROCESS_TABLE");
+static const Descrip(sys_input,"SYS$INPUT");
+static const Descrip(sys_output,"SYS$OUTPUT");
+static const unsigned char acmode = PSL$C_USER; /* only care about user-mode */
+
+ /* macros for simplfying the code a bunch */
+#define DelTrans(l) SYS$DELLNM(&lnmtable, (l), &acmode)
+#define GetTrans(l,i) SYS$TRNLNM((u_long *)0, &lnmtable, (l), &acmode, (i))
+#define SetTrans(l,i) SYS$CRELNM((u_long *)0, &lnmtable, (l), &acmode, (i))
+ /* itemlist manipulation macros; separate versions for aggregate and scalar */
+#define SetItmA(i,c,p,r) ((i).code = (c), (i).len = sizeof (p),\
+ (i).buffer = (p), (i).retlen = (u_short *)(r))
+#define SetItmS(i,c,p) ((i).code = (c), (i).len = sizeof *(p),\
+ (i).buffer = (p), (i).retlen = (u_short *)0)
+#define EndItm0(i) ((i).code = (i).len = 0)
+
+ /* translate things once, then hold the results here for multiple re-use */
+static Itm *input_definition, *output_definition;
+
+static void
+push_logicals( void ) /* deassign sys$input and/or sys$output */
+{
+ static int init_done = 0;
+
+ if (!init_done) { /* do logical name lookups one-time only */
+ input_definition = save_translation(&sys_input);
+ output_definition = save_translation(&sys_output);
+ init_done = 1;
+ }
+ if (input_definition) DelTrans(&sys_input); /* kill sys$input */
+ if (output_definition) DelTrans(&sys_output); /* and sys$output */
+}
+
+static void
+pop_logicals( void ) /* redefine sys$input and/or sys$output */
+{
+ if (input_definition) restore_translation(&sys_input, input_definition);
+ if (output_definition) restore_translation(&sys_output, output_definition);
+}
+
+static Itm *
+save_translation( const Dsc *logname )
+{
+ Itm trans[4], *itmlst;
+ long trans_attr, max_trans_indx; /* 0-based translation index count */
+ unsigned char trans_acmode; /* translation's access mode */
+ unsigned itmlst_size;
+ register int i, j;
+
+ itmlst = 0;
+ /* Want translation index count for non-confined, user-mode definition;
+ unfortunately, $trnlnm does not provide that much control. Try to
+ fetch several values of interest, then decide based on the result.
+ */
+ SetItmS(trans[0], LNM$_MAX_INDEX, &max_trans_indx), max_trans_indx = -1;
+ SetItmS(trans[1], LNM$_ACMODE, &trans_acmode), trans_acmode = 0;
+ SetItmS(trans[2], LNM$_ATTRIBUTES, &trans_attr), trans_attr = 0;
+ EndItm0(trans[3]);
+ if (vmswork(GetTrans(logname, trans)) && max_trans_indx >= 0
+ && trans_acmode == PSL$C_USER && !(trans_attr & LNM$M_CONFINE)) {
+ /* Now know that definition of interest exists;
+ allocate and initialize an item list and associated buffers;
+ use three entries for each translation.
+ */
+ itmlst_size = (3 * (max_trans_indx + 1) + 1) * sizeof(Itm);
+ emalloc(itmlst, Itm *, itmlst_size, "save_translation");
+ for (i = 0; i <= max_trans_indx; i++) {
+ struct def { u_long indx, attr; u_short len;
+ char str[LNM$C_NAMLENGTH], eos; } *wrk;
+ emalloc(wrk, struct def *, sizeof (struct def), "save_translation");
+ wrk->indx = (u_long)i; /* this one's an input value for $trnlnm */
+ SetItmS(itmlst[3*i+0], LNM$_INDEX, &wrk->indx);
+ SetItmS(itmlst[3*i+1], LNM$_ATTRIBUTES, &wrk->attr), wrk->attr = 0;
+ SetItmA(itmlst[3*i+2], LNM$_STRING, &wrk->str, &wrk->len), wrk->len = 0;
+ }
+ EndItm0(itmlst[3*i]); /* assert( i == max_trans_indx+1 ); */
+ /* Time to perform full logical name translation,
+ then update item list for subsequent restoration.
+ If there are any holes [don't know whether that's possible]
+ collapse them out of the list; don't want them at restore time.
+ */
+ if (vmswork(GetTrans(logname, itmlst))) {
+ for (i = 0, j = -1; i <= max_trans_indx; i++) {
+ u_long *attr_p;
+ attr_p = itmlst[3*i+1].buffer; /* copy (void *) to true type */
+ if (*attr_p & LNM$M_EXISTS) {
+ *attr_p &= ~LNM$M_EXISTS; /* must clear this bit */
+ if (++j < i) itmlst[3*j+0] = itmlst[3*i+0],
+ itmlst[3*j+1] = itmlst[3*i+1],
+ itmlst[3*j+2] = itmlst[3*i+2];
+ if (itmlst[3*j+2].retlen) { /* fixup buffer length */
+ itmlst[3*j+2].len = *itmlst[3*j+2].retlen;
+ itmlst[3*j+2].retlen = (u_short *)0;
+ }
+ }
+ }
+ if (++j < i) EndItm0(itmlst[3*j]);
+ } else /* should never happen; tolerate potential memory leak */
+ free(itmlst), itmlst = 0; /*('wrk' buffer(s) will become lost)*/
+ }
+ return itmlst;
+}
+
+static void
+restore_translation( const Dsc *logname, const Itm *itemlist )
+{
+ Dsc trans_val;
+ u_long *attr_p;
+# define LOG_PROCESS_TABLE 2 /* <obsolete> */
+# define LOG_USERMODE PSL$C_USER
+
+ /* assert( itemlist[1].code == LNM$_ATTRIBUTES ); */
+ attr_p = itemlist[1].buffer; /* copy (void *) to (u_long *) */
+ if (*attr_p & LNM$M_CRELOG) { /* check original creation method */
+ /* $crelog values can have only one translation;
+ so it'll be the first string entry in the itemlist.
+ */
+ /* assert( itemlist[2].code == LNM$_STRING ); */
+ trans_val.adr = itemlist[2].buffer;
+ trans_val.len = itemlist[2].len;
+ (void) SYS$CRELOG(LOG_PROCESS_TABLE, logname, &trans_val, LOG_USERMODE);
+ } else {
+ /* $crelnm definition; itemlist could specify multiple translations,
+ but has already been setup properly for use as-is.
+ */
+ (void) SetTrans(logname, itemlist);
+ }
+}
+
#endif /* PIPES_SIMULATED */
#endif /*!NO_VMS_PIPES*/