
/**********
 * $Id: cmdparse.c,v 1.18 2002/09/09 10:26:56 mjames Exp $ **********

             One time Author: Kevin Ross (x6143)
                      Philips Semiconductors Limited.
                      Millbrook Industrial Estate, Southampton, SO9 7BH. England.

                Date: 13th May 1993

             Mostly by Mike James these days

             Comment: This program parses the input commands for the SFC3 evaluation software
$Log: cmdparse.c,v $
Revision 1.18  2002/09/09 10:26:56  mjames
Removed set generic range and replaced it with a set generic value command
that takes both integers and ranges.

Revision 1.17  2002/01/16 11:22:41  mjames
database.h header file is read in first as it undefined DLL stuff irrelevant
to HPUX

Revision 1.16  2001/12/20 13:49:48  mjames
force ?

Revision 1.15  2001/12/13 22:14:26  mjames
Corrected nested command handlers to allow variable passing without corruption.

Revision 1.14  2001/10/31 22:20:00  mjames
Tidying up problematical comments caused by CVS
'intelligent' comment guessing

Revision 1.13  2001/10/10 20:19:56  mjames
Adjustment of handling of escapes and comments

Revision 1.12  2001/10/10 10:02:59  mjames
Final tweaks to work with end-of line at end-of-file

Revision 1.11  2001/10/10 09:54:30  mjames
Modified command line handler to correctly recognise
comment leaders anywhere on line, not just as first character.

Revision 1.10  2001/07/09 15:08:44  mjames
Check the last character of a line of text to see if it is actually a control code
before deleting it

Revision 1.9  2001/06/19 05:24:57  mjames
Created a trap_fopen to overcome trying to write to read only files.
If this attempted in NT the file can be opened but not written to.

Revision 1.8  2001/06/06 12:10:24  mjames
Move from HPUX

Revision 1.7  2001/05/01 15:21:18  mjames
Added comment leaders on printout of commands executed

Revision 1.6  2001/04/06 22:47:01  mjames
Added doc2, the creator of documentation to Vertical scripts uses PERL


Also correcting generic behaviour and the printing of Verilog.

Revision 1.5  2001/02/06 22:41:14  mjames
Added correct argument passing for 'read file comp_suffix arg0 arg1 arg2 ...

Revision 1.4  2001/01/26 21:50:10  mjames
Managed to get vertical non TCL to compile again

Conversion to argv, argc[] mode of operation continues

Revision 1.3  2001/01/04 21:26:54  mjames
Modifications to add in the TCL style
argument list to all of the functions
.

Revision 1.2  2001/01/02 07:53:52  mjames
Made changes to allow for interface with TCL/Tk

Revision 1.1.1.1  2000/10/19 21:58:35  mjames
Mike put it here

There has been a lot more development, but the comments have been
removed in order to cut down the junk

*********************************************************************************************************/

#include "cmdparse.h"

#include "cmdexec.h"
#include "cmdlog.h"
#include "cmdutil.h"
#include "database.h"
#include "expression.h"
#include "generic.h"
#include "vertcl_main.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* used for dynamic string allocation/deallocation code
 * used here for command keywords May 2 2000 */
#include "lx_support.h"

/*********************************************************************************************************/
#ident                                                                                        \
    "@(#)$Header: /cygwin/cvsroot/vertical/cmdparse.c,v 1.18 2002/09/09 10:26:56 mjames Exp $"

/******************************************* Execute a command
 * ******************************************/
/* define flag for global control of execution mode
 * can be Tcl_Cmd or  Tcl_GUI : default to be Tcl_Cmd for
 * backwards compatibility */

tcl_mode_t tcl_mode = Tcl_Cmd;

#define WORDWIDTH 100 /* max command word  ever expected */
#define CMDWIDTH 20   /*Field width for printing CMD word */
int Execute (int argc, char *argv[], struct command Dispatch[])
{
        int i, j, FinalResult = UNKNOWNCMD;

        if (!argc)
                return (NARGS);
        if (argv[0][0] == '?')
        {
                printf ("Commands available;\nHelp on:");
                for (i = 0; i < argc; i++)
                        printf ("%s ", argv[i]);
                putchar ('\n');
                for (i = 0; Dispatch[i].name; i++)
                {
                        int p;
                        for (j = 0; j < Dispatch[i].NumChar;
                             j++) /* upper case significant chars */
                                putchar (toupper (Dispatch[i].name[j]));
                        for (; Dispatch[i].name[j];
                             j++) /* and all of the rest, we just print */
                                putchar (Dispatch[i].name[j]);
                        putchar (' ');
                        j++;
                        if (Dispatch[i].extras)
                        {
                                p = j;
                                for (; Dispatch[i].extras[j - p];
                                     j++) /* and all of the extras field we just print */
                                        putchar (Dispatch[i].extras[j - p]);
                        };
                        for (; j < CMDWIDTH; j++) /* create spacing , relies on auto - wrap */
                                putchar (j & 1 ? '.' : ' ');
                        putchar (' ');
                        puts (Dispatch[i].helpstr);
                }
                putchar ('\n');
                FinalResult = OKCMD; /* ? is a valid command at this level */
        }
        else
        {
                for (i = 0; Dispatch[i].name; i++)
                        if (strncmp (argv[0], Dispatch[i].name, Dispatch[i].NumChar) == 0)
                        {
                                /* Dispatch the next level command */
                                if (Dispatch[i].function)
                                { /* call function if there */
                                        FinalResult = (*Dispatch[i].function) (
                                            Tcl_interp, NULL, argc - 1, argv + 1);
                                }
                                else /* call down the menu */
                                        FinalResult =
                                            Execute (argc - 1, argv + 1, Dispatch[i].Menu);
                                break;
                        }
        }
        return (FinalResult);
}

/******************************************** Strip off words
 * ********************************************/
/* this needs repairing . This code is unsafe.
 * Needs to keep a record of how many chars copied !!
 * and stop when in danger of a buffer overflow.
 *
 * Did not work properly if expanded strings are longer than the
 * original strings. This now fixed.
 *
 * Code modified to use some string allocation functions in lx_support.c
 */
/*char wordbuff[1024];
 */
typedef enum
{
        SPACES,
        CHARS
} Phase_t;

int FindWords (
    char *linebuff,
    int *pargc,
    char *argv[],
    int nArgs,
    char *Args[],
    struct str **cmd_first,
    struct str **cmd_last)
{
        char *WordPtr;
        char *BasePtr;
        int argc = 0;
        int term = ' ', escape, comment, done_word, chars_seen;
        char *copy_buff;
        Phase_t Phase;

        copy_buff = malloc (strlen (linebuff) + 1); /* make a safe copy */

        /*      for (i=0; i<Len; i++)                               */
        /*         Cmd->Text[i] = tolower(Cmd->Text[i]);            */ /* convert text to lower
                                                                          case */
        WordPtr = linebuff; /* set LineStart to beginning of string */
        escape = 0;
        comment = 0;
        /* Now split off the words */
        BasePtr = NULL;
        while (*WordPtr && argc < WORDSINLINE && comment < 2)
        {
                BasePtr = copy_buff;
                Phase = SPACES;
                term = ' ';
                done_word = 0;
                chars_seen = 0;
                while (*WordPtr && !done_word && comment < 2)
                {
                        switch (Phase)
                        {
                        case SPACES:
                                if (isspace (*WordPtr)) /* skip leading spaces */
                                        WordPtr++;
                                else
                                        Phase = CHARS;
                                break;
                        case CHARS:
                                if (!escape && *WordPtr == '\\')
                                {
                                        escape = 1;
                                        WordPtr++;
                                }
                                else
                                {
                                        if (term == ' ' && !escape &&
                                            (*WordPtr == '"' || *WordPtr == '\''))
                                        { /* sigle or double quotes ? */
                                                term = *WordPtr++;
                                                chars_seen = 1;
                                        }
                                        else
                                        {
                                                /* handle ordinary chars */
                                                if (!escape && term == ' ' &&
                                                    WordPtr[0] == '#')
                                                {
                                                        comment = 2;
                                                        done_word =
                                                            chars_seen; /* comment is appended
                                                                           to valid chars */
                                                        WordPtr++;
                                                }
                                                else if (
                                                    !escape && term == ' ' &&
                                                    WordPtr[0] == '-')
                                                {
                                                        comment++;
                                                        WordPtr++;
                                                        if (comment == 2)
                                                                done_word =
                                                                    chars_seen; /* comment is
                                                                                   appended to
                                                                                   valid chars
                                                                                 */
                                                }
                                                else if (
                                                    (*WordPtr == '\r') || (*WordPtr == '\n') ||
                                                    (!escape && (*WordPtr == term)))
                                                {
                                                        if (comment == 1)
                                                                *BasePtr++ = '-';
                                                        comment = 0;
                                                        Phase = SPACES;
                                                        *BasePtr = '\0';
                                                        done_word = 1;
                                                        WordPtr++;
                                                }
                                                else
                                                {
                                                        if (escape)
                                                                *BasePtr++ = '\\';
                                                        if (comment == 1)
                                                                *BasePtr++ = '-';
                                                        chars_seen = 1;
                                                        comment = 0;
                                                        escape = 0;
                                                        *BasePtr++ = *WordPtr++;
                                                }
                                        }
                                }
                                break;
                        } /* switch */
                }         /* while */
                *BasePtr = '\0';
                /* make_string now performs an expand_string call */
                if (chars_seen)
                {
                        argv[argc++] = make_string (copy_buff, cmd_first, cmd_last);
                }
        }
        *pargc = argc;
        free (copy_buff);

        return (OKCMD);
}

/********************************* executes the command from stdin or a do file
 * **************************/

int ExecuteCommand (FILE *CmdFile, int nArgs, char *Args[])
{
        char Buff[PATHLENGTH];
        int i, Status;
        char *p;
        int argc;
        char *argv[WORDSINLINE];
        struct str *cmd_first, *cmd_last;
        cmd_first = NULL;
        cmd_last = NULL;

        if (!CmdFile || feof (CmdFile))
                return (QUITCMD);

        p = fgets (Buff, PATHLENGTH - 1, CmdFile); /* read the command from the input stream */
        argc = 0; /* place the command in the command structure */
        if (p == NULL || *Buff == '\0')
                return (OKCMD);

        /* convert control codes to spaces */
        i = 0;
        while (Buff[i])
        {
                if (Buff[i] < ' ')
                        Buff[i] = ' ';
                i++;
        }

        FindWords (Buff, &argc, argv, nArgs, Args, &cmd_first, &cmd_last); /* split off the
                                                                              words and save in
                                                                              structure */
        if (1 || CmdFile != stdin)
        {
                printf ("-- ");
                for (i = 0; i < argc; i++)
                        printf (
                            "%s ",
                            ISNULLSTR (argv[i]) ? "\"\""
                                                : argv[i]); /* log the command word by word if
                                                               its not given interactively */
                printf ("\n");                              /*  */
        }
        /* the code below is not as helpful as it seems . Moved recognizer for comments into
           Execute as the comments can appear at any point on the command line */
        /*   if (argc ==0 ||
               argv[0][0] == '-' ||
               argv[0][0] == '#') */
        if (argc == 0)
        {
                /* deallocate any previous structures */
                free_strings (&cmd_first, &cmd_last);
                return (OKCMD); /* comment or a null string */
        }

        Status =
            Execute (argc, argv, TopMenu); /* pass the command through the top-level menu */
        switch (Status)
        {
        case UNKNOWNCMD:
                Log (LOG_ERROR, "# Unknown command\n");
                break;
        case NARGS:
                Log (LOG_ERROR, "# Too few parameters\n");
                break;
        case FAILED:
                Log (LOG_ERROR, "# Problem with command\n");
                break;
        };

        if (Status != OKCMD)
        {
                Log (LOG_ERROR, "-- ? ");
                for (i = 0; i < argc; i++) /*    display offending command */
                        Log (LOG_ERROR, " %s", ISNULLSTR (argv[i]) ? "\"\"" : argv[i]);

                Log (LOG_ERROR, "\n");
        };

        /* deallocate any previous structures */
        free_strings (&cmd_first, &cmd_last);

        return (Status);
}

/********************************* Execute a command string
 * ******************************************/
int ExecuteString (char *commandstring, int nArgs, char *Args[])
{
        int argc = 0;
        char *argv[WORDSINLINE];
        int Status = 0;

        struct str *cmd_first, *cmd_last;
        cmd_first = NULL;
        cmd_last = NULL;
        FindWords (
            commandstring, &argc, argv, nArgs, Args, &cmd_first, &cmd_last); /* split off the
                                                                                words and save
                                                                                in structure */
        Status =
            Execute (argc, argv, TopMenu); /* pass the command through the top-level menu */
                                           /* deallocate any previous structures */
        free_strings (&cmd_first, &cmd_last);
        return (Status);
}

/********************************* A parser for the '*.do files
 * ******************************************/
static int Depth = 0;
#define MAXDEPTH 10 /* maximum depth of 'do' files */
#define MAXARGV 10
int ParseFile (FILE *CmdFile, int nArgs, char *Args[])
{
        int Status = 0;
        char *argp[MAXARGV];
        char **argp_prev;
        int nArgs_prev;
        int i;
        /* diagnosis */
        if (nArgs > MAXARGV)
                nArgs = MAXARGV;
        if (nArgs < 0)
                nArgs = 0;
        /* belt and braces null check */
        if (!CmdFile)
                return (FAILED);

        if (feof (CmdFile))
                return (FAILED);

        for (i = 0; i < MAXARGV; i++)
        {
                if (i < nArgs)
                        argp[i] = strdup (Args[i]);
                else
                        argp[i] = NULL;

                /* printf("Arg [%2d]='%s'\n",i,argp[i]);  */
        }
        nArgs_prev = yy_nArgs;
        argp_prev = yy_Args;
        yy_nArgs = nArgs;
        yy_Args = argp;

        Depth++; /* keep a count of recursion depth */
        printf ("-- Command file nesting level = %d\n", Depth);
        if (Depth > MAXDEPTH)
        {
                Log (LOG_ERROR, "Recursive call of files is greater than %d\n", MAXDEPTH);
                Depth--;
                return (FAILED);
        }
        while (Status == OKCMD) /* execute the command from do file input stream */
                Status = ExecuteCommand (CmdFile, nArgs, argp);
        Depth--;
        yy_nArgs = nArgs_prev;
        yy_Args = argp_prev;
        for (i = 0; i < MAXARGV; i++)
                if (argp[i])
                {
                        free (argp[i]);
                        argp[i] = NULL;
                }
        return (Status);
}

/*********************************************************************************************************/