#
# comments start with a # sign
#
# each item in the make file consists of
# three things:  a product, its ingredients and how to make it
#
# gcc -o test test.c -lcurses


CC = gcc -g -Wall

smsh: smsh.o varlib.o splitline.o
	$(CC) smsh.o varlib.o splitline.o -o smsh
        
smsh.o: smsh.c smsh.h
	$(CC) -c smsh.c

varlib.o: varlib.c varlib.h
	$(CC) -c varlib.c

splitline.o: splitline.c
	$(CC) -c splitline.c

______

/*//////
//
// header for smsh.c package
//
*/

#ifndef _SMSH_H_
#define _SMSH_H_

#include	<stdio.h>
#include        <stdlib.h>
#include	<signal.h>
#include        <strings.h>
#include        <unistd.h>
#include        <sys/wait.h>
#include        <errno.h>
#include	"varlib.h"
#include        "splitline.h"

#define	MAXARG		20
#define	MAXCMDLEN	512
#define	DFL_PROMPT	"> "
#define	TRUE            1
#define	FALSE           0
#define	YES             1
#define	NO              0

#define	NORMAL          0
#define	NEEDTHEN        1
#define	NEEDELSE        2
#define	GOTTHEN         3
#define	GOTELSE         4
#define	NEEDFI          5

/* Error handling for fatal errors */
#define oops(errStr)  { perror(errStr); exit(1); }


/*
// stuff we'll be using
*/

void setup();
void readCmd(char *varname);
int  getKeyInput(char *buffer);
void cdCmd(char *pathname);
void fatal(char *errStr);
int  handleCommand(int argc, char *argv[]);
int  get_next_command( char *prompt, char *buffer, FILE *input_stream);
int  execute(char *argv[]);
void gracefulExit(int exitVal);

#endif /* _SMSH_ */

____


/*
//
//  smsh.c ( adapted from Bruce's smsh4.c )
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Five
//
//       Usage: smsh [script file name]
//
//  [var]=[value]  Assigns an internal var to a value
//            set  prints a list of all saved values
//         export  exports a value
//   exit [value]  exits smsh with [value]
//     read [var]  reads into [var] an assignment input from the keyboard
//  cd [pathname]  changes the current working directory
//  if [list ;then list ... [;else list] ;fi]
//                 Executes the list following if and, if it returns a 0
//                 (zero)exit status, executes the list following the first
//                 then. Otherwise, the else list is executed.
//
// Description: Create a Unix shell that allows users to manage processes
//              such as running programs and controlling input.
//              The shell has its own scripting language with variables and
//              if statements.
//
//      Method: Main reads from file or stdin, taking input and sending it to
//              splitline (splitline.c) to have variables substituted and to
//              be parsed into arguments.  varlib (varlib.c) is used to keep 
//              track of all variables added by the user, and to 
//              get unix environment vars.
//              Once arguments have been isolated they are sent to
//              handleCommand to be processed by an appropriate function,
//              in most cases a command is external, in which case it is
//              handled by execute(), who forks and exec's the command.
//              handleCommand also deals with IF related statements, keeping
//              careful track of their progress and only allowing the
//              correct then/else blocks to be processed.
//
//              smsh.c methods:
//
//                main - main checks for a script file for input, otherwise
//                       uses stdin input. the loop reads input line by line.  
//                       Each line has variables ($VAR)subsititued, then
//                       is split into its individual arguments.
//                       Finally, the arg count and all args are pased into
//                       handleCommand() which returns a state. If this state
//                       is != NORMAL on exit then there is a
//                       scripting bug ("no fi")
//               setup - All initialization goes here.
//       handleCommand - Works out where to pass the args to.
//                       Internal commands are set to their own specific
//                       function, external commands are delt with by execute().
//                       More importantly, handleCommand deals with the 
//                       possibility of if statements, by use of an 'ifState'
//                       var.  This var keeps track of the progress of the
//                       if statement.  HandleCommand returns the ifState,
//                       which main needs, in case it reaches EOF without
//                       a concludeing fi.
//               fatal - Fatal error message to stderr then exit (never returns)
//        gracefulExit - cleanup smsh before exiting
//    get_next_command - read next line from input_stream.  Return FALSE on EOF
//             execute - execute commands in argv.
//                       Uses fork to make a child duplicate to exe
//                       if sucessful exe will exit itself. information of
//                       child is held in child_info
//             readCmd - Assigns internal var [varname] equal to keyboard input
//         getKeyInput - Reads input from the keyboard, placing it in buffer
//               cdCmd - cd command, calls chdir with a path.
//              report - Report of important vars in info from child
//                       Good for debugging, this function is not used by smsh
//
//              see top of splitline.c for its methods.
//
//
//       Notes: VLtable2environ in execute calls malloc, but memory is
//              free()d with exec
*/


        
#include	"smsh.h"

extern char **environ;

/*
//  main
//
//     main checks for a script file for input, otherwise uses stdin input.
//     the loop reads input line by line.  Each line has variables ($VAR)
//     subsititued, then is split into its individual arguments
//     Finally, the arg count and all args are pased into handleCommand()
//     handleCommand returns a state.  If this state is != NORMAL on exit
//     then there is a scripting bug ("no fi")
//
//     args:  ac         --  argument count
//            **arg      --  pointer to arguments
*/
int main(int ac, char **arg)
{
    char    *argv[MAXARG + 1], 
             orgCmdline[MAXCMDLEN],                 /* original command line  */
             modCmdline[MAXCMDLEN];                 /* modified command line  */            
    int	     argc, cmdState;
    FILE    *inputVia = stdin;
    
    setup();                                        /* initalize smsh         */
    
    if (ac > 1) {                                   /* check for a filename   */
        if ( ! (inputVia = fopen(arg[1],"r")))      /* open file in arg[1],   */
            perror( arg[1] );                       /* couldn't open          */
    }
    
    /*
    // main loop
    */
    while ( get_next_command( DFL_PROMPT,
                              orgCmdline,
                              inputVia )) {         /* get input              */
        
        if ( ! substituteVars(orgCmdline,
                              modCmdline) )         /* substitute vars        */
            continue;                               /* bug... get next line   */
        
        if ( ! splitline(modCmdline,
                         &argc,
                          argv,
                          MAXARG) )                 /* split input            */
	    continue;                               /* bug... get next line   */
        
	cmdState = handleCommand( argc, argv );     /* deal with command      */
    }
    
    if (cmdState != NORMAL)                         /* error. no fi           */
        fatal("unexpected error: IF structure without FI");
    
    exit(0);

}

/*
//  setup
//
//     All initialization goes here.
*/
void setup()
{
    /* block ctrl c and ctrl backslash */
    signal(SIGINT,  SIG_IGN); // ctr c
    signal(SIGQUIT, SIG_IGN); // ctr backslash

    /* init the variable table */
    if ( VLenviron2table(environ) == 0 )
        exit(1);
}


/*
//  handleCommand
//
//     Works out where to pass the args to.
//     Internal commands are set to their own specific function,
//     external commands are delt with by execute().
//     More importantly, handleCommand deals with the possibility of if
//     statements, by use of an 'ifState' var.  This var keeps track of the
//     progress of the if statement.  HandleCommand returns the ifState,
//     which main needs, in case it reaches EOF without a concludeing fi.
//
//     args:  argc        --  argument count
//            *argv[]     --  list of args
*/
int handleCommand(int argc, char *argv[])
{
    char *cp;
    int static ifState = NORMAL;                    /* ifstates are:
                         NORMAL, NEEDTHEN, NEEDELSE, GOTTHEN,GOTELSE,NEEDFI   */

    if ( (cp=strchr(argv[0], '=')) != NULL ){       /* var=val assignment     */
        *cp = '\0';
        VLstore(argv[0], cp+1);                     /* store var              */
    }
    else if (! strcmp(argv[0], "set"))              /* set command            */
        VLset();
    else if (! strcmp(argv[0],"export") && argc == 2) /* export cmd           */
        VLexport(argv[1]);
    else if (! strcmp(argv[0],"exit")){             /* exit cmd?              */
        if (argc == 2)
            gracefulExit(atoi(argv[1]));            /* exit clean with arg    */
        else
            gracefulExit(1);                        /* exit clean             */
    }
    else if (! strcmp(argv[0], "read")  && argc == 2) /* read cmd?            */
        readCmd(argv[1]);
    else if (! strcmp(argv[0], "cd")    && argc == 2) /* cd cmd               */
        cdCmd(argv[1]);
    else if ( strcmp(argv[0], "if")     == 0 ){
        if ( execute( ++argv ) == 0 )               /* exec cmd after 'if'    */
             ifState = NEEDTHEN;                    /*...looking for then     */
        else
             ifState = NEEDELSE;                    /*...looking for else     */
    }
    else if ( strcmp(argv[0], "then")   == 0 ) {    /* got 'then'             */
        if ( ifState == NEEDTHEN )
             ifState =  GOTTHEN;
        else
            fatal("unexpected error: THEN without an IF");
    }
    else if ( strcmp(argv[0], "else")   == 0 ) {    /* got 'else'             */
        if ( ifState == NEEDELSE )
             ifState =  GOTELSE;
        else if ( ifState == GOTTHEN )
             ifState =  NEEDFI;                     /* need 'fi'              */
    }
    else if ( strcmp(argv[0], "fi")     == 0 ) {    /* got 'fi'               */
        if ( ifState == NEEDFI )
             ifState = NORMAL;
        else if ( ifState == GOTELSE )
             ifState = NORMAL;
        else                                        /* ...FI too early        */
            fatal("unexpected error: bad IF structure"); 
    }
    else {
        if ( ifState == NEEDTHEN)                   /* ...no THEN             */
            fatal("unexpected error: IF structure without THEN");
        if ( ifState == NORMAL  || 
             ifState == GOTTHEN || 
             ifState == GOTELSE )
        execute( argv );                            /* call external cmd      */
    }

    return ifState;                                 /* return the ifstate     */
}


/*
//  fatal
//
//     Fatal error message to stderr then exit (never returns)
//
//     args:  errStr      --  error message to display
*/
void fatal(char *errStr)
{
    fprintf(stderr, "%s\n", errStr);
    gracefulExit(1);
}


/*
//  gracefulExit
//
//     cleanup smsh before exiting
//
//     args:  exitVal      --  value to exit with
*/
void gracefulExit(int exitVal)
{
    signal(SIGINT, SIG_DFL);                      /* fix ctrl c               */
    signal(SIGQUIT, SIG_DFL);                     /* fix ctrl backslash       */
    
    exit(exitVal);                                /* die                      */
}


/*
//  get_next_command
//
//     read next line from input_stream.  Return FALSE on EOF
//
//     args: *prompt         --  symbol to use for prompt
//           *buffer         --  string buffer
//           *input_stream   --  source of the input (a file or stdin)
*/
int get_next_command( char *prompt, char *buffer, FILE *input_stream )
{
    if (input_stream == stdin)
        printf("%s", prompt);                      /* prompt user             */

    if ( fgets(buffer, MAXCMDLEN, input_stream) )  /* get line                */
    {
        buffer[strlen(buffer) - 1] = '\0';         /* remove nl               */
        return TRUE;                               /* say ok                  */
    }
    return FALSE;                                  /* no more                 */
}


/*
//  execute
//
//     execute commands in argv
//     fork makes a child duplicate to exe
//     if sucessful exe will exit itself.
//     information of child is held in child_info
//
//     args: *argv[]    --  pointer to arguments for exe
*/
int execute(char *argv[])
{

    int	pid = fork(),                              /* fork                    */
        child_info,
        myReturn = FALSE;
                                  
    if ( pid == -1 )
        perror("fork");
    else if ( pid == 0 ){                          /* child(new) process      */

        signal(SIGINT, SIG_DFL);                   /* set ctl chars, etc      */
        signal(SIGQUIT, SIG_DFL);

        /* "The header comment for VLtable2environ() in varlib.c says you
        // need to free this tbale to prevent memory leaks. That comment only
        // holds true if you call the functon within the parent process. If
        // you call the function only in the child,
        // the memory is free()d with exec." */
        environ = VLtable2environ();
        
        execvp(argv[0], argv);                     /* execute the args, execvp
                                                      auto exits if sucessful */

        perror("cannot execute command");          /* exe did not work        */
        gracefulExit(1);                           /* die                     */
    }
    else {                                         /* parent(old) process     */
        if ( wait( &child_info) == -1 )            /* wait for child to exit  */
	    perror("wait");
        else
            myReturn = (child_info >> 8);          /* return child's exit code*/
    }

    return myReturn;
}



/*
//  readCmd
//
//     Assigns internal var [varname] equal to keyboard input
//
//            *varname         --  name of variable being assigned
*/
void readCmd(char *varname)
{
    char myInput[MAXCMDLEN];                        /* string to hold input   */

    if (validVarName (varname)) {                   /* ensure legal var name  */
        getKeyInput(myInput);                       /* read input             */
        VLstore(varname, myInput);                  /* store var              */
    }
    else
        printf("%s: is not an identifier\n", varname);
}

/*
//  getKeyInput
//
//     Reads input from the keyboard, placing it in buffer
//
//            *buffer         --  buffer for string
*/
int getKeyInput(char *buffer)
{
    if ( fgets(buffer, MAXCMDLEN, stdin) )         /* get line	              */
    {
        buffer[strlen(buffer) - 1] = '\0';         /* remove nl	              */
        return TRUE;                               /* say ok	              */
    }
    return FALSE;                                  /* no more                 */
}


/*
//  cdCmd
//
//     cd command,  calls chdir with a path.
//
//     args: *pathname         --  path to switch directory to.
*/
void cdCmd(char *pathname)
{
    if ( chdir(pathname) == -1 )                   /* open file in arg[1],    */
        perror( pathname );                        /* couldn't open           */
}


/*
//  report
//
//     Report of important vars in info from child
//     Good for debugging, this function is not used by smsh
//
//     args:  info         --  information created by wait()
*/
void report( int info )
{
	int	from_exit, core_flag, signal_num;

	signal_num = info & 0x7F ;
	core_flag  = (info >> 7 ) & 1;
	from_exit  = (info >> 8) ;

	if ( signal_num != 0 ){
		printf("\n[child died from signal %d]", signal_num);
		if ( core_flag ) printf(" (core dumped)" );
	}
	else
		printf("\n[child exited with code %d]", from_exit);
	putchar('\n');
}



_____


/*//////
//
// header for varlib.c package
//
*/

#ifndef _VARLIB_H_
#define _VARLIB_H_

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include        "smsh.h"

int	VLenviron2table(char **);
int	VLexport(char *);
char	*VLlookup(char *);
void	VLset();
int	VLstore( char *, char * );
char	**VLtable2environ();
int	VLenviron2table(char **);

#endif /* _VARLIB_ */



_______


/*/////////
//
//  varlib.c 
//
//      AUTHOR: Bruce's varlib.c modified by Russell Lowke
//   CSCIE-215: Assignment Five
//
//       Usage: a simple storage system to store name=value pairs
//              with facility to mark items as part of the environment
//
//   interface: VLstore( name, value )    returns 1 for 0k, 0 for no
//              VLlookup( name )          returns string or NULL if not there
//              VLset()                   prints out current table
//              VLexport( name )          adds name to list of env vars
//              VLtable2environ()         copy from table to environ
//              VLenviron2table()         copy from environ to table
//
//     details: the table is stored as an array of structs that
//	        contain a flag for `global' and a single string of
//	        the form name=value.  This allows EZ addition to the
//              environment.  It makes searching pretty easy, as
//              long as you search for "name=" 
//
*/

#include	"varlib.h"

#define	MAXVARS	200		/* a linked list would be nicer */

struct var
	{
	char *str;		/* name=val string	*/
	int  global;		/* a boolean		*/
	};

/* local vars: the table */
static struct var tab[MAXVARS];

/* local functions */
static char *new_string( char *, char *);
static struct var *find_item(char *, int);


/*
//  VLstore
//
//     traverse list, if found, replace it, else add at end
//     since there is no delete, a blank one is a free one
//     return 0 if trouble, 1 if ok
//
//     args:  *name         --  var name to store
//            *val          --  value to set
*/
int VLstore( char *name, char *val )
{
    struct var *itemp;
    char	*s;
   
    if (! validVarName (name)) {                    /* ensure legal var name  */
        printf("%s: is not an identifier\n", name);
        return 0;
    }
        
    if ( (itemp = find_item(name,1)) == NULL )      /* find spot to put it    */
        return 0;

    if ( itemp->str )                               /* chuck old value        */
        free(itemp->str);

    if ( ( s = new_string( name, val )) == NULL )   /* new name=val           */
        return 0;				    /* no memory              */
    
    itemp->str = s;                                 /* store it               */
    return 1;
}


/*
//  new_string
//
//     returns new string of form name=value or NULL on error
//
//     args:  *name        --  name of variable
//            *val         --  value of variable
*/
char *new_string( char *name, char *val )
{
    char	*retval;

    retval = malloc( strlen(name) + strlen(val) + 2 );
    if ( retval != NULL )
        sprintf(retval, "%s=%s", name, val );
    return retval;
}


/*
//  VLlookup
//
//     returns value of var or empty string if not there
//
//     args:  *name         --  name of var to look up
*/
char *VLlookup( char *name )
{
    struct var *itemp;

    if ( (itemp = find_item(name,0)) != NULL )
        return itemp->str + 1 + strlen(name);
    return "";
}


/*
//  VLexport
//
//     marks a var for export, adds it if not there
//     returns 0 for no, 1 for ok
//
//     args:  *name     --  name of var to export
*/
int VLexport( char *name )
{
	struct var *itemp;

	if ( (itemp = find_item(name,0)) != NULL ){
		itemp->global = 1;
		return 1;
	}
	if ( VLstore(name, "") == 1 )
		VLexport(name);
	return 0;
}


/*
//  find_item
//
//     searches table for an item
//     returns ptr to struct or NULL if not found
//     OR if (first_blank) then ptr to first blank one
//
//     args:  *name         --  variable name to look up
//            first_blank   --  ptr to first blank one
*/
static struct var *find_item( char *name , int first_blank )
{
    int   i;
    int   len = strlen(name);
    char *s;

    for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
    {
        s = tab[i].str;
        if ( strncmp(s,name,len) == 0 && s[len] == '=' ){
            return &tab[i];
        }
    }
    if ( i < MAXVARS && first_blank )
        return &tab[i];
    return NULL;
}


/*
//  VLset
//
//     performs the shell's  `set'  command
//     Lists the contents of the variable table, marking each
//     exported variable with the symbol  '[E]' 
*/
void VLset()
{
	int	i;
	for(i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
	{
		if ( tab[i].global )
			printf("[E] %s\n", tab[i].str);
		else
			printf("    %s\n", tab[i].str);
	}
}


/*
//  VLenviron2table
//
//     initialize the variable table by loading array of strings
//     return 1 for ok, 0 for not ok
//
//     args:  *env[]         --  environment list
*/
int VLenviron2table(char *env[])
{
	int     i;
	char	*newstring;

	for(i = 0 ; env[i] != NULL ; i++ )
	{
		if ( i == MAXVARS )
			return 0;
		newstring = malloc(1+strlen(env[i]));
		if ( newstring == NULL )
			return 0;
		strcpy(newstring, env[i]);
		tab[i].str = newstring;
		tab[i].global = 1;
	}
	while( i < MAXVARS ){		/* I know we don't need this	*/
		tab[i].str = NULL ;	/* static globals are nulled	*/
		tab[i++].global = 0;	/* by default			*/
	}
	return 1;
}


/*
//  VLtable2environ
//
//     build an array of pointers suitable for making a new environment
//     note, you need to free() this when done to avoid memory leaks
*/
char **VLtable2environ()
{
	int	i,			/* index			*/
		j,			/* another index		*/
		n = 0;			/* counter			*/
	char	**envtab;		/* array of pointers		*/

	/*
	// first, count the number of global variables
	*/

	for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
		if ( tab[i].global == 1 )
			n++;

	/* then, allocate space for that many variables	*/
	envtab = (char **) malloc( (n+1) * sizeof(char *) );
	if ( envtab == NULL )
		return NULL;

	/* then, load the array with pointers		*/
	for(i = 0, j = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ )
		if ( tab[i].global == 1 )
			envtab[j++] = tab[i].str;
	envtab[j] = NULL;
	return envtab;
}


_______


/*//////
//
// header for splitline.c package
//
*/

#ifndef _SPLITLINE_H_
#define _SPLITLINE_H_

#include	<stdio.h>
#include	"smsh.h"
#include	"varlib.h"
#define	BUFLEN	512

/*
// stuff we'll be using
 */
int splitline(char *cmdline, int *argcp, char *argv[], int max);
int substituteVars(char *oldStr, char *newStr);
int validVarName (char *varName);
int isValidVarChar (char c);
void printCharArray(char mArray[], int arraySize);

#endif /* _SPLITLINE_ */



______



/*/////////
//
//  splitline.c ( adapted from Bruce's splitline.c )
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Five
//
//       Usage: a component of smsh
//              Contains methods to parse strings and general string
//              handling, also has routines to check for valid variable
//              names.
//
//     Methods: 
//
//           splitline - parse line [cmdline] into an array of strings
//                       cmdline has a string of white-space separated tokens
//                       put the addresses of the tokens in the array argv[]
//                       put their number in *argcp and do not put more than max
//                       in argv or suffer dire consequences!
//                       NOTE: this modifies cmdline
//                       returns FALSE on too many args or zero args.
//                       TRUE for ok stuff
//      substituteVars - search through original string [orgStr] looking for
//                       embeded vars ('$') and backslash ('\\').  
//                       In case of backslash read the next char 'as is',
//                       otherwise, in case of viarable, substitute in the
//                       variables value.
//                       Put the result into new string [newStr].
//        validVarName - Test string [varName] to see if it is a legal variable
//                       name. Done primarily using isValidVarChar - but also
//                       tests for 1st char not being a number
//      isValidVarChar - Check if a char [c] is considered legal when in a
//                       variable name.
//                       Legal values are: numbers from 0 through 9
//                                        A - Z, a - z, and underscore.
//
*/

#include	"splitline.h"

/*
//  splitline
//
//     parse line [cmdline] into an array of strings
//     cmdline has a string of white-space separated tokens
//     put the addresses of the tokens in the array argv[]
//     put their number in *argcp and do not put more than max 
//     in argv or suffer dire consequences!
//     NOTE: this modifies cmdline
//     returns FALSE on too many args or zero args.  TRUE for ok stuff
//
//     args:  *cmdline    --  line to be parsed into array
//            *argcp      --  argument count
//            *argv[]     --  list of args
//            max         --  limit
*/
int splitline(char *cmdline, int *argcp, char *argv[], int max)
{
    int   i      = 0,
          retval = FALSE;
    char  mode   = ' ',
         *cmdp   = cmdline;

    while ( i<=max ){			            /* still room?            */
						    /* skip leading space     */
        while ( *cmdp == ' ' || *cmdp == '\t' )
            cmdp++;
					
        if ( *cmdp == '\0' )                        /* at end of string?      */
            break;

        if ( *cmdp == '\"' ) {                      /* start of a quote?      */
            cmdp++;                                 /* bump pointer           */
            mode = '\"';                            /* change to quote mode   */
        }
						    /* record string          */
        argv[i++] = cmdp ;		            /* and bump counter       */
		
                                                 /* move to end of word quote */
        while ( *++cmdp && *cmdp != mode && *cmdp != '\t' )
            ;

        mode = ' ';                                 /* revert mode to space   */

        if ( *cmdp != '\0' )		            /* past end of word       */
            *cmdp++ = '\0';                         /* terminate string       */
    }

    if ( i > max )
        printf("Too many args\n");
    else if ( i > 0 ){
        argv[i] = NULL ;                            /* mark end of array      */
       *argcp   = i;                                /* and store argc         */
        retval  = TRUE ;                            /* say ok                 */
    }
    
    return retval ;
}


/*
//  substituteVars
//
//     search through original string [orgStr] looking for embeded vars
//     ('$') and backslash ('\\').  In case of backslash read the next char
//     'as is', otherwise, in case of viarable, substitute in the variables
//     value. Put the result into new string [newStr].
*/
int substituteVars(char *orgStr, char *newStr)
{       
    char *tmpPtr,
          varName[BUFLEN];
    int i = 0;
    
    while (*orgStr) {
        if (*orgStr == '\\') {                      /* check for backslash    */
             orgStr++;                              /* move to next char      */
            *newStr++ = *orgStr++;                  /* add char to new str    */
        }
        else if (*orgStr == '$') {
            
            orgStr++;                               /* skip '$'               */
            while (isValidVarChar (*orgStr))        /* read valid chars       */
                varName[i++] = *orgStr++;
            
            varName[i] = '\0';                      /* terminate varName      */
            
            if (validVarName(varName)) {            /* ensure as valid        */
                tmpPtr = VLlookup(varName);

                while (*tmpPtr)
                    *newStr++ = *tmpPtr++;
            }

            i = 0;                         /* reset varName for other strings */
            
        }
        else
            *newStr++ = *orgStr++;                  /* add char to new str    */
    }
               
     *newStr = '\0';                                /* terminate newString    */
    
    return TRUE;
}

/*
//  validVarName
//
//     Test string [varName] to see if it is a legal variable name.
//     Done primarily using isValidVarChar - but also tests for 1st char not
//     being a number
//
//     args:  *varName  --  string being tested
*/
int validVarName (char *varName)
{      
    if (*varName >= '0' && *varName <= '9' )        /* numbers in first char? */
          return FALSE;

   while(*varName) {                                /*...use isValidVarChar   */
       if ( ! isValidVarChar( *varName ) )
           return FALSE;
       varName++;
   }

   return TRUE;                                     /* name OK                */
}

/*
//  isValidVarChar
//
//     Check if a char [c] is considered legal when in a variable name.
//     Legal values are: numbers from 0 through 9
//                       A - Z, a - z, and underscore.
//
//     args:  c         --  the char [c] being checked
//
//  Note: the case of the first char being a number 
//        is tested for in validVarName
*/
int isValidVarChar (char c) 
{    
    if ( (c < '0' ) ||                              /* check illegal chars    */
         (c > '9' && c < 'A' ) ||
         (c > 'Z' && c < 'a' && c != '_') ||
         (c > 'z') )
        return FALSE;
    else
        return TRUE;                                /* char [c] OK            */
}


________