/* acf4:comp.os.vms / jym@remarque.berkeley.edu (Jym Dyer) / 5:27 pm Aug 24, 1990 .-. |N|OTE: There's some worthwhile freeware at the end of this `-' message. .-. |T|he I/O redirection you ask about is simply a matter of reassigning `-' the default file descriptors and file pointers to your new files. Attached below is a library function that mungs your `argv' data to make your command lines work something like Unix (which uses "<" to redirect input, ">" to redirect output, etc.). .-. |T|his is a deluxe approach to it---it globs wildcarded filespecs `-' on the command line. A simpler public domain library function is DECUS C's getredirection(). Maybe somebody will dig that up and post it. .-. |A| handy enhancement you might wish to make to this is to redefine `-' SYS$INPUT, SYS$OUTPUT, and SYS$ERROR (in user mode, of course) to your redirected places. That will be in my next version of this, along with pipes and improved options processing. Once I have tons of free time, that is. :^) <_Jym_> -------------8<-----------cut-hereabouts---------8<---------------*/ #module SHELL_MUNG "Jym V1.2-003" /* Only VAX C understands this. */ /* 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-003 **----------------------------------------------------------------------------- ** 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} **============================================================================= */ /* -=- FILE INCLUSIONS -=- */ #include #include #include #include #include #include #include #include #include /* -=- 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 -=- */ globaldef char * shell_default = NULL; /* Default filespec for shell_glob(). ** 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. */ char expanded_filespec[NAM$C_MAXRSS + 1]; /* 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. */ char resultant_filespec[NAM$C_MAXRSS + 1]; /* 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 --- */ if (our_path == NULL) 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; nam.nam$b_ess = NAM$C_MAXRSS; nam.nam$l_rsa = &resultant_filespec; 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); 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 ); 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 ); strcpy(filespec_v[filespec_count],nam.nam$l_dir); } else { filespec_v[filespec_count] = xmalloc((int) nam.nam$b_rsl + 1); 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: ** ** 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 !", ">&!", ">>!", 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) || ((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 { /* 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); 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[NAM$C_MAXRSS + 1]; /* 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 --- */ fab.fab$l_fna = given_filespec; fab.fab$b_fns = strlen(given_filespec); fab.fab$l_nam = &nam; nam.nam$l_esa = &expanded_filespec; 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); /* --- 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); } strcpy(filespec_p,nam.nam$l_name); return filespec_p; } /* truncate_argv_0() */ /* ---------- */