Blame vms/vms_popen.c

Packit Service f629e6
/* [.vms]vms_popen.c -- substitute routines for missing pipe calls.
Packit Service f629e6
Packit Service f629e6
   Copyright (C) 1991-1993, 1996, 2010, 2011, 2014
Packit Service f629e6
   the Free Software Foundation, Inc.
Packit Service f629e6
Packit Service f629e6
   This program is free software; you can redistribute it and/or modify
Packit Service f629e6
   it under the terms of the GNU General Public License as published by
Packit Service f629e6
   the Free Software Foundation; either version 2, or (at your option)
Packit Service f629e6
   any later version.
Packit Service f629e6
Packit Service f629e6
   This program is distributed in the hope that it will be useful,
Packit Service f629e6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service f629e6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service f629e6
   GNU General Public License for more details.
Packit Service f629e6
Packit Service f629e6
   You should have received a copy of the GNU General Public License
Packit Service f629e6
   along with this program; if not, write to the Free Software Foundation,
Packit Service f629e6
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
Packit Service f629e6
Packit Service f629e6
#ifndef NO_VMS_PIPES
Packit Service f629e6
Packit Service f629e6
#include "awk.h"	/* really "../awk.h" */
Packit Service f629e6
#include <stdio.h>
Packit Service f629e6
Packit Service f629e6
#ifndef PIPES_SIMULATED
Packit Service f629e6
Packit Service f629e6
FILE *
Packit Service f629e6
popen( const char *command, const char *mode )
Packit Service f629e6
{
Packit Service f629e6
    fatal(" Cannot open pipe `%s' (not implemented)", command);
Packit Service f629e6
    /* NOT REACHED */
Packit Service f629e6
    return 0;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
int
Packit Service f629e6
pclose( FILE *current )
Packit Service f629e6
{
Packit Service f629e6
    fatal(" Internal error ('pclose' not implemented)");
Packit Service f629e6
    /* NOT REACHED */
Packit Service f629e6
    return -1;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
int
Packit Service f629e6
fork( void )
Packit Service f629e6
{
Packit Service f629e6
    fatal(" Internal error ('fork' not implemented)");
Packit Service f629e6
    /* NOT REACHED */
Packit Service f629e6
    return -1;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
#else	/*PIPES_SIMULATED*/
Packit Service f629e6
	/*
Packit Service f629e6
	 * Simulate pipes using temporary files; hope that the user
Packit Service f629e6
	 * doesn't expect pipe i/o to be interleaved with other i/o ;-}.
Packit Service f629e6
	 *
Packit Service f629e6
	 * This was initially based on the MSDOS version, but cannot
Packit Service f629e6
	 * use a static array to hold pipe info, because there's no
Packit Service f629e6
	 * fixed limit on the range of valid 'fileno's.  Another
Packit Service f629e6
	 * difference is that redirection is handled using LIB$SPAWN
Packit Service f629e6
	 * rather than constructing a command for system() which uses
Packit Service f629e6
	 * '<' or '>'.
Packit Service f629e6
	 */
Packit Service f629e6
#include "vms.h"
Packit Service f629e6
#include <errno.h>
Packit Service f629e6
#include <lnmdef.h>	/* logical name definitions */
Packit Service f629e6
Packit Service f629e6
#ifndef STDC_HEADERS
Packit Service f629e6
extern int strcmp(const char*, const char *);
Packit Service f629e6
#endif
Packit Service f629e6
extern char *mktemp(char *);
Packit Service f629e6
Packit Service f629e6
static void push_logicals(void);
Packit Service f629e6
static void pop_logicals(void);
Packit Service f629e6
static Itm *save_translation(const struct dsc$descriptor_s *);
Packit Service f629e6
static void restore_translation(const struct dsc$descriptor_s *, const Itm *);
Packit Service f629e6
Packit Service f629e6
typedef enum { unopened = 0, reading, writing } pipemode;
Packit Service f629e6
typedef struct pipe_info {
Packit Service f629e6
    char *command;
Packit Service f629e6
    char *name;
Packit Service f629e6
    pipemode pmode;
Packit Service f629e6
} PIPE;
Packit Service f629e6
static PIPE *pipes;
Packit Service f629e6
static int pipes_lim = 0;
Packit Service f629e6
Packit Service f629e6
#define psize(n) ((n) * sizeof(PIPE))
Packit Service f629e6
#define expand_pipes(k) do {  PIPE *new_p; \
Packit Service f629e6
	int new_p_lim = ((k) / _NFILE + 1) * _NFILE; \
Packit Service f629e6
	emalloc(new_p, PIPE *, psize(new_p_lim), "expand_pipes"); \
Packit Service f629e6
	if (pipes_lim > 0) \
Packit Service f629e6
		memcpy(new_p, pipes, psize(pipes_lim)),  free(pipes); \
Packit Service f629e6
	memset(new_p + psize(pipes_lim), 0, psize(new_p_lim - pipes_lim)); \
Packit Service f629e6
	pipes = new_p,  pipes_lim = new_p_lim;  } while(0)
Packit Service f629e6
Packit Service f629e6
FILE *
Packit Service f629e6
popen( const char *command, const char *mode )
Packit Service f629e6
{
Packit Service f629e6
    FILE *current;
Packit Service f629e6
    char *name;
Packit Service f629e6
    int   cur;
Packit Service f629e6
    pipemode curmode;
Packit Service f629e6
Packit Service f629e6
    if (strcmp(mode, "r") == 0)
Packit Service f629e6
	curmode = reading;
Packit Service f629e6
    else if (strcmp(mode, "w") == 0)
Packit Service f629e6
	curmode = writing;
Packit Service f629e6
    else
Packit Service f629e6
	return NULL;
Packit Service f629e6
Packit Service f629e6
    /* make a name for the temporary file */
Packit Service f629e6
    if ((name = mktemp(strdup("sys$scratch:gawk-pipe_XXXXXX.tmp"))) == 0)
Packit Service f629e6
	return NULL;
Packit Service f629e6
Packit Service f629e6
    if (curmode == reading) {
Packit Service f629e6
	/* an input pipe reads a temporary file created by the command */
Packit Service f629e6
	vms_execute(command, (char *)0, name);	/* 'command >tempfile' */
Packit Service f629e6
    }
Packit Service f629e6
    if ((current = fopen(name, mode, "mbc=24", "mbf=2")) == NULL) {
Packit Service f629e6
	free(name);
Packit Service f629e6
	return NULL;
Packit Service f629e6
    }
Packit Service f629e6
    cur = fileno(current);
Packit Service f629e6
    if (cur >= pipes_lim)  expand_pipes(cur);
Packit Service f629e6
 /* assert( cur >= 0 && cur < pipes_lim ); */
Packit Service f629e6
    pipes[cur].name = name;
Packit Service f629e6
    pipes[cur].pmode = curmode;
Packit Service f629e6
    pipes[cur].command = strdup(command);
Packit Service f629e6
    return current;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
int
Packit Service f629e6
pclose( FILE *current )
Packit Service f629e6
{
Packit Service f629e6
    int rval, cur = fileno(current);
Packit Service f629e6
Packit Service f629e6
 /* assert( cur >= 0 && cur < pipes_lim ); */
Packit Service f629e6
    if ((cur < 0) || (pipes[cur].pmode == unopened))
Packit Service f629e6
	return -1;	/* should never happen, but does with two-way */
Packit Service f629e6
Packit Service f629e6
    rval = fclose(current);	/* close temp file; if reading, we're done */
Packit Service f629e6
    if (pipes[cur].pmode == writing) {
Packit Service f629e6
	/* an output pipe feeds the temporary file to the other program */
Packit Service f629e6
	rval = vms_execute(pipes[cur].command, pipes[cur].name, (char *)0);
Packit Service f629e6
    }
Packit Service f629e6
    /* clean up */
Packit Service f629e6
    unlink(pipes[cur].name);	/* get rid of the temporary file */
Packit Service f629e6
    pipes[cur].pmode = unopened;
Packit Service f629e6
    free(pipes[cur].name),  pipes[cur].name = 0;
Packit Service f629e6
    free(pipes[cur].command),  pipes[cur].command = 0;
Packit Service f629e6
    return rval;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
    /*
Packit Service f629e6
     * Create a process and execute a command in it.  This is essentially
Packit Service f629e6
     * the same as system() but allows us to specify SYS$INPUT (stdin)
Packit Service f629e6
     * and/or SYS$OUTPUT (stdout) for the process.
Packit Service f629e6
     * [With more work it could truly simulate a pipe using mailboxes.]
Packit Service f629e6
     */
Packit Service f629e6
int
Packit Service f629e6
vms_execute( const char *command, const char *input, const char *output )
Packit Service f629e6
{
Packit Service f629e6
    struct dsc$descriptor_s cmd, in, out, *in_p, *out_p;
Packit Service f629e6
    U_Long sts, cmpltn_sts;
Packit Service f629e6
Packit Service f629e6
    cmd.dsc$w_length = strlen(cmd.dsc$a_pointer = (char *)command);
Packit Service f629e6
    cmd.dsc$b_dtype = DSC$K_DTYPE_T;
Packit Service f629e6
    cmd.dsc$b_class = DSC$K_CLASS_S;
Packit Service f629e6
    if (input) {
Packit Service f629e6
	in.dsc$w_length = strlen(in.dsc$a_pointer = (char *)input);
Packit Service f629e6
	in_p = ∈
Packit Service f629e6
	in.dsc$b_dtype = DSC$K_DTYPE_T;
Packit Service f629e6
	in.dsc$b_class = DSC$K_CLASS_S;
Packit Service f629e6
    } else
Packit Service f629e6
	in_p = 0;
Packit Service f629e6
    if (output) {
Packit Service f629e6
	out.dsc$w_length = strlen(out.dsc$a_pointer = (char *)output);
Packit Service f629e6
	out_p = &out;
Packit Service f629e6
	out.dsc$b_dtype = DSC$K_DTYPE_T;
Packit Service f629e6
	out.dsc$b_class = DSC$K_CLASS_S;
Packit Service f629e6
    } else
Packit Service f629e6
	out_p = 0;
Packit Service f629e6
Packit Service f629e6
    push_logicals();	/* guard against user-mode definitions of sys$Xput */
Packit Service f629e6
    sts = LIB$SPAWN(&cmd, in_p, out_p, (U_Long *)0,
Packit Service f629e6
		    (struct dsc$descriptor_s *)0, (U_Long *)0, &cmpltn_sts);
Packit Service f629e6
    pop_logicals();	/* restore environment */
Packit Service f629e6
Packit Service f629e6
    if (vmswork(sts) && vmsfail(cmpltn_sts))  sts = cmpltn_sts;
Packit Service f629e6
    if (vmsfail(sts)) {
Packit Service f629e6
	errno = EVMSERR,  vaxc$errno = sts;
Packit Service f629e6
	return -1;
Packit Service f629e6
    } else
Packit Service f629e6
	return 0;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
/*----*
Packit Service f629e6
	This rigmarole is to guard against interference from the current
Packit Service f629e6
	environment.  User-mode definitions of SYS$INPUT and/or SYS$OUTPUT
Packit Service f629e6
	will interact with spawned subprocesses--including LIB$SPAWN with
Packit Service f629e6
	explicit input and/or output arguments specified--if they were
Packit Service f629e6
	defined without the 'CONFINED' attribute.  The definitions created
Packit Service f629e6
	in vms_args.c as part of command line I/O redirection happened to
Packit Service f629e6
	fall into this category :-(, but even though that's been fixed,
Packit Service f629e6
	there's still the possibility of the user doing something like
Packit Service f629e6
	 |$ define/user sys$output foo.out
Packit Service f629e6
	prior to starting the program.  Without ``/name_attr=confine'',
Packit Service f629e6
	that will really screw up pipe simulation, so we've got to work-
Packit Service f629e6
	around it here.  This is true whether pipes are implemented via
Packit Service f629e6
	mailboxes or temporary files, as long as lib$spawn() is being used.
Packit Service f629e6
Packit Service f629e6
	push_logicals() calls save_translation() the first time it's
Packit Service f629e6
	invoked; the latter allocates some memory to hold a full logical
Packit Service f629e6
	name translation and uses $trnlnm to fill that in.  Then if either
Packit Service f629e6
	sys$input or sys$output has a user-mode, non-confined translation,
Packit Service f629e6
	push_logicals() will delete the definition(s) using $dellnm.
Packit Service f629e6
	After the spawned command has returned, pop_logicals() is called;
Packit Service f629e6
	it calls restore_translation() for any deleted values; the latter
Packit Service f629e6
	uses $crllnm or $crelog to recreate the original definition.
Packit Service f629e6
Packit Service f629e6
	SYS$ERROR is currently ignored; perhaps it should receive the same
Packit Service f629e6
	treatment...
Packit Service f629e6
*----*/
Packit Service f629e6
Packit Service f629e6
 /* logical name table, and names of interest; these are all constant */
Packit Service f629e6
static const Descrip(lnmtable,"LNM$PROCESS_TABLE");
Packit Service f629e6
static const Descrip(sys_input,"SYS$INPUT");
Packit Service f629e6
static const Descrip(sys_output,"SYS$OUTPUT");
Packit Service f629e6
static const unsigned char acmode = PSL$C_USER; /* only care about user-mode */
Packit Service f629e6
Packit Service f629e6
 /* macros for simplfying the code a bunch */
Packit Service f629e6
#define DelTrans(l)	SYS$DELLNM(&lnmtable, (l), &acmode)
Packit Service f629e6
#define GetTrans(l,i)	SYS$TRNLNM((U_Long *)0, &lnmtable, (l), &acmode, (i))
Packit Service f629e6
#define SetTrans(l,i)	SYS$CRELNM((U_Long *)0, &lnmtable, (l), &acmode, (i))
Packit Service f629e6
 /* itemlist manipulation macros; separate versions for aggregate and scalar */
Packit Service f629e6
#define SetItmA(i,c,p,r) ((i).code = (c), (i).len = sizeof (p),\
Packit Service f629e6
			  (i).buffer = (p), (i).retlen = (U_Short *)(r))
Packit Service f629e6
#define SetItmS(i,c,p)	 ((i).code = (c), (i).len = sizeof *(p),\
Packit Service f629e6
			  (i).buffer = (p), (i).retlen = (U_Short *)0)
Packit Service f629e6
#define EndItm0(i)	 ((i).code = (i).len = 0)
Packit Service f629e6
Packit Service f629e6
 /* translate things once, then hold the results here for multiple re-use */
Packit Service f629e6
static Itm *input_definition, *output_definition;
Packit Service f629e6
Packit Service f629e6
static void
Packit Service f629e6
push_logicals( void )		/* deassign sys$input and/or sys$output */
Packit Service f629e6
{
Packit Service f629e6
    static int init_done = 0;
Packit Service f629e6
Packit Service f629e6
    if (!init_done) {	/* do logical name lookups one-time only */
Packit Service f629e6
	input_definition = save_translation(&sys_input);
Packit Service f629e6
	output_definition = save_translation(&sys_output);
Packit Service f629e6
	init_done = 1;
Packit Service f629e6
    }
Packit Service f629e6
    if (input_definition) DelTrans(&sys_input);		/* kill sys$input */
Packit Service f629e6
    if (output_definition) DelTrans(&sys_output);	/* and sys$output */
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
static void
Packit Service f629e6
pop_logicals( void )		/* redefine sys$input and/or sys$output */
Packit Service f629e6
{
Packit Service f629e6
    if (input_definition) restore_translation(&sys_input, input_definition);
Packit Service f629e6
    if (output_definition) restore_translation(&sys_output, output_definition);
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
static Itm *
Packit Service f629e6
save_translation( const struct dsc$descriptor_s *logname )
Packit Service f629e6
{
Packit Service f629e6
    Itm trans[4], *itmlst;
Packit Service f629e6
    long trans_attr, max_trans_indx;	/* 0-based translation index count */
Packit Service f629e6
    unsigned char trans_acmode;		/* translation's access mode */
Packit Service f629e6
    unsigned itmlst_size;
Packit Service f629e6
    register int i, j;
Packit Service f629e6
Packit Service f629e6
    itmlst = 0;
Packit Service f629e6
    /* Want translation index count for non-confined, user-mode definition;
Packit Service f629e6
	unfortunately, $trnlnm does not provide that much control.  Try to
Packit Service f629e6
	fetch several values of interest, then decide based on the result.
Packit Service f629e6
     */
Packit Service f629e6
    SetItmS(trans[0], LNM$_MAX_INDEX, &max_trans_indx),	 max_trans_indx = -1;
Packit Service f629e6
    SetItmS(trans[1], LNM$_ACMODE, &trans_acmode),	 trans_acmode = 0;
Packit Service f629e6
    SetItmS(trans[2], LNM$_ATTRIBUTES, &trans_attr),	 trans_attr = 0;
Packit Service f629e6
    EndItm0(trans[3]);
Packit Service f629e6
    if (vmswork(GetTrans(logname, trans)) && max_trans_indx >= 0
Packit Service f629e6
      && trans_acmode == PSL$C_USER && !(trans_attr & LNM$M_CONFINE)) {
Packit Service f629e6
	/* Now know that definition of interest exists;
Packit Service f629e6
	    allocate and initialize an item list and associated buffers;
Packit Service f629e6
	    use three entries for each translation.
Packit Service f629e6
	 */
Packit Service f629e6
	itmlst_size = (3 * (max_trans_indx + 1) + 1) * sizeof(Itm);
Packit Service f629e6
	emalloc(itmlst, Itm *, itmlst_size, "save_translation");
Packit Service f629e6
	for (i = 0; i <= max_trans_indx; i++) {
Packit Service f629e6
	    struct def { U_Long indx, attr; U_Short len;
Packit Service f629e6
			 char str[LNM$C_NAMLENGTH], eos; } *wrk;
Packit Service f629e6
	    emalloc(wrk, struct def *, sizeof (struct def), "save_translation");
Packit Service f629e6
	    wrk->indx = (U_Long)i;  /* this one's an input value for $trnlnm */
Packit Service f629e6
	    SetItmS(itmlst[3*i+0], LNM$_INDEX, &wrk->indx);
Packit Service f629e6
	    SetItmS(itmlst[3*i+1], LNM$_ATTRIBUTES, &wrk->attr),  wrk->attr = 0;
Packit Service f629e6
	    SetItmA(itmlst[3*i+2], LNM$_STRING, &wrk->str, &wrk->len),  wrk->len = 0;
Packit Service f629e6
	}
Packit Service f629e6
	EndItm0(itmlst[3*i]);   /* assert( i == max_trans_indx+1 ); */
Packit Service f629e6
	/* Time to perform full logical name translation,
Packit Service f629e6
	    then update item list for subsequent restoration.
Packit Service f629e6
	    If there are any holes [don't know whether that's possible]
Packit Service f629e6
	    collapse them out of the list; don't want them at restore time.
Packit Service f629e6
	 */
Packit Service f629e6
	if (vmswork(GetTrans(logname, itmlst))) {
Packit Service f629e6
	    for (i = 0, j = -1; i <= max_trans_indx; i++) {
Packit Service f629e6
		U_Long *attr_p;
Packit Service f629e6
		attr_p = itmlst[3*i+1].buffer;	/* copy (void *) to true type */
Packit Service f629e6
		if (*attr_p & LNM$M_EXISTS) {
Packit Service f629e6
		    *attr_p &= ~LNM$M_EXISTS;	/* must clear this bit */
Packit Service f629e6
		    if (++j < i)  itmlst[3*j+0] = itmlst[3*i+0],
Packit Service f629e6
				  itmlst[3*j+1] = itmlst[3*i+1],
Packit Service f629e6
				  itmlst[3*j+2] = itmlst[3*i+2];
Packit Service f629e6
		    if (itmlst[3*j+2].retlen) { /* fixup buffer length */
Packit Service f629e6
			itmlst[3*j+2].len = *itmlst[3*j+2].retlen;
Packit Service f629e6
			itmlst[3*j+2].retlen = (U_Short *)0;
Packit Service f629e6
		    }
Packit Service f629e6
		}
Packit Service f629e6
	    }
Packit Service f629e6
	    if (++j < i)  EndItm0(itmlst[3*j]);
Packit Service f629e6
	} else	    /* should never happen; tolerate potential memory leak */
Packit Service f629e6
	    free(itmlst),  itmlst = 0;  /*('wrk' buffer(s) will become lost)*/
Packit Service f629e6
    }
Packit Service f629e6
    return itmlst;
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
static void
Packit Service f629e6
restore_translation( const struct dsc$descriptor_s *logname,
Packit Service f629e6
                     const Itm *itemlist )
Packit Service f629e6
{
Packit Service f629e6
    struct dsc$descriptor_s trans_val;
Packit Service f629e6
    U_Long *attr_p;
Packit Service f629e6
# define LOG_PROCESS_TABLE 2		/* <obsolete> */
Packit Service f629e6
# define LOG_USERMODE PSL$C_USER
Packit Service f629e6
Packit Service f629e6
 /* assert( itemlist[1].code == LNM$_ATTRIBUTES ); */
Packit Service f629e6
    attr_p = itemlist[1].buffer;	/* copy (void *) to (U_Long *) */
Packit Service f629e6
    if (*attr_p & LNM$M_CRELOG) {	/* check original creation method */
Packit Service f629e6
	/* $crelog values can have only one translation;
Packit Service f629e6
	    so it'll be the first string entry in the itemlist.
Packit Service f629e6
	 */
Packit Service f629e6
     /* assert( itemlist[2].code == LNM$_STRING ); */
Packit Service f629e6
	trans_val.dsc$a_pointer = itemlist[2].buffer;
Packit Service f629e6
	trans_val.dsc$w_length = itemlist[2].len;
Packit Service f629e6
	trans_val.dsc$b_dtype = DSC$K_DTYPE_T;
Packit Service f629e6
	trans_val.dsc$b_class = DSC$K_CLASS_S;
Packit Service f629e6
	(void) SYS$CRELOG(LOG_PROCESS_TABLE, logname, &trans_val, LOG_USERMODE);
Packit Service f629e6
    } else {
Packit Service f629e6
	/* $crelnm definition; itemlist could specify multiple translations,
Packit Service f629e6
	    but has already been setup properly for use as-is.
Packit Service f629e6
	 */
Packit Service f629e6
	(void) SetTrans(logname, itemlist);
Packit Service f629e6
    }
Packit Service f629e6
}
Packit Service f629e6
Packit Service f629e6
#endif	/*PIPES_SIMULATED*/
Packit Service f629e6
Packit Service f629e6
#endif	/*!NO_VMS_PIPES*/