home *** CD-ROM | disk | FTP | other *** search
- /*
- * comsub.c - routines to perform command substitution and environment variable
- * parsing and expansion
- *
- * Author: R. Brittain 4/11/90
- * This code placed in the public domain
- *
- */
- #include <string.h>
- #include <ctype.h>
- #include <stdlib.h>
-
- #include "comsub.h"
-
- int rebuild_argv(int *argc, char ***argv)
- {
- /*
- * Build a new argv array, by iteratively performing backquote expansion
- * and environment variable expansion until no more is possible.
- * We always replace argv[] at least once, because the routine terminates it
- * with an extra null, which is useful later for exec()ing.
- * Returns the number of substitutions performed
- */
- int i, count = 0, subst;
-
- count += expand_backquotes(argc, argv);
- do {
- subst = FALSE;
- /* look for any argument beginning with BKQ or ENV */
- for (i=0; i < *argc; i++) {
- if (*(*argv)[i] == BKQ || *(*argv)[i] == ENV ) {
- subst = TRUE;
- break;
- }
- }
- if (subst) count += expand_backquotes(argc, argv);
- } while (subst);
- return(count);
- }
-
-
- int expand_backquotes(int *argc, char ***argv)
- {
- /*
- * Perform command substitution if any argument begins with BKQ (`)
- * Perform environment variable expansion if an argument begins with ENV (%)
- * Returns the number of substitutions performed
- * This routine would be simpler if backquoted commands were always parsed
- * as a single command line argument, but doing it here means we don't need
- * to modify the startup code.
- */
- char **base, *p, *result;
- estring command = {NULL, NULL, 0, 80};
- int i, j, status = 0;
- FILE *fp;
-
- MEMCHECK(base = (char **) malloc(sizeof(char **))) ;
- *base = NULL;
- for (i=0, j=0; i < *argc; i++) {
- if (*(*argv)[i] == BKQ) {
- /*
- * first character is a BKQ - we have a request for command
- * substitution so build command in an estring structure
- */
- status++;
- MEMCHECK( addstring(&command, (*argv)[i]+1, 0) );
- if (*command.b == EXE) {
- set_popen_exec();
- command.b++;
- } else {
- set_popen_shell();
- }
- /*
- * if this argument ends with a second BKQ, remove it
- * otherwise, scan forward for an argument terminated by BKQ,
- * appending to command as we go
- */
- if (*(p = endptr(command.b)-1) == BKQ) {
- *p = '\0';
- } else {
- do {
- /* increment argument pointer and append to current command */
- if ( i++ >= *argc) fatal("Unterminated command substitution",1);
- /* we ran out of arguments before finding BKQ */
- MEMCHECK(addstring(&command," ",1));
- MEMCHECK(addstring(&command,(*argv)[i], 0));
- } while (*(endptr((*argv)[i])-1) != BKQ);
- /* now stomp on of the trailing backquote */
- if (*(p = endptr(command.b)-1) == BKQ) *p = '\0';
- }
-
- /* perform variable substitution then run the command via popen */
- command.b = expand_env(command.b);
- if ((fp = popen(command.b, "r")) == NULL) {
- fputs (command.b, stderr);
- fatal(": popen failed: \n",1);
- }
- result = mfgets(fp);
- pclose(fp);
-
- /* now parse <result> for whitespace and treat as new arguments */
- p = strtok(result," \t");
- while (p != NULL) {
- MEMCHECK(base = (char **)realloc(base,(j+2)*sizeof(char **))) ;
- MEMCHECK(base[j++] = strdup(p)) ;
- base[j] = (char *)NULL;
- p = strtok(NULL," \t");
- }
-
- } else if (*(*argv)[i] == ENV) {
- /* we have a request for environment substitution */
- status++;
- result = expand_env((*argv)[i]);
- /* now parse <result> for whitespace and treat as new arguments */
- p = strtok(result," \t");
- while (p != NULL) {
- MEMCHECK(base = (char **)realloc(base,(j+2)*sizeof(char **))) ;
- MEMCHECK(base[j++] = strdup(p)) ;
- base[j] = (char *)NULL;
- p = strtok(NULL," \t");
- }
-
- } else {
- /* grab one more pointer, add to base array, and copy this argument */
- MEMCHECK(base = (char **)realloc(base,(j+2)*sizeof(char **))) ;
- base[j++] = (*argv)[i];
- base[j] = (char *)NULL;
- }
- }
- *argv = base;
- *argc = j;
- return(status);
- }
-
-
- char *endptr(p)
- char *p;
- {
- while (*p) p++;
- return p;
- }
-
-
- char *mfgets (FILE *stream)
- {
- /*
- * Suck in the entire file, replacing newlines by spaces
- * and allocating memory as needed
- */
- estring contents = {NULL, NULL, 0, 132};
- char line[132];
-
- if (feof(stream)) return NULL;
- while (fgets(line,sizeof(line),stream) != NULL) {
- if (*(endptr(line)-1) == '\n') *(endptr(line)-1) = ' ';
- addstring(&contents,line,strlen(line));
- }
- return(contents.b);
- }
-
-
- char *expand_env(char *s)
- {
- /*
- * Scan string s for '%' (or ENV) and expand environment variables as found
- * Return a pointer to the expanded string (which may be the same as the input)
- * The input string is left intact
- */
- char *r;
- estring result = {NULL, NULL, 0, 128};
- estring var = {NULL, NULL, 0, 64};
-
- result.p = result.b;
- if (strchr(s,ENV) == NULL) {
- /*
- * nothing to do
- */
- return(s);
- } else {
- /*
- * we have some vars to substitute - parse into words by whitespace
- * and examine each one
- */
- while (*s) {
- if (*s == ENV) {
- /* some work to do */
- if (*(++s) == ENV) {
- /* double ENV */
- MEMCHECK(addstring(&result, s++, 1));
- } else {
- /* look for next whitespace or ENV */
- var.b = var.p;
- while (*s && *s != ENV && !isspace(*s)) {
- addstring(&var, s++, 1);
- }
- /* copy over the variable value, if non null */
- if ((r = getenv(strupr(var.b))) != NULL)
- MEMCHECK(addstring(&result,r,strlen(r)));
- /* examine how the var name was terminated */
- if (*s == ENV && isspace(*(s+1))) {
- /* redundant trailing ENV - skip over */
- s++;
- }
- }
- } else {
- /* just copy characters */
- MEMCHECK(addstring(&result, s++, 1));
- }
- }
- return(result.b);
- }
- }
-
- char *addstring(estring *es, char *add, int count)
- {
- /*
- * Add <count> characters from string <add> to estring <es>
- * <es> is extended automatically as needed
- * Returns a pointer to the start of the string (NULL if an expansion failed)
- * If the count is zero, then it is taken to be strlen(add) by default
- */
- if (count == 0) count = strlen(add);
-
- if (es->p - es->b + count >= es->len) {
- if (es->p != NULL) {
- /* we are expanding */
- *es->p = '\0';
- es->b = (char *)realloc(es->b, es->len + max(es->inc, count+1));
- if (es->b == (char *)NULL) return (NULL);
- es->p = endptr(es->b);
- es->len += es->inc;
- } else {
- /* we are making initial allocation */
- es->p = es->b = (char *)calloc(max(es->inc,count+1), 1);
- if (es->b == (char *)NULL) return (NULL);
- es->len = es->inc;
- }
- }
- strncpy(es->p, add, count);
- es->p += count;
- *es->p = '\0';
- return(es->b);
- }