home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
fchk294s.zip
/
ftnchek-2.9.4
/
shell_mung.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-10
|
39KB
|
924 lines
/******************************************************************/
#ifdef VAXC /* v1.2-004 */
#module SHELL_MUNG "Jym V1.2-005" /* Only VAX C understands this. */
#else /* v1.2-004 */
#define TRUE 1 /* GNU C stdio.h doesn't define TRUE and FALSE*/
#define FALSE 0
#endif
/* SHELL_MUNG.C
**=============================================================================
** Copyright (C) 1989 Jym Dyer (jym@wheaties.ai.mit.edu)
**
** This program 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.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**-----------------------------------------------------------------------------
** Version: V1.2-005
**-----------------------------------------------------------------------------
** Facility: GNU
**-----------------------------------------------------------------------------
** Prefix: SHELL_
**-----------------------------------------------------------------------------
** Abstract
** ~~~~~~~~
** If you think "shell mung" is a step in the preparation of a good mung dahl,
** you're right, but in this context "mung" is the verb. ("Mung" historically
** means "mung until no good," but one could easily interpret it to mean "mung
** until nearly gnu". Or something to that effect.)
** This file provides the shell_glob() and shell_mung() functions for the
** VMS/DCL/VAX C user. The shell_glob() function "globs" (parses filespecs
** with wildcards and matches them with existing files). The shell_mung()
** function provides GNU-shell-like IO redirection and globbing: it changes
** `stderr', `stdin', and/or `stdout' to do the IO redirection; it mungs a
** VAX C program's main() function's `argc' and `argv' variables (using shell_
** glob()) to do the globbing; and it truncates `argv[0]' down to a filename
** and filetype or, if the file is a .EXE file, to just the filename.
** This makes it easy to port many GNU utilities to VMS, as these utilities
** often expect the shell to have done the work that shell_mung() does. It can
** also be used to port Un*x utilities as well, if anybody cares.
**-----------------------------------------------------------------------------
** Functions
** ~~~~~~~~~
** shell_glob()
** shell_mung()
** (static) truncate_argv_0()
**-----------------------------------------------------------------------------
** Environment
** ~~~~~~~~~~~
** Must be linked with the VAX C RTL. Intended for use with the DCL CLI on
** VMS; it would be pointless to use shell_mung() with the SHELL CLI, though
** shell_glob() might possibly come in handy.
** Must be linked with xmalloc() and xrealloc() functions, which are readily
** available from GNU source code. These are simply malloc() and realloc()
** functions that exit if there is an error getting memory.
**-----------------------------------------------------------------------------
** Author: Jym Dyer (jym@wheaties.ai.mit) 8-Apr-1988
**-----------------------------------------------------------------------------
** Modifications
** ~~~~~~~~~~~~~
** 1.0-001 - Original version. Inspired by the DECUS C getredirection()
** function written by Martin Minow, Jerry Leichter, and Jym Dyer.
** {Jym 8-Apr-1988}
** 1.1-002 - Added `shell_default' and use of same by shell_glob(), which
** allows default filespecs to be used in globbing.
** {Jym 9-Jan-1989}
** 1.2-003 - Made shell_glob() accept unglobbable filespecs.
** - In addition to '-', '+' is now accepted as the start of an option.
** {Jym 3-Jun-1989}
** 1.2-004 - Made changes to allow compilation by GNU C.
** {Hunter Goatley, goathunter@WKUVX1.BITNET, 12-SEP-1991}
** 1.2-005 - Added Andrew Greer's patch to fix problem parsing non-filespec
** arguments to options
** {Hunter Goatley, goathunter@WKUVX1.BITNET, 22-SEP-1991}
** {Andrew Greer, <Andrew.Greer@vuw.ac.nz>}
**=============================================================================
*/
/* -=- FILE INCLUSIONS -=- */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <fab.h>
#include <nam.h>
#include <rmsdef.h>
#include <stsdef.h>
#ifndef VAXC /* v1.2-004 */
#define _tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20:(c))
#endif /* Also changed tolower() calls to _tolower() */
/* -=- FORWARD DECLARATIONS -=- */
extern void * xmalloc();
extern void * xrealloc();
extern void sys$exit();
extern int sys$parse();
extern int sys$search();
extern char ** shell_glob();
static char * truncate_argv_0();
/* -=- GLOBAL VARIABLE DEFINITION -=- */
#ifdef VAXC /* v1.2-004 */
globaldef char * shell_default = NULL; /* Default filespec for shell_glob().*/
#else
char * shell_default = NULL; /* Default filespec for shell_glob().*/
#endif
/* Initialized here to NULL, which
** means that no default filespec is
** used by shell_glob().
*/
/* -=- FUNCTIONS -=- */
/* shell_glob()
**-----------------------------------------------------------------------------
** Functional Description
** ~~~~~~~~~~~~~~~~~~~~~~
** Given a filespec, this function parses that filespec out and searches for
** the file---or, if the filespec has wildcard characters in it, the files---
** associated with the filespec. It returns a newly-allocated vector of file-
** specs that are also newly-allocated. This function can be used to find only
** the first file that matches the filespec or all the files that match it. In
** the latter case, this function will also provide a count of the files that
** were found.
** Filespecs returned by this function are in lowercase, to approximate the
** "look and feel" of GNU.
** This function is used by shell_mung(), and is also handy for general use.
**-----------------------------------------------------------------------------
** Usage
** ~~~~~
** This function returns a vector---that is, a pointer to pointers. Thus you
** will need to declare this function and a vector like this:
**
** extern char ** shell_glob();
**
** char ** filespec_v;
**
** To find only the first match of a wildcarded filespec, or just to get the
** fully-parsed filespec of a given filespec, call shell_glob() with NULL as
** its second parameter. For example, if you call it like this:
**
** static int const filespec = "*.c";
** char ** filespec_v;
** ...
** filespec_v = shell_glob(filespec,NULL);
**
** The value in `filespec_v[0]' will be (for example) "dev:[dir]bar.c;". Note
** that the character string pointer is the content of the vector, not the
** vector itself.
** With this usage, `filespec_v' points to a pointer (actually an array of one
** pointer, which is the same thing). When used to get a number of filespecs,
** it points to an array of pointers.
** To get all the filespecs that match a filespec, use an address of an
** integer as shell_glob()'s second parameter, as in this example:
**
** static int const filespec = "*.c";
** int filespec_count;
** char ** filespec_v;
** ...
** filespec_v = shell_glob(filespec,&filespec_count);
**
** The result of this would be (for example),
**
** filespec_count = 3,
** filespec_v[0] = "dev:[dir]bar.c",
** filespec_v[1] = "dev:[dir]baz.c", and
** filespec_v[2] = "dev:[dir]foo.c".
**
** In either type of usage, both the vector and the character strings are in
** newly-allocated memory.
** VMS globbing entails a feature that allows one to fill in parts of a file-
** spec with default values. This feature can be used by shell_glob() using
** `shell_default', a global character string. Use of this character string is
** optional, and it is initially set to NULL, meaning no default filespec is
** used. To use it, you need to declare it like this:
**
** globalref char * shell_default;
**
** The most frequent use of a default filespec in VMS is to provide a default
** filetype, as shown in this example:
**
** static int const filespec = "*.c";
** int filespec_count;
** char ** filespec_v;
** ...
** shell_default = ".c";
** filespec_v = shell_glob(filespec,&filespec_count);
**
** If `user_string' (which is, of course, a character string) is (for example)
** "foo*", a filespec without a filetype, shell_glob() will look for all files
** whose filespecs match "foo*.c". If `user_string' were "foo*.h", a filespec
** that has a filetype, the default filespec ".c" is ignored and shell_glob()
** will look for all files whose filespecs match "foo*.h".
** Default filespecs can include devices, directories, filenames, filetypes,
** and version numbers. Wildcards can be used in filenames, filetypes, and
** version numbers. To turn default filespecs off, just set `shell_default'
** back to NULL.
** If the given filespec cannot be globbed, shell_glob() will simply return
** a filespec vector with an array of one pointer, pointing to the unglobbable
** given filespec. The given filespec, therefore, should be something that
** will stick around for as long as you expect to use the filespec vector. In
** particular, don't call shell_glob() from a function using an automatic
** character string variable of that function and expect the string to still be
** available when you've exited that function.
**-----------------------------------------------------------------------------
** Calling Sequence
** ~~~~~~~~~~~~~~~~
** filespec_v = shell_glob(given_filespec,filespec_count_p);
**-----------------------------------------------------------------------------
** Formal Arguments Description
** ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
** given_filespec Filespec "as given"---that is, as typed on the command
** line. This is what we parse and search for.
** filespec_count_p Pointer to an integer that is to receive a count of
** filespecs found that match `given_filespec'. If NULL,
** indicates that we're only to search for the first file
** that matches `given_filespec'.
**-----------------------------------------------------------------------------
** Implicit Input Description
** ~~~~~~~~~~~~~~ ~~~~~~~~~~~
** shell_default This global variable is a character string of a default
** filespec. If NULL, no default filespec is used.
**-----------------------------------------------------------------------------
** Implicit Outputs: None
**-----------------------------------------------------------------------------
** Return Value
** ~~~~~~~~~~~~
** Returns a vector of pointers to newly-parsed filespecs.
**-----------------------------------------------------------------------------
** Side Effects
** ~~~~~~~~~~~~
** Memory is allocated for the filespec vector and for the filespecs that it
** points to.
**-----------------------------------------------------------------------------
*/
char **
shell_glob(given_filespec,filespec_count_p)
char * given_filespec;
int * filespec_count_p;
{
/* --- LOCAL VARIABLES --- */
register char * character_p; /* General-purpose character pointer,
** used to convert filespecs to
** lowercase.
*/
register char * expanded_filespec; /* v1.2-004 */
/* Buffer to hold the expanded filespec
** from a call to sys$parse().
*/
register int filespec_count = 0; /* Count of filespecs that sys$search()
** finds.
*/
register char ** filespec_v = NULL; /* Vector of pointers to filespecs that
** sys$search() finds. Allocated and
** reallocated as filespecs are found.
*/
static char *our_path; /* The default device and directory
** path. Initialized the first time
** this function is called.
*/
register char * resultant_filespec; /* v1.2-004 */
/* Buffer to hold a resultant filespec
** from a call to sys$search().
*/
register int status; /* Status codes returned from calls to
** sys$parse() and sys$search().
*/
struct FAB fab = cc$rms_fab; /* RMS file access block.
*/
struct NAM nam = cc$rms_nam; /* RMS name block.
*/
/* --- INITIALIZE PATH --- */
resultant_filespec = xmalloc(NAM$C_MAXRSS + 1); /* v1.2-004 */
expanded_filespec = xmalloc(NAM$C_MAXRSS + 1); /* v1.2-004 */
if (our_path == NULL)
(char *) our_path = getenv("PATH"); /* v1.2-004 */
/* our_path = getenv("PATH"); */
/* --- INITIALIZE FAB AND NAM --- */
if (shell_default != NULL)
{
fab.fab$l_dna = shell_default;
fab.fab$b_dns = strlen(shell_default);
}
fab.fab$l_fna = given_filespec;
fab.fab$b_fns = strlen(given_filespec);
fab.fab$l_nam = &nam;
nam.nam$l_esa = expanded_filespec; /* v1.2-004 */
nam.nam$b_ess = NAM$C_MAXRSS;
nam.nam$l_rsa = resultant_filespec; /* v1.2-004 */
nam.nam$b_rss = NAM$C_MAXRSS;
/* --- PARSE OUT THE GIVEN FILESPEC --- */
if ((status = sys$parse(&fab)) == RMS$_NORMAL)
{
/* --- SEARCH FOR FILE OR FILES --- */
while ((status = sys$search(&fab)) == RMS$_NORMAL)
{
filespec_v = ((filespec_v == NULL) ? xmalloc(sizeof (char *)) :
xrealloc(filespec_v,(filespec_count + 1) * sizeof (char *)));
nam.nam$l_rsa[nam.nam$b_rsl] = '\0';
for (character_p = nam.nam$l_rsa; *character_p; ++character_p)
*character_p = _tolower(*character_p); /* v1.2-004 */
if (strncmp(nam.nam$l_rsa,our_path,nam.nam$b_dev + nam.nam$b_dir) == 0)
{
filespec_v[filespec_count] = xmalloc(
(int) (nam.nam$b_rsl - nam.nam$b_dev - nam.nam$b_dir) + 1
);
(void)strcpy(filespec_v[filespec_count],nam.nam$l_name);
}
else if (strncmp(nam.nam$l_rsa,our_path,nam.nam$b_dev) == 0)
{
filespec_v[filespec_count] = xmalloc(
(int) (nam.nam$b_rsl - nam.nam$b_dev) + 1
);
(void)strcpy(filespec_v[filespec_count],nam.nam$l_dir);
}
else
{
filespec_v[filespec_count] = xmalloc((int) nam.nam$b_rsl + 1);
(void)strcpy(filespec_v[filespec_count],nam.nam$l_rsa);
}
if (filespec_count_p == NULL)
break;
++filespec_count;
} /* sys$search() while */
/* --- UPDATE FILESPEC COUNT AND RETURN FILESPEC VECTOR --- */
if ((status == RMS$_NORMAL) || (status == RMS$_NMF))
{
if (filespec_count_p)
*filespec_count_p = filespec_count;
return filespec_v;
}
} /* sys$parse() if */
/* --- RETURN VECTOR REFERRING TO GIVEN FILESPEC --- */
/* If we get here, we either could not parse the given filespec or could
** not find it. In such cases we simply return a vector that refers to
** the given filespec.
*/
filespec_v = xmalloc(sizeof (char *));
filespec_v[0] = given_filespec;
if (filespec_count_p)
*filespec_count_p = 1;
return filespec_v;
} /* shell_glob() */
/* shell_mung()
**-----------------------------------------------------------------------------
** Functional Description
** ~~~~~~~~~~~~~~~~~~~~~~
** Given a description of the command line that invoked a program from the
** main() function's `argc' and `argv' variables, this function changes those
** variables to reflect wildcard matching of filespecs in that command line
** and changes standard IO file pointers `stderr', `stdin', and `stdout' to
** reflect any IO redirection requested in that command line.
**-----------------------------------------------------------------------------
** Usage
** ~~~~~
** Since shell_mung() is written for programs running VAX C programs under the
** DCL CLI on VMS, a call to shell_mung() should be predicated on these things:
**
** #if defined(VAXC) && defined(VMS)
** if (!shell$is_shell())
** shell_mung(&argc,&argv,...,...);
** #endif
**
** One should call shell_mung() before doing anything else---in particular,
** before anything is done with `argc', `argv', and parts of the standard IO
** library.
** The first and second arguments to shell_mung() should always be addresses
** of `argc' and `argv', respectively. The third and fourth arguments require
** a bit more thought. Before they are explained, though, we have to define
** some terms.
** A "parameter" is a position-dependent command line argument that isn't an
** IO redirection (those start with '<' or '>'), an option or an option value.
** An "option" is similar to a DCL qualifier, except that it starts with '-' or
** '+' instead of '/'. A "value" is a string that follows certain options.
** Which options have values is something that varies from program to program.
** A value in DCL is indicated with a equal sign (e.g. /qualifier=value), but
** here it can either be concatenated to an option (e.g. -ovalue) or appear as
** an argument following the option (e.g. -o value). "Globbing," as mentioned
** above, is to parse wildcarded filespecs and match them with existing files.
** A "mung" is a legume used in Indian cooking, often after being shelled.
** The third argument to shell_mung() is the number of the parameter where we
** expect filespecs to appear---in other words, the parameters we expect to
** need globbing. A GNU-shell-like command language interpreter will glob any
** argument that has a wildcard character in it, unless that argument is put in
** single quotes. DCL doesn't glob arguments, and though you can put arguments
** in double quotes to pass them through exactly as typed, by the time VAX C
** has processed them into `argv' strings, the double quotes have been stripped
** off. This is why we have to tell shell_mung() where to start globbing. The
** assumption is made that unglobbable arguments (such as strings) will appear
** as parameters before globbable arguments (such as filespecs), an assumption
** that holds for most GNU utilities, probably because it's the only rational
** way to code utilities that accept multiple filespecs on the command line.
** As an example, consider the following command line, where "fgrep" is a DCL
** command to run the GNU "fgrep" program:
**
** $ fgrep -n xyzzy *.c *.h
**
** "xyzzy", "*.c", and "*.h" are the parameters. For fgrep, the string to be
** searched for is always the first parameter ("xyzzy" in this case); the files
** to be searched always begin as a second parameter. The "-n" is an option,
** which could be placed anywhere. The following commands mean the same thing
** as the previous one:
**
** $ fgrep xyzzy -n *.c *.h
** $ fgrep xyzzy *.c -n *.h
** $ fgrep xyzzy *.c *.h -n
**
** (It is, however, usually considered better form to type the options first.
** Some programs actually require that certain options appear before certain
** parameters.) To indicate that we expect input filespecs to begin as the
** second parameter, we call shell_mung() with 2 as its third argument:
**
** shell_mung(&argc,&argv,2,...);
**
** This changes `argc' and `argv' so that if the user types this:
**
** $ fgrep -n xyzzy *.c
**
** It's as if the user had typed this (for example):
**
** $ fgrep -n xyzzy dev:[dir]bar.c dev:[dir]baz.c dev:[dir]foo.c
**
** If all of the parameters are to be considered globbable filespecs, the third
** argument to shell_mung() should be 1 (indicating, of course, that globbing
** can start with the first parameter). If no parameters are to be considered
** globbable, shell_mung()'s third argument should be 0. If the user uses a
** negative number as the third argument, it is treated as 0.
** The fourth argument is a list of options which are followed by values, with
** indications as to which values are to be globbed, and which option/value
** combinations are to be considered as replacements for parameters (more on
** this later). The list is passed as a character string.
** As an example, the "make" program has a "-f" option that expects a filespec
** as its value:
**
** $ make [-f makefile] [target] [macros]...
**
** To indicate that we have a "-f" option followed by a globbable value, we put
** 'f' in shell_mung()'s fourth argument:
**
** shell_mung(&argc,&argv,0,"f");
**
** When an option's value is globbed, only the first matching filespec is used
** to replace that value. If, for example, "makefile.vms" and "makefile.xenix"
** are the only two files in your directory, and the user types one of these:
**
** $ make -fmakefile.*
** $ make -f makefile.*
**
** It's as if the user had typed one of these (respectively):
**
** $ make -fdev:[dir]makefile.vms
** $ make -f dev:[dir]makefile.vms
**
** Options with unglobbable values must also be listed, with indications that
** they are unglobbable. The "from" program is an example of this:
**
** $ from [-s sender] [user]
**
** The "-s" option is followed by a username, not an input filespec. Thus we
** put a '-' after the 's' in shell_mung()'s fourth argument:
**
** shell_mung(&argc,&argv,0,"s-");
**
** There are times when an option/value combination can replace a parameter.
** Using fgrep as an example again, its first parameter---the string to be
** searched for---can be replaced by the "-e" and "-f" options and their
** values. The "-e" option is used when the string to be searched for starts
** with a '-' character and could be confused with an option, and the "-f"
** option is used when the string or strings to be searched for is in a file.
** We indicate such options by appending a '+' character to them in the string
** that is shell_mungs()'s fourth argument:
**
** shell_mung(&argc,&argv,2,"e+-f+");
**
** (Note that the "-e" option also has a '-' character following it, since we
** don't want its value to be globbed. When both '+' and '-' are used, the '+'
** should always be first.) It should be noted that such option/value pairs
** should be typed early in the command line, in positions where the parameters
** they're replacing would have been. For example, you would do this:
**
** $ fgrep -f stringfile inputfile
**
** But this would be an invitation to disaster:
**
** $ fgrep inputfile -f stringfile
**
** Programs that use the getopt() library function to process options will
** help you decide which options to include in shell_mung()'s fourth argument.
** The third argument to getopt() is a list of options, and options that have
** values are followed by a ':' character. Here's an example from the source
** to the GNU "grep" program:
**
** while ((c = getopt(argc,argv,"0123456789A:B:CVbce:f:hilnsvwx")) != EOF)
**
** This tells us that the "-A", "-B", "-e", and "-f" options have values. Of
** these, "-A", "-B", and "-e" have unglobbable values; and "-e" and "-f" can
** replace a parameter. Thus, our call to shell_mung() for the GNU grep
** program looks like this:
**
** shell_mung(&argc,&argv,2,"A-B-e+-f+");
**
** The IO redirection capabilities provided are as follows:
**
** <infile Input comes from "infile".
** >outfile Output goes to "outfile".
** >&outfile Output and errors go to "outfile".
** >>outfile Output appended to "outfile".
** >>&outfile Output and errors appended to "outfile".
**
** They can be used anywhere on the command line, except between an option and
** its value. Here are some examples:
**
** $ fgrep xyzzy *.txt >&lines_with_xyzzy.lis
** $ from -s flintstone >>accumulated_flintstone_missives.dat
** $ my_mailer -d <mail_commands.com
**
** Some notes: (1) The filespecs must be next to the redirection characters;
** they can't be separate arguments. (2) For input redirection, the "infile"
** is globbed to the first matching filespec, just like an option's value. (3)
** The "<<" redirection characters are not implemented. Unlike shell scripts,
** VMS DCL command procedures define standard input as records that don't start
** with a '$' character, or aren't the record defined by the DECK command.
** Thus, "<<" redirection is irrelevant. (4) The "!" redirection character as
** used in ">!", ">&!", ">>!", and ">>&!" is also not implemented. VMS uses
** file versions, which makes it irrelevant. (5) If a globbable argument turns
** out to be unglobbable, the new vector will simply point to the original
** argument. (6) Pipes (as in the "|" and "|&" redirection characters) are not
** implemented.
**-----------------------------------------------------------------------------
** Calling Sequence
** ~~~~~~~~~~~~~~~~
** delta-arg-count = shell_mung(argc_p,argv_p,parameter_number,option_string);
**-----------------------------------------------------------------------------
** Formal Arguments Description
** ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
** argc_p Pointer to argument count (main()'s `argc').
** argv_p Pointer to argument vector (main()'s `argv').
** parameter_number Position among parameters where filespecs are expected.
** option_string List of options that may be followed by filespecs.
**-----------------------------------------------------------------------------
** Implicit Inputs: None
**-----------------------------------------------------------------------------
** Implicit Outputs: None
**-----------------------------------------------------------------------------
** Return Value
** ~~~~~~~~~~~~
** Returns the number of new arguments that have been added to the argument
** vector. In other words, the difference between the new value of `argc_p'
** and its old value.
**-----------------------------------------------------------------------------
** Side Effects
** ~~~~~~~~~~~~
** Function may cause the program to exit if it can't open files for error,
** input, and output redirection. Uses global variable `vaxc$errno' for exit
** status.
**-----------------------------------------------------------------------------
*/
int
shell_mung(argc_p,argv_p,parameter_number,option_string)
int * argc_p;
char *** argv_p;
int parameter_number;
char * option_string;
{
/* --- LOCAL VARIABLES --- */
register char * arg_p; /* General-purpose argument pointer.
*/
int filespec_count; /* Number of filespecs in `filespec_v',
** returned from shell_glob().
*/
char ** filespec_v; /* Vector of filespecs returned from
** shell_glob().
*/
char from_err_too = FALSE; /* Flag set to indicate that error text
** will be redirected to a file along
** with the output text.
*/
register int i_new; /* Index to new pointers in `new_argv'.
*/
register int i_old; /* Index to old pointers in `argv_p'.
*/
char ** new_argv; /* New argument vector, which `argv_p'
** will end up being replaced with.
*/
int new_argv_count; /* Count of pointers in `new_argv'.
*/
int output_fd; /* File descriptor for file being
** created as redirected output, for
** use with creat() and dup2().
*/
int parameter_count = 0; /* Counts parameters---arguments that
** don't have '<' or '>' or '-' in
** front of them. When and if the
** count reaches `parameter_number',
** we start attempting to parse the
** parameters as filespecs.
*/
register char * place_p; /* Location of a character, as returned
** from strchr() or strrchr().
*/
/* --- INITIALIZE THINGS --- */
/* Allocate the new argument vector.
*/
new_argv_count = *argc_p;
new_argv = xmalloc(new_argv_count * sizeof (char *));
/* Replace main()'s `argv[0]' with a GNU-like truncated version.
*/
new_argv[0] = truncate_argv_0((*argv_p)[0]);
/* --- TRAVERSE THE ARGUMENT VECTOR --- */
for (i_new = i_old = 1; i_old < *argc_p; ++i_old)
switch (*(arg_p = (*argv_p)[i_old]))
{
/* If it begins with '<', we're redirecting input.
*/
case '<':
filespec_v = shell_glob(++arg_p,NULL);
if (freopen(*filespec_v,"r",stdin) == NULL)
{
perror(*filespec_v);
sys$exit(vaxc$errno | STS$M_INHIB_MSG);
}
free(*filespec_v);
free(filespec_v);
--new_argv_count;
break;
/* If it begins with '>', we're redirecting output. This comes in four
** varieties, as one can append output to an existing file and/or send
** error text out along with the output. If we're told to append it (by
** two '>' characters), we try to access the filespec and append to it.
** If we can't access it, we proceed as if we were told to create one.
** We create an output file (which is also what we're told to do when we
** only get one '>' character) with "standard" VMS attributes. If we're
** told to redirect error text as well (by the '&' character), we set
** `stderr' to refer to the same file.
*/
case '>':
if (*++arg_p == '>')
{
if (*++arg_p == '&')
{
from_err_too = TRUE;
++arg_p;
}
if (access(arg_p,2) == 0)
{
if (freopen(arg_p,"a",stdout) != NULL)
{
if (from_err_too == TRUE)
stderr = stdout;
--new_argv_count;
break;
}
perror(arg_p);
sys$exit(vaxc$errno | STS$M_INHIB_MSG);
}
}
/* We get to this point if we didn't want to append output text to an
** existing file, or if an attempt to establish append access to an
** existing file has failed.
*/
if ((from_err_too == FALSE) && (*arg_p == '&'))
{
from_err_too = TRUE;
++arg_p;
}
if (
((output_fd = creat(arg_p,0,"rat=cr","rfm=var","mrs=512")) != 1)
&& (dup2(output_fd,fileno(stdout)) != -1)
&& ((from_err_too == FALSE) || (dup2(output_fd,fileno(stderr)) != -1))
)
{
--new_argv_count;
break;
}
perror(arg_p);
sys$exit(vaxc$errno | STS$M_INHIB_MSG);
/* If it begins with '-' or '+', it's an option. We check it against
** `option_string' to see if it's an option followed by an argument.
*/
case '-':
case '+':
if (
(option_string == NULL)
|| (((char *) place_p = strchr(option_string,(*(arg_p + 1)))) == 0)
)
{
/* Option is not in `option_string'. We copy it into `new_argv'.
*/
new_argv[i_new++] = arg_p;
}
else
{
/* Option is in `option_string'. First we check to see if its existence
** is to be thought of as introducing a parameter. This is indicated by
** a '+' character following the option in `option_string'.
*/
if (*++place_p == '+')
++parameter_count;
else
--place_p;
/* Next we check to see if the string following it is to be considered an
** input filespec. If a '-' character follows the option in `option_
** string' (or a '+' character following that option), that indicates
** that it's not to be considered as such.
*/
if (*++place_p == '-')
{
new_argv[i_new++] = arg_p;
if ((arg_p = (*argv_p)[++i_old]) == NULL)
goto BREAKOUT;
else /* V1.2-005 */
new_argv[i_new++] = arg_p; /* V1.2-005 */
}
else
{
/* The string after the option is an input filespec, so we glob it.
** But we glob it to return only the first match (if it's a wildcard),
** not all possible matches.
*/
if (*(arg_p + 2) == '\0')
{
new_argv = xrealloc(new_argv,(++new_argv_count * sizeof (char *)));
new_argv[i_new++] = arg_p;
if ((arg_p = (*argv_p)[++i_old]) == NULL)
goto BREAKOUT;
filespec_v = shell_glob(arg_p,NULL);
new_argv[i_new++] = *filespec_v;
free(filespec_v);
}
else
{
filespec_v = shell_glob((arg_p + 2),NULL);
new_argv[i_new] = xmalloc(2 + strlen(*filespec_v) + 1);
memcpy(new_argv[i_new],arg_p,2);
(void)strcpy((new_argv[i_new++] + 2),*filespec_v);
free(*filespec_v);
free(filespec_v);
}
}
}
break;
/* If we get here, the argument is a parameter. If we're not concerned
** about parameters (that is, if `parameter_number' is zero or less), or
** if the parameter count is less than `parameter_number', we simply copy
** the parameter into the new argument vector. Otherwise, the parameter
** and those following it are expected to be filespecs, and an attempt is
** made to parse them as such and put all resulting filespecs into the
** argument vector.
*/
default:
if ((parameter_number <= 0) || (++parameter_count < parameter_number))
new_argv[i_new++] = arg_p;
else
{
filespec_v = shell_glob(arg_p,&filespec_count);
if (filespec_count == 1)
new_argv[i_new++] = *filespec_v;
else
{
new_argv_count += filespec_count - 1;
new_argv = xrealloc(new_argv,(new_argv_count * sizeof (char *)));
memcpy(&new_argv[i_new],filespec_v,(filespec_count * sizeof (char *)));
i_new += filespec_count;
}
free(filespec_v);
}
break;
} /* for/switch */
BREAKOUT:
new_argv[i_new] = NULL;
/* --- UPDATE ARGUMENT COUNT AND ARGUMENT VECTOR --- */
*argc_p = i_new;
*argv_p = new_argv;
return i_new - i_old;
} /* shell_mung() */
/* -=- STATIC FUNCTION -=- */
/* truncate_argv_0
**-----------------------------------------------------------------------------
** Functional Description
** ~~~~~~~~~~~~~~~~~~~~~~
** Programs written in VAX C, when run under VMS DCL, will have their entire
** filespec (i.e., the filespec of the running image) in the main() function's
** `argv[0]' variable. While this makes sense from a VMS standpoint, it does
** not look very good in programs ported from GNU, which often use `argv[0]' as
** an error message prefix.
** This function, which is called from shell_mung(), truncates the filespec in
** `argv[0]' to just the filename or, if the filetype is not ".EXE", to the
** filename and filetype.
**-----------------------------------------------------------------------------
** Calling Sequence
** ~~~~~~~~~~~~~~~~
** new_argv[0] = truncate_argv_0(given_filespec);
**-----------------------------------------------------------------------------
** Formal Argument Description
** ~~~~~~~~~~~~~~~ ~~~~~~~~~~~
** given_filespec The filespec to be truncated. Assumed to be `argv[0]'
** or a pointer to the same thing `argv[0]' points to.
**-----------------------------------------------------------------------------
** Implicit Inputs: None
**-----------------------------------------------------------------------------
** Implicit Outputs: None
**-----------------------------------------------------------------------------
** Return Value
** ~~~~~~~~~~~~
** Returns the truncated filespec, which is in newly-allocated memory.
**-----------------------------------------------------------------------------
** Side Effects
** ~~~~~~~~~~~~
** Memory is allocated for the truncated filespec.
**-----------------------------------------------------------------------------
*/
static char *
truncate_argv_0(given_filespec)
char * given_filespec;
{
/* --- LOCAL VARIABLES --- */
register char * character_p; /* General-purpose character pointer,
** used to lowercase filespec.
*/
char * expanded_filespec; /* v1.2-004 */
/* Buffer to hold the expanded filespec
** from a call to sys$parse().
*/
register char * filespec_p; /* Pointer to truncated filespec to be
** returned.
*/
struct FAB fab = cc$rms_fab; /* RMS file access block for file.
*/
struct NAM nam = cc$rms_nam; /* RMS name block for file.
*/
/* --- INITIALIZE FABS AND NAMS --- */
expanded_filespec = xmalloc(NAM$C_MAXRSS + 1); /* v1.2-004 */
fab.fab$l_fna = given_filespec;
fab.fab$b_fns = strlen(given_filespec);
fab.fab$l_nam = &nam;
nam.nam$l_esa = expanded_filespec; /* v1.2-004 */
nam.nam$b_ess = NAM$C_MAXRSS;
nam.nam$b_nop = NAM$M_SYNCHK;
/* --- PARSE OUT THE GIVEN FILESPEC --- */
sys$parse(&fab);
/* --- NUL-TERMINATE AND LOWERCASE FILESPEC --- */
*nam.nam$l_ver = '\0';
for (character_p = nam.nam$l_esa; *character_p; ++character_p)
*character_p = _tolower(*character_p); /* v1.2-004 */
/* --- RETURN TRUNCATED FILESPEC --- */
if (strcmp(nam.nam$l_type,".exe"))
filespec_p = xmalloc(nam.nam$b_name + nam.nam$b_type + 1);
else
{
*nam.nam$l_type = '\0';
filespec_p = xmalloc(nam.nam$b_name + 1);
}
(void)strcpy(filespec_p,nam.nam$l_name);
return filespec_p;
} /* truncate_argv_0() */