[comp.os.vms] redirection of stdio, et al.

minow@decvax.UUCP (Martin Minow) (04/27/87)

In article <224@unc.cs.unc.edu> mcguffey@unc.cs.unc.edu (Michael McGuffey)
requests a method to allow command line redirection under VMS.  What I
use follows the signature.

Martin Minow
decvax!minow

main(argc, argv)
int		argc;
char		*argv[];
{
#ifdef vms
	argc = getredirection(argc, argv);
#endif
	...
}
--------
/*
 * getredirection() is intended to aid in porting C programs
 * to VMS (Vax-11 C) which does not support '>' and '<'
 * I/O redirection.  With suitable modification, it may
 * useful for other portability problems as well.
 */

static int
getredirection(argc, argv)
int		argc;
char		**argv;
/*
 * Process vms redirection arg's.  Exit if any error is seen.
 * If getredirection() processes an argument, it is erased
 * from the vector.  getredirection() returns a new argc value.
 *
 * Warning: do not try to simplify the code for vms.  The code
 * presupposes that getredirection() is called before any data is
 * read from stdin or written to stdout.
 *
 * Normal usage is as follows:
 *
 *	main(argc, argv)
 *	int		argc;
 *	char		*argv[];
 *	{
 *		argc = getredirection(argc, argv);
 *	}
 */
{
#ifdef	vms
	register char		*ap;	/* Argument pointer	*/
	int			i;	/* argv[] index		*/
	int			j;	/* Output index		*/
	int			file;	/* File_descriptor 	*/
	extern int		errno;	/* Last vms i/o error 	*/

	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
	    switch (*(ap = argv[i])) {
	    case '<':			/* <file		*/
		if (freopen(++ap, "r", stdin) == NULL) {
		    perror(ap);		/* Can't find file	*/
		    exit(errno);	/* Is a fatal error	*/
		}

	    case '>':			/* >file or >>file	*/
		if (*++ap == '>') {	/* >>file		*/
		    /*
		     * If the file exists, and is writable by us,
		     * call freopen to append to the file (using the
		     * file's current attributes).  Otherwise, create
		     * a new file with "vanilla" attributes as if
		     * the argument was given as ">filename".
		     * access(name, 2) is TRUE if we can write on
		     * the specified file.
		     */
		    if (access(++ap, 2) == 0) {
			if (freopen(ap, "a", stdout) != NULL)
			    break;	/* Exit case statement	*/
			perror(ap);	/* Error, can't append	*/
			exit(errno);	/* After access test	*/
		    }			/* If file accessable	*/
		}
		/*
		 * On vms, we want to create the file using "standard"
		 * record attributes.  create(...) creates the file
		 * using the caller's default protection mask and
		 * "variable length, implied carriage return"
		 * attributes. dup2() associates the file with stdout.
		 */
		if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1
		 || dup2(file, fileno(stdout)) == -1) {
		    perror(ap);		/* Can't create file	*/
		    exit(errno);	/* is a fatal error	*/
		}			/* If '>' creation	*/
		break;			/* Exit case test	*/

	    default:
		argv[j++] = ap;		/* Not a redirector	*/
		break;			/* Exit case test	*/
	    }
	}				/* For all arguments	*/
	return (j);
#else
	/*
	 * Note: argv[] is referenced to fool the Decus C
	 * syntax analyser, supressing an unneeded warning
	 * message.
	 */
	return (argv[0], argc);		/* Just return as seen	*/
#endif
}

gwalker@SPCA.BBN.COM.UUCP (04/27/87)

The following are the ways I know of to get stdio
redirection on VMS. This is just a summary and doesn't give
a lot of details. Whether these work out for you may depend on
what programs you are trying to use this way and even what language
they are written in.

1. For all programs that use SYS$OUTPUT (stdout for VMS) for their output (as
   most do) you can either
    a) use
    ASSIGN/USER somefile SYS$OUTPUT
    just before running the program to make everything directed to SYS$OUTPUT
    go to the file "somefile" instead OR

    b) Put the call to your program in a command file and invoke it with
    @commandfile /OUTPUT=somefile
    to redirect the output. (If the program takes input from the terminal
    you need to ASSIGN/USER SYS$COMMAND SYS$INPUT  before running the program
    from the command file.)

There may be some cases where the file comes out funny if the program is
one that believes its output is always a terminal, or anything like that.
I guess the best way to know if these will meet your needs is to try them.

On the input side (SYS$INPUT), method (a) can be used:
  ASSIGN/USER myinfile SYS$INPUT
also in command files you can follow the call to the program with
the input itself (the input data lines just don't start with a '$' sign.)

2. For VMS commands, many of them now have a /OUTPUT switch to allow
you to redirect their output to a file.

3. If what you really want is to have your programs accept ">somefile"
or "<myinfile" on their command line and redirect output or input, they
would have to be explicitly written to look for that construct. 

I don't know offhand if VAX C and its runtime library will do this for
you automatically, I haven't used the VAX C runtime library, but it is
just possible that it will handle this for you for programs written in
VAX C.

-- Gail Walker

carl@CITHEX.CALTECH.EDU.UUCP (04/27/87)

 > Does anyone know of any tools that allow command line redirection of
 > stdio under VMS. Something similar to the way unix and msdos does it is
 > preferable to some of the methods that have previously been proposed.

The following is C code to do at least more or less what you want.  First
a short rationale for using the technique I did, rather than something else:
The C run time library does things like setting up the array of io blocks
and parsing the command line for you; while I could have written code to
do this, and then linked this code with everything I wrote, I decided to 
use the default startup routines, and then to do any I/O redirection later.
The end result is a routine called ioinit that is used as follows:
main(nargs, args)
int nargs;
char **args;
{	...
	ioinit(&nargs, args);
	...
}
It redirects any or all of stdin, stdout, or stderr, using the same syntax
as under UNIX* (with the exception of "here documents");
/******************************************************************************/
/* IOINIT.C -- I/O redirection subroutine.  Redirects stdin (< or <<);
 *	stdout (> or >>); or stderr ( 2>, 2>>)
 * Usage:
 *	main(nargs, args)
 *	{	...
 *		ioinit(&nargs, args);
 *		...
 *	}
 */

#include <errno.h>
#include <stdio.h>

typedef char *STR;

ioinit(nargs, args)
int *nargs;
STR *args;
{   int argnum;

    for (argnum = 1; argnum < *nargs; ++argnum)
    {	if (strncmp(args[argnum], "<", 1) == 0)
	{   args[argnum] += 1;
	    *nargs = u_reopen(*nargs, args, argnum, "r", stdin);
	    argnum--;
	} else if (strncmp(args[argnum], ">>", 2) == 0)
	{   args[argnum] += 2;
	    *nargs = u_reopen(*nargs, args, argnum, "a", stdout);
	    argnum--;
	} else if (strncmp(args[argnum], ">", 1) == 0)
	{   args[argnum] += 1;
	    *nargs = u_reopen(*nargs, args, argnum, "w", stdout);
	    argnum--;
	} else if (strncmp(args[argnum], "2>>", 3) == 0)
	{   args[argnum] += 3;
	    *nargs = u_reopen(*nargs, args, argnum, "a", stderr);
	    argnum--;
	} else if (strncmp(args[argnum], "2>", 2) == 0)
	{    args[argnum] += 2;
	    *nargs = u_reopen(*nargs, args, argnum, "w", stderr);
	    argnum--;
	}
    }
}

u_reopen(nargs, args, argnum, acmod, chan)
int nargs, argnum;
STR *args, acmod;
FILE *chan;
{   char *file;
    int offset, i, errornum;

    if (args[argnum][0] != '\0')
    {	offset = 1;
	file = args[argnum];
    } else if ((argnum + 1) < nargs)
    {	offset = 2;
	file = args[argnum+1];
    } else
    {	fprintf(stderr, "Illegal redirection on command line.\n");
	exit(1);
    } for (i = argnum; i < nargs - offset; ++i)
	args[i] = args[i + offset];
    if (((*acmod == ' ' || strcmp(acmod,"a+") == 0) && freopen(file, acmod,
	chan ,"rfm=stm") != chan) || freopen(file, acmod, chan) != chan)
    {	if (errno == EVMSERR)
	{	errornum = vaxc$errno;
		fprintf(stderr, "Failure opening redirected stream.\n");
		exit(errornum);
	} else
	{	perror("Failure opening redirected stream.");
		exit(1);
	}
    } return(nargs - offset);
}
/******************************************************************************/
Using this as a starting point, you should be able to tailor it to your
needs.

LEICHTER-JERRY@YALE.ARPA.UUCP (04/28/87)

To: unc!mcguffey@mcnc.org (Michael McGuffey)
    Does anyone know of any tools that allow command line redirection of
    stdio under VMS. Something similar to the way unix and msdos does it is
    preferable to some of the methods that have previously been proposed.

The following is from the DECUS C library; it's meant for programs written
in C - which seems to be what you have in mind - though it could be adapted
for other languages.

getredirection() was written by Martin Minow.  Note that the code dates
back to V1 of C; some of it could be simplified, as you no longer need to
creat/fdopen's to get at various RMS options.

							-- Jerry

/*
 * getredirection() is intended to aid in porting C programs
 * to VMS (Vax-11 C) which does not support '>' and '<'
 * I/O redirection.  With suitable modification, it may
 * useful for other portability problems as well.
 *
 * Modified, 24-Jan-86 by Jerry Leichter
 *	When creating a new output file, force the maximum record size to
 *	512; otherwise, it ends up as 0 (though the C I/O system won't write
 *	a record longer than 512 bytes anyway) which will cause problems if
 *	the file is later opened for APPEND - if the maximum record size is
 *	0, C will use the length of the longest record written to the file
 *	for its buffer!  [This was a C RTL bug, probably since fixed.]
 */

#ifdef	vms
#include	<stdio.h>
#include	<errno.h>

int
getredirection(argc, argv)
int		argc;
char		**argv;
/*
 * Process vms redirection arg's.  Exit if any error is seen.
 * If getredirection() processes an argument, it is erased
 * from the vector.  getredirection() returns a new argc value.
 *
 * Warning: do not try to simplify the code for vms.  The code
 * presupposes that getredirection() is called before any data is
 * read from stdin or written to stdout.
 *
 * Normal usage is as follows:
 *
 *	main(argc, argv)
 *	int		argc;
 *	char		*argv[];
 *	{
 *		argc = getredirection(argc, argv);
 *	}
 */
{
	register char		*ap;	/* Argument pointer	*/
	int			i;	/* argv[] index		*/
	int			j;	/* Output index		*/
	int			file;	/* File_descriptor 	*/

	for (j = i = 1; i < argc; i++) {   /* Do all arguments	*/
	    switch (*(ap = argv[i])) {
	    case '<':			/* <file		*/
		if (freopen(++ap, "r", stdin) == NULL) {
		    perror(ap);		/* Can't find file	*/
		    exit(errno);	/* Is a fatal error	*/
		}
		break;

	    case '>':			/* >file or >>file	*/
		if (*++ap == '>') {	/* >>file		*/
		    /*
		     * If the file exists, and is writable by us,
		     * call freopen to append to the file (using the
		     * file's current attributes).  Otherwise, create
		     * a new file with "vanilla" attributes as if
		     * the argument was given as ">filename".
		     * access(name, 2) is TRUE if we can write on
		     * the specified file.
		     */
		    if (access(++ap, 2) == 0) {
			if (freopen(ap, "a", stdout) != NULL)
			    break;	/* Exit case statement	*/
			perror(ap);	/* Error, can't append	*/
			exit(errno);	/* After access test	*/
		    }			/* If file accessable	*/
		}
		/*
		 * On vms, we want to create the file using "standard"
		 * record attributes.  create(...) creates the file
		 * using the caller's default protection mask and
		 * "variable length, implied carriage return"
		 * attributes. dup2() associates the file with stdout.
		 */
		if ((file = creat(ap, 0, "rat=cr", "rfm=var", "mrs=512"))
			== -1
		 || dup2(file, fileno(stdout)) == -1) {
		    perror(ap);		/* Can't create file	*/
		    exit(errno);	/* is a fatal error	*/
		}			/* If '>' creation	*/
		break;			/* Exit case test	*/

	    default:
		argv[j++] = ap;		/* Not a redirector	*/
		break;			/* Exit case test	*/
	    }
	}				/* For all arguments	*/
	argv[j] = NULL;			/* Terminate argv[]	*/
	return (j);			/* Return new argc	*/
}
#else
getredirection(argc, argv)
int		argc;
char		*argv[];
/*
 * Dummy routine.
 */
{
	return (argv[0], argc);
}
#endif
-------

gdw@ssl-macc.co.uk (Grenville Whelan) (04/30/87)

In article <224@unc.cs.unc.edu> mcguffey@unc.cs.unc.edu (Michael McGuffey) writes:
>
>Does anyone know of any tools that allow command line redirection of
>stdio under VMS. Something similar to the way unix and msdos does it is
>preferable to some of the methods that have previously been proposed.
>
>-mike mcguffey


Simply redirect the logical SYS$OUTPUT to a file rather than the terminal,
ie.
     $ ASSIGN <FILE> SYS$OUTPUT
     $ <commands>
     $ DEASSIGN SYS$OUTPUT
-- 
# Grenville Whelan  -  Software Sciences Ltd, Park Street, Macclesfield, UK  #
# (EMAIL gdw@uk.co.ssl-macc) (UUCP !mcvax!ukc!mcvax!gdw) (TEL +44 625 29241) #

      "Are you the Police?"  "No ma'am, we're musicians!" -- Elwood.

jimp@cognos.uucp (Jim Patterson) (05/02/87)

In article <8704272220.AA01440@ucbvax.Berkeley.EDU> 
gwalker@SPCA.BBN.COM.UUCP writes:

>The following are the ways I know of to get stdio
>redirection on VMS. 
>1. For all programs that use SYS$OUTPUT (stdout for VMS) for their output (as
>   most do) you can either
>    a) use
>    ASSIGN/USER somefile SYS$OUTPUT
>On the input side (SYS$INPUT), method (a) can be used:
>  ASSIGN/USER myinfile SYS$INPUT

For VAX 11 C users, ASSIGN/USER doesn't work.  You can use ASSIGN
(which assigns a supervisor-level logical), but of course you have
to remember to DEASSIGN it after. As near as I can figure out, this
is because the VAX 11 C I/O interface doesn't actually open the
standard input/output files but instead relies on the process-permanent
file opens.

You can also do your own I/O redirection as noted in some of the other
articles. No, VAX 11 C won't do this for you (or at least
it won't for me).

There is a product called VAX Shell that I think does all of this
redirection etc. for you.  I haven't used it, though, so have no
idea how it works or what it does for you.  From what I know of it,
VAX Shell gives you a UNIX-like shell environment to replace the
VMS DCL environment.
-- 
Jim Patterson
Cognos Inc.

kenw@noah.arc.CDN (Ken Wallewein) (05/02/87)

  A while back, I wrote a set of DCL command files to perform piping a la
unix. It allows me to say things like
	pipe dir | dups > purgable.lst.
I write a number of filters and filter templates in TECO, C, BASIC, and DCL.
  The redirection was just a tad tricky, and I learned a bit about 
logical device handling. I used temporary files and sequential processing
rather than pipes between spawned processes, but that is irrelevant to the
redirection aspect. Also easier under restrictive environments. Anyway, I 
could provide it (post it, whatever) if anybody's interested.
/kenw

gore@nucsrl.UUCP (05/03/87)

>Does anyone know of any tools that allow command line redirection of
>stdio under VMS. Something similar to the way unix and msdos does it is
>preferable to some of the methods that have previously been proposed.
>
>-mike mcguffey

$ run/input=input_file/output=output_file program

is all it takes.  Note that you do have to have both /input and /output.  If
you want one of them to remain your terminal, use /input=TT: or /output=TT:.
If you don't have any input, use /input=NLA0: (the null device).

Warning:  this is all from memory.  Use HELP RUN to confirm this.

Jacob Gore
Northwestern University, Computer Science Research Lab
{gargoyle,ihnp4,chinet}!nucsrl!gore
gore@EECS.NWU.Edu (for now, only from ARPA)