(root)/
gawk-5.2.2/
vms/
vms_popen.c
       1  /* [.vms]vms_popen.c -- substitute routines for missing pipe calls.
       2  
       3     Copyright (C) 1991-1993, 1996, 2010, 2011, 2014, 2022, 2023,
       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 3, 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 *
      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
      36  pclose( FILE *current )
      37  {
      38      fatal(" Internal error ('pclose' not implemented)");
      39      /* NOT REACHED */
      40      return -1;
      41  }
      42  
      43  int
      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 *
      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
     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
     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 = &in;
     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
     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
     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 *
     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
     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*/