home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / gawk-2.15.6-src.tgz / tar.out / fsf / gawk / vms / vms_popen.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  12KB  |  350 lines

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