char *tecstate_c_version = "tecstate.c: $Revision: 1.3 $";
/*
* $Date: 2007/12/26 13:28:31 $
* $Source: /cvsroot/videoteco/videoteco/tecstate.c,v $
* $Revision: 1.3 $
* $Locker: $
*/
/**
* \file tecstate.c
* \brief Main SWITCH/CASE statements to implement the parser syntax stage
*/
/*
* Copyright (C) 1985-2007 BY Paul Cantrell
*
* 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 3 of the License, 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, see .
*/
#include "teco.h"
#include "tecparse.h"
extern char immediate_execute_flag;
extern char trace_mode_flag;
extern char suspend_is_okay_flag;
/**
* \brief Continue the parse with the supplied character
*
* We re-enter the parser with a new character at this point. We jump back
* to the state we left before.
*/
void
parse_input_character( struct cmd_token *ct, struct cmd_token *uct )
{
char tmp_message[LINE_BUFFER_SIZE];
register struct cmd_token *oct = NULL;
PREAMBLE();
switch(ct->ctx.state){
/*
* Here on initial command state. This is the begining of a command, so we are
* looking for arguments that might go with a command. If there are no args,
* we just transfer into the main command loop.
*/
case STATE_C_INITIALSTATE:
ct->ctx.flags &= ~(CTOK_M_COLON_SEEN | CTOK_M_ATSIGN_SEEN);
ct->flags |= TOK_M_WORDBOUNDARY;
case STATE_C_ACCEPT_ARGS:
ct->ctx.carg = NULL;
switch(ct->input_byte){
/*
* If it looks like an argument, then transfer into a subexpression parser to
* get the value of the expression. ARG1 will stuff the result into iarg1 and
* also check for a comma (',') incase he is specifing a twin argument command
*/
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'Q': case 'q': case '%':
#ifdef CLASSIC_B_BEHAVIOR
case 'B': case 'b':
#endif
case 'Z': case 'z':
case '\\':
case '.':
case '-':
case '(':
case '^':
ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
ct->ctx.state = STATE_C_EXPRESSION;
ct->flags &= ~TOK_M_EAT_TOKEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_ARG1;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
/*
* H is a special case, since the single argument actually implies two args.
* (H actually is the same as 0,z)
*/
case 'H': case 'h':
ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = YES;
ct->ctx.state = STATE_C_MAINCOMMANDS;
ct->execute_state = EXEC_C_HVALUE;
return;
/*
* Here on the @ command. This says to use user specified delimeters for
* strings, rather than just terminating with escape.
*/
case '@':
ct->ctx.flags |= CTOK_M_ATSIGN_SEEN;
ct->ctx.state = STATE_C_ACCEPT_ARGS;
return;
/*
* Here on the : command. This is just a flag to many commands to tell them
* to work in a slightly different way. The most common usage is to mean that
* the command should return a value which says whether it worked or not.
*/
case ':':
ct->ctx.flags |= CTOK_M_COLON_SEEN;
ct->ctx.state = STATE_C_ACCEPT_ARGS;
return;
/*
* Well, it doesn't look like he is going to be specifying any arguments, so we
* just go and decode the command.
*/
default:
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
ct->ctx.state = STATE_C_MAINCOMMANDS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
return;
}/* End IF */
ct->ctx.state = STATE_C_MAINCOMMANDS;
ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
/*
* Here to parse the major commands (i.e., ones that have no lead-in)
*/
case STATE_C_MAINCOMMANDS:
switch(ct->input_byte){
/*
* Space is a nop so that it can be used to make macros more readable
*/
case ' ':
ct->ctx.state = STATE_C_INITIALSTATE;
return;
/*
* Carriage Return is a nop so that it can be used in a macro to avoid
* extremely long lines wrapping.
*/
case '\n':
ct->ctx.state = STATE_C_MAINCOMMANDS;
return;
/*
* Here on an escape. First of all, we have to remember that we have seen an
* escape since two in a row will terminate the command. Also, escape has the
* effect of blocking arguments from commands i.e. 2L will move two lines, but
* 2$L will only move one since the escape eats the argument.
*/
case ESCAPE:
ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
ct->ctx.state = STATE_C_ESCAPESEEN;
return;
/*
* Here on an Asterisk command. This causes the last double-escaped command
* sequence to be saved in the named q-register.
*/
case '*':
if(parse_any_arguments(ct,"*")) return;
ct->ctx.state = STATE_C_SAVECOMMAND;
return;
/*
* Here on the @ command. This says to use user specified delimeters for
* strings, rather than just terminating with escape.
*/
case '@':
ct->ctx.flags |= CTOK_M_ATSIGN_SEEN;
return;
/*
* Here on the : command. This is just a flag to many commands to tell them
* to work in a slightly different way. The most common usage is to mean that
* the command should return a value which says whether it worked or not.
*/
case ':':
ct->ctx.flags |= CTOK_M_COLON_SEEN;
return;
/*
* Here on the [ command. This causes the specified Q register to be pushed
* onto the Q register pushdown stack.
*/
case '[':
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
ct->ctx.state = STATE_C_PUSH_QREGISTER;
return;
/*
* Here on the ] command. This causes a Q register to be popped off of the
* Q register pushdown stack, and replaces the specified Q register.
*/
case ']':
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
ct->ctx.state = STATE_C_POP_QREGISTER;
return;
/*
* The B command moves backwards by lines. It is strictly a Video TECO
* enhancement. In normal TECO, the B command returns the address of the
* begining of the buffer, i.e. 0.
*/
case 'B': case 'b':
if(parse_more_than_one_arg(ct,"B")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_RLINE;
return;
/*
* Here on the C command. The C command is a relative move command, i.e., the
* argument says how many spaces from the current position to move.
*/
case 'C': case 'c':
if(parse_more_than_one_arg(ct,"C")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_CHAR;
return;
/*
* The D command deletes the specified number of characters in the indicated
* direction.
*/
case 'D': case 'd':
if(parse_more_than_one_arg(ct,"D")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_DELETE;
return;
/*
* Here on an E command. This is simply the lead-in to any one of the many
* E commands.
*/
case 'E': case 'e':
ct->ctx.state = STATE_C_ECOMMAND;
return;
/*
* Here on an F command. This is a lead-in to one of the F? commands.
*/
case 'F': case 'f':
ct->ctx.state = STATE_C_FCOMMAND;
return;
/*
* The G command copies the contents of the specified q-register into the
* edit buffer at the current position.
*/
case 'G': case 'g':
if(parse_any_arguments(ct,"G")) return;
ct->ctx.state = STATE_C_GQREGISTER;
return;
/*
* The I command inserts characters until an escape is input. If this is
* entered with the tab command, not only do we enter insert mode, we also
* insert the tab itself.
*/
case '\t':
if(ct->ctx.iarg1_flag == NO) ct->flags &= ~TOK_M_EAT_TOKEN;
case 'I': case 'i':
if(parse_more_than_one_arg(ct,"I")) return;
if(ct->ctx.iarg1_flag == YES){
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_INSERT;
return;
}/* End IF */
if(ct->ctx.flags & CTOK_M_ATSIGN_SEEN){
ct->ctx.state = STATE_C_ATINSERT;
return;
}/* End IF */
ct->ctx.state = STATE_C_INSERT;
return;
/*
* Here on the J command. The J command jumps to an absolute position in the
* buffer.
*/
case 'J': case 'j':
if(parse_more_than_one_arg(ct,"J")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_JUMP;
return;
/*
* The K command acts just like the L command except that it deletes instead
* of moving over. Also, it is legal to specify an a,b range to K
*/
case 'K': case 'k':
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_KILL;
return;
/*
* The L command moves over the specified number of new-line characters. An
* argument of zero means get to the begining of the current line.
*/
case 'L': case 'l':
if(parse_more_than_one_arg(ct,"L")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_LINE;
return;
/*
* The M command executes the contents of the specified Q register as a macro.
*/
case 'M': case 'm':
ct->ctx.state = STATE_C_MQREGISTER;
return;
/*
* The N command will search for the specified string across multiple edit
* buffers.
*/
case 'N': case 'n':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_NSEARCH;
return;
/*
* The O command goes to the specified label
*/
case 'O': case 'o':
if(parse_any_arguments(ct,"O")) return;
ct->ctx.state = STATE_C_GOTO;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_GOTO_BEGIN;
return;
/*
* The P command selects the next window
*/
case 'P': case 'p':
if(parse_more_than_one_arg(ct,"P")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_NEXT_WINDOW;
return;
/*
* The R command is exactly like the C command, except that the direction
* is reversed.
*/
case 'R': case 'r':
if(parse_more_than_one_arg(ct,"R")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_RCHAR;
return;
/*
* The S command will search for the specified string
*/
case 'S': case 's':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_SEARCH;
return;
/*
* The ^L command is temporarily used to redraw the screen
*/
case '\f':
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_REDRAW_SCREEN;
return;
/*
* The U command loads the argument into the specified Q register
*/
case 'U': case 'u':
if(parse_more_than_one_arg(ct,"U")) return;
ct->ctx.state = STATE_C_UQREGISTER;
return;
/*
* The V command deletes words. This is a Vido TECO enhancement. In normal
* TECO, the V command was equivalent to 0TT
*/
case 'V': case 'v':
if(parse_more_than_one_arg(ct,"V")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_DELWORD;
return;
/*
* The W command moves by words
*/
case 'W': case 'w':
if(parse_more_than_one_arg(ct,"W")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_WORD;
return;
/*
* The X command moves a block of characters from the edit buffer into
* the specified q register.
*/
case 'X': case 'x':
ct->ctx.state = STATE_C_XQREGISTER;
return;
/*
* The Y command deletes words in reverse direction. This is a Video TECO
* enhancement. In classic TECO, the Y command 'yanked' input data into the
* edit buffer.
*/
case 'Y': case 'y':
if(parse_more_than_one_arg(ct,"Y")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_RDELWORD;
return;
/*
* The ^A command allows you to print a message to the message line
*/
case CNTRL_A:
if(parse_any_arguments(ct,"^A")) return;
ct->ctx.state = STATE_C_MESSAGE;
ct->execute_state = EXEC_C_RESET_MESSAGE;
return;
/*
* The < command opens an iteration
*/
case '<':
if(parse_more_than_one_arg(ct,"<")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_ITERATION_BEGIN;
ct->ctx.inest += 1;
ct->execute_state = EXEC_C_ITERATION_BEGIN;
return;
/*
* The > command closes an iteration
*/
case '>':
if(parse_more_than_one_arg(ct,">")) return;
if(ct->ctx.inest == 0){
error_message("?No Iteration Present");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_ITERATION_END;
while((oct = oct->prev_token)){
if(oct->opcode != TOK_C_ITERATION_BEGIN) continue;
if(oct->ctx.inest != ct->ctx.inest) continue;
ct->ctx.caller_token = oct;
break;
}/* End While */
/*
* Preserve the arguments so that if the loop gets undone, it will get redone
* the correct number of iterations when the > is typed again.
*/
{
register struct undo_token *ut;
ut = allocate_undo_token(ct);
if(ut == NULL){
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ut->opcode = UNDO_C_PRESERVEARGS;
ut->iarg1 = oct->ctx.iarg1;
ut->iarg2 = oct->ctx.iarg2;
ut->carg1 = (char *)oct;
ct->ctx.inest -= 1;
ct->execute_state = EXEC_C_ITERATION_END;
ct = allocate_cmd_token(ct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_COPYTOKEN;
return;
}
/*
* The ; command terminates an iteration if the argument is >= 0.
* If there is no argument supplied, it uses the state of the last
* search operation performed as a value.
*/
case ';':
if(parse_more_than_one_arg(ct,";")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_SEMICOLON;
return;
/*
* The = command prints out the value of the supplied expression.
* A single = prints out in decimal, == prints in octal and === in hex.
*/
case '=':
if(parse_more_than_one_arg(ct,"=")) return;
ct->ctx.state = STATE_C_ONE_EQUALS;
ct->execute_state = EXEC_C_EQUALS;
return;
/*
* The " command is the conditional 'IF' operator. The following commands only
* get executed if the condition is satisfied.
*/
case '"':
if(parse_more_than_one_arg(ct,"\"")) return;
ct->ctx.state = STATE_C_CONDITIONALS;
ct->ctx.cnest += 1;
return;
/*
* The | command provides an else clause to a conditional expression
*/
case '|':
if(parse_any_arguments(ct,"|")) return;
if(ct->ctx.cnest <= 0){
error_message("?Not in a conditional");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_SKIP_ELSE;
ct->execute_state = EXEC_C_SKIP_ELSE;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
/*
* The ' command ends a conditional expression.
*/
case '\'':
if(parse_any_arguments(ct,"'")) return;
if(ct->ctx.cnest <= 0){
error_message("?Not in a conditional");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_CONDITIONAL_END;
ct->ctx.cnest -= 1;
return;
/*
* The Label command allows you to set a tag which can be jumped to with
* the 'O' command. It also provides a way to comment macros.
*/
case '!':
if(parse_any_arguments(ct,"!")) return;
ct->ctx.state = STATE_C_LABEL;
ct->execute_state = EXEC_C_SKIPLABEL;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_LABEL_BEGIN;
return;
/*
* The backslash command with an argument inserts the decimal
* representation of the argument into the buffer at the current position.
*/
case '\\':
if(ct->ctx.iarg2_flag == NO){
ct->ctx.iarg2 = 10;
ct->flags |= TOK_M_PARSELOAD_IARG2;
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_BACKSLASH;
return;
/*
* The open-brace command copies the current command string into a special
* Q-register and places the user there so he can edit it.
*/
case '{':
if(parse_any_arguments(ct,"{")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_OPENBRACE;
return;
/*
* The close-brace command replaces the command string with the string in
* the special Q-register.
*/
case '}':
if(parse_any_arguments(ct,"}")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_CLOSEBRACE;
return;
/*
* Here on a system which doesn't really do suspend correctly
*/
case CNTRL_Z:
if(suspend_is_okay_flag == YES){
cmd_suspend();
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
return;
/*
* Here to toggle trace mode. This inserts information into q-register ?
* to help us track execution.
*/
case '?':
trace_mode_flag = !trace_mode_flag;
{
char traceString[ 64 ];
if( trace_mode_flag ) strcpy( traceString, "Trace Mode ON" );
else strcpy( traceString, "Trace Mode OFF" );
screen_message( traceString );
}
ct->ctx.state = STATE_C_INITIALSTATE;
return;
/*
* We only get here on an error, since we should never dispatch a command that
* does not have a corresponding case statement.
*/
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,
"?MAINCOMMANDS: unknown command <%c>",
UPCASE((int)ct->input_byte));
error_message(tmp_message);
return;
}/* End Switch */
/*
* Here when we have seen the begining of a conditional '"'. Now we have to
* look at the next character to determine what the actual condition test is.
*/
case STATE_C_CONDITIONALS:
switch(ct->input_byte){
case 'G': case 'g':
case '>':
ct->execute_state = EXEC_C_COND_GT;
break;
case 'L': case 'l':
case 'T': case 't':
case 'S': case 's':
case '<':
ct->execute_state = EXEC_C_COND_LT;
break;
case 'E': case 'e':
case 'F': case 'f':
case 'U': case 'u':
case '=':
ct->execute_state = EXEC_C_COND_EQ;
break;
case 'N': case 'n':
case '!':
ct->execute_state = EXEC_C_COND_NE;
break;
case 'C': case 'c':
ct->execute_state = EXEC_C_COND_SYMBOL;
break;
case 'D': case 'd':
ct->execute_state = EXEC_C_COND_DIGIT;
break;
case 'A': case 'a':
ct->execute_state = EXEC_C_COND_ALPHA;
break;
case 'V': case 'v':
ct->execute_state = EXEC_C_COND_LOWER;
break;
case 'W': case 'w':
ct->execute_state = EXEC_C_COND_UPPER;
break;
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,
"?Unknown conditional command '%c'",
UPCASE((int)ct->input_byte));
error_message(tmp_message);
return;
}/* End Switch */
ct->ctx.state = STATE_C_INITIALSTATE;
return;
/*
* Here to watch for the end of a label
*/
case STATE_C_LABEL:
switch(ct->input_byte){
case '!':
ct->ctx.state = STATE_C_INITIALSTATE;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_LABEL_END;
return;
default:
ct->ctx.state = STATE_C_LABEL;
return;
}/* End Switch */
/*
* Here to eat the characters in a GOTO command
*/
case STATE_C_GOTO:
switch(ct->input_byte){
case ESCAPE:
ct->ctx.state = STATE_C_INITIALSTATE;
ct->flags &= ~TOK_M_EAT_TOKEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_GOTO_END;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->execute_state = EXEC_C_GOTO;
return;
default:
ct->ctx.state = STATE_C_GOTO;
return;
}/* End Switch */
/*
* Here if we have seen an escape. If we see another, then he wants us to
* complete the parse and execute any remaining commands.
*/
case STATE_C_ESCAPESEEN:
switch(ct->input_byte){
case ESCAPE:
if(ct->ctx.inest){
error_message("?Unterminated Iteration");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
if(ct->ctx.cnest){
error_message("?Unterminated Conditional");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_FINALSTATE;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->opcode = TOK_C_FINALTOKEN;
return;
default:
ct->ctx.state = STATE_C_INITIALSTATE;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
/*
* Here if we have seen an E as a lead-in to one of the E commands.
*/
case STATE_C_ECOMMAND:
switch(ct->input_byte){
/*
* The EB command creates a new edit buffer and reads the specified file in.
* If the command is given an argument, then this is a shorthand switch to
* the buffer that has that number.
*/
case 'B': case 'b':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
if(ct->ctx.iarg1_flag == YES){
ct->execute_state = EXEC_C_EDITBUF;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_EDITBUF;
return;
/*
* The EC command allows you to execute a command, and the output is placed
* into the edit buffer.
*/
case 'C': case 'c':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_ECCOMMAND;
return;
/*
* The EF command closes the current edit buffer. If the current buffer
* is modified, the command will fail unless the user specifies an argument
* to the command.
*/
case 'F': case 'f':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->execute_state = EXEC_C_CLOSEBUF;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The EI command allows you to alter immediate execution mode. With no
* argument, execute mode is turned off for the remainder of the current
* command. An argument of 0 turns it off until further notice and an
* argument of 1 turns it back on.
*/
case 'I': case 'i':
if(parse_more_than_one_arg(ct,"EI")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->ctx.iarg1_flag == NO){
ct->ctx.go_flag = NO;
return;
}/* End IF */
ct->execute_state = EXEC_C_SET_IMMEDIATE_MODE;
return;
/*
* The EJ command allows you to load runtime options into TECO.
*/
case 'J': case 'j':
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_SETOPTIONS;
return;
/*
* The EP command splits the current window in half. If an argument is
* supplied, the command deletes the current window.
*/
case 'P': case 'p':
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_WINDOW_CONTROL;
return;
/*
* The EV command works exactly like the EB command except that the buffer
* which is created is 'readonly'. Thus this command stands for 'VIEW' file.
*/
case 'V': case 'v':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
if(ct->ctx.iarg1_flag == YES){
ct->execute_state = EXEC_C_VIEWBUF;
ct->ctx.state = STATE_C_INITIALSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_VIEWBUF;
return;
/*
* The EQ command reads a file into the specified Q-register.
*/
case 'Q': case 'q':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
if(parse_any_arguments(ct,"EQ")) return;
ct->ctx.state = STATE_C_EQQREGISTER1;
return;
/*
* The ER command reads the specified file into the current buffer location.
*/
case 'R': case 'r':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_READFILE;
return;
/*
* The ES command causes the screen to scroll
*/
case 'S': case 's':
if(parse_more_than_one_arg(ct,"ES")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_SCROLL;
return;
/*
* The ET command causes the screen to update
*/
case 'T': case 't':
if(parse_any_arguments(ct,"ET")) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_UPDATE_SCREEN;
return;
/*
* The EW command writes out the current buffer
*/
case 'W': case 'w':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_WRITEFILE;
return;
/*
* The EX command causes the editor to exit
*/
case 'X': case 'x':
ct->execute_state = EXEC_C_EXITCOMMAND;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->ctx.go_flag = NO;
return;
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,
"?Unknown command E%c",UPCASE((int)ct->input_byte));
error_message(tmp_message);
return;
}/* End Switch */
/*
* Here if we have seen an F as a lead-in to one of the F commands.
*/
case STATE_C_FCOMMAND:
switch(ct->input_byte){
/*
* The FD command finds the string and deletes it.
*/
case 'D': case 'd':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_FDCOMMAND;
return;
/*
* The FK command clears the text between the current position and the position
* of the searched for text. The searched for text is left unmodified.
*/
case 'K': case 'k':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_FKCOMMAND;
return;
/*
* The FS command finds the first string and replaces it with the second
*/
case 'S': case 's':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_FSPART1;
return;
/*
* The FT command is a Video TECO extension to read & use a unix style
* tags file.
*/
case 'T': case 't':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_FTAGS;
return;
/*
* The FR command acts like the FS command except that if the second argument
* is NULL, the string is replaced with the last replace value.
*/
case 'R': case 'r':
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
ct->ctx.flags |= CTOK_M_STATUS_PASSED;
}/* End IF */
ct->execute_state = EXEC_C_SEARCH;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->execute_state = EXEC_C_FRREPLACE;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
ct = allocate_cmd_token(ct);
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* Here when we have an 'F' command with an unknown second character. This
* makes it an illegal command, and we don't waste any time letting the user
* know about it.
*/
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,
"?Unknown command F%c",UPCASE((int)ct->input_byte));
error_message(tmp_message);
return;
}/* End Switch */
/*
* The following state is the return-state from STRING when the first part
* of an FS command has been parsed.
*/
case STATE_C_FSPART1:
ct->ctx.state = STATE_C_FSPART2;
ct->execute_state = EXEC_C_SEARCH;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->execute_state = EXEC_C_FSREPLACE1;
return;
case STATE_C_FSPART2:
ct->flags |= TOK_M_WORDBOUNDARY;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_FSPART3;
return;
case STATE_C_FSPART3:
if(ct->ctx.flags & CTOK_M_ATSIGN_SEEN){
if(ct->input_byte == ct->ctx.delimeter){
ct->ctx.state = STATE_C_ESCAPESEEN;
ct->execute_state = EXEC_C_FSREPLACE3;
return;
}
else {
ct->ctx.state = STATE_C_FSPART3;
ct->execute_state = EXEC_C_FSREPLACE2;
return;
}
}
if(ct->input_byte == ESCAPE){
ct->ctx.state = STATE_C_ESCAPESEEN;
ct->execute_state = EXEC_C_FSREPLACE3;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
ct = allocate_cmd_token(ct);
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
}
else {
ct->ctx.state = STATE_C_FSPART3;
ct->execute_state = EXEC_C_FSREPLACE2;
return;
}
#ifdef WIMPY_CODER_WHO_HAS_NOT_HANDLED_THIS_YET
case CNTRL_V:
ct->ctx.state = STATE_C_QUOTED_INSERT;
return;
#endif /* WIMPY_CODER_WHO_HAS_NOT_HANDLED_THIS_YET */
/*
* This state is the return state from STRING and the TAGS command is
* ready to execute.
*/
case STATE_C_FTAGS:
ct->execute_state = EXEC_C_FTAGS;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is the return-state from STRING when a search string
* has been completely specified.
*/
case STATE_C_SEARCH:
ct->execute_state = EXEC_C_SEARCH;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is the return-state from STRING when a search string
* has been completely specified for the 'N' search command.
*/
case STATE_C_NSEARCH:
ct->execute_state = EXEC_C_NSEARCH;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is the return-state from STRING when the search part
* of an FD command has been parsed.
*/
case STATE_C_FDCOMMAND:
ct->execute_state = EXEC_C_SEARCH;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.carg = NULL;
ct->execute_state = EXEC_C_FDCOMMAND;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
ct = allocate_cmd_token(ct);
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is the return-state from STRING when the search part
* of an FK command has been parsed.
*/
case STATE_C_FKCOMMAND:
ct->execute_state = EXEC_C_REMEMBER_DOT;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->execute_state = EXEC_C_SEARCH;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.carg = NULL;
ct->execute_state = EXEC_C_FKCOMMAND;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
ct = allocate_cmd_token(ct);
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is a return-state from STRING when we have seen an
* escape terminated string which in this case is the name of the file to
* write.
*/
case STATE_C_WRITEFILE:
ct->execute_state = EXEC_C_WRITEFILE;
ct->ctx.go_flag = NO;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is a return-state from STRING when we have seen an
* escape terminated string which in this case is the name of the file to
* read into the current buffer location.
*/
case STATE_C_READFILE:
ct->execute_state = EXEC_C_READFILE;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is a return-state from STRING when we have seen an
* escape terminated string which in this case is the name of the file which
* we want to create a buffer for and load.
*/
case STATE_C_EDITBUF:
ct->execute_state = EXEC_C_EDITBUF;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(ct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is a return-state from STRING when we have seen an
* escape terminated string which in this case is the name of the file which
* we want to create a readonly buffer for and load.
*/
case STATE_C_VIEWBUF:
ct->execute_state = EXEC_C_VIEWBUF;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(ct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state is a return-state from STRING when we have seen an
* escape terminated string which in this case is the command the user wants
* to execute.
*/
case STATE_C_ECCOMMAND:
ct->execute_state = EXEC_C_ECCOMMAND;
ct->ctx.state = STATE_C_INITIALSTATE;
if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state gets entered when the user has typed ^A
* to print out a message.
*/
case STATE_C_MESSAGE:
switch(ct->input_byte){
case CNTRL_A:
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_OUTPUT_MESSAGE;
return;
default:
ct->execute_state = EXEC_C_MESSAGE;
ct->ctx.state = STATE_C_MESSAGE;
return;
}/* End Switch */
/*
* The following state is a return-state from EXPRESSION when we have seen
* what appears to be a first argument. It stashes the argument and tests if
* there is a second argument available (indicated by a comma).
*/
case STATE_C_ARG1:
ct->ctx.iarg1_flag = YES;
ct->execute_state = EXEC_C_STORE1;
switch(ct->input_byte){
case ',':
ct->ctx.state = STATE_C_EXPRESSION;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_ARG2;
return;
default:
ct->ctx.state = STATE_C_MAINCOMMANDS;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
/*
* Here if the first argument was followed by a comma. This indicates that a
* second argument is being specified. Only certain commands accept a double
* argument.
*/
case STATE_C_ARG2:
ct->ctx.iarg2_flag = YES;
ct->execute_state = EXEC_C_STORE2;
switch(ct->input_byte){
case ',':
ct->ctx.state = STATE_C_ERRORSTATE;
error_message("?Maximum of two arguments exceeded");
return;
default:
ct->ctx.state = STATE_C_MAINCOMMANDS;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
/*
* The next state is the expression substate. States outside of the expression
* parser call through here so that pnest (the current nesting level of
* parentheses) gets set to zero. Then it calls the expression parser's own
* internal sub-expression state.
*/
case STATE_C_EXPRESSION:
ct->ctx.pnest = 0;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_SUBEXPRESSION;
return;
/*
* This is the sub-expression state. It gets called from within the expression
* parser when we want to recursively parse an expression. This lets us handle
* precedence and parenthesis correctly.
*/
case STATE_C_SUBEXPRESSION:
switch(ct->input_byte){
/*
* When we see an open parenthesis, we want to recursively call the
* subexpression state to parse the contents of the parenthesis. Since the open
* parenthesis always occurs in place of an operand, we set that to be the next
* state to call.
*/
case '(':
ct->ctx.pnest += 1;
ct->ctx.state = STATE_C_OPERAND;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_OPERATOR;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_OPERATOR;
return;
/*
* If we see a minus sign here, it is a unary minus. The difference between
* the unary minus and the normal minus is one of precedence. The unary minus
* is very high precedence.
*/
case '-':
ct->ctx.state = STATE_C_MINUSSEEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_UMINUS;
return;
/*
* Here on a leading % command. This just means he is going to default to
* incrementing the queue register by one.
*/
case '%':
ct->ctx.tmpval = 1;
ct->flags |= TOK_M_PARSELOAD_TMPVAL;
/* ;;; the following line shouldn't be required, but is. A bug. */
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.state = STATE_C_PERCENT_OPERAND;
return;
/*
* Well, not much exciting going on here, so we just call the normal operand
* state to parse the current token.
*/
default:
ct->ctx.state = STATE_C_OPERAND;
ct->flags &= ~TOK_M_EAT_TOKEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_OPERATOR;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
/*
* Here if we saw a unary minus. The reason for calling this state at all is to
* catch constructs like -L which really implies -1L. In a case like this,
* the OPERAND state will not see anything it understands, and will return.
* So as to make the defaulting work correctly, we catch that case here and
* default so that we get our -1.
*/
case STATE_C_MINUSSEEN:
switch(ct->input_byte){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'Q': case 'q': case '%':
case '.':
#ifdef CLASSIC_B_BEHAVIOR
case 'B': case 'b':
#endif
case 'Z': case 'z':
case '(':
case '^':
ct->ctx.state = STATE_C_OPERAND;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
default:
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = 1;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End Switch */
/*
* Here on the return from the OPERAND state. Whatever it parsed, we want to
* make it minus.
*/
case STATE_C_UMINUS:
ct->execute_state = EXEC_C_UMINUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here when we are expecting an operator like + = * /. Note that ')' also gets
* handled here since it always appears in the place an operator would be
* expected to appear.
*/
case STATE_C_OPERATOR:
switch(ct->input_byte){
/*
* Here on an a-b case. Note that since minus is a low precedence operator,
* we stash the temporary value, and call subexpression to parse the rest
* of the expression. In that way, if the next operator was * or /, it will
* get processed first.
*/
case '-':
ct->execute_state = EXEC_C_STORE2;
ct->ctx.state = STATE_C_OPERAND;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_MINUS;
return;
/*
* Here on an a+b case. This is similar to the case above in that we want to
* just stash the first value, and then recursively call the parser to handle
* the rest of the expression first. Note that this causes a+b+c to actually
* get handled as a+(b+c) which is a little weird, but works out ok.
*/
case '+':
ct->execute_state = EXEC_C_STORE2;
ct->ctx.state = STATE_C_OPERAND;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_PLUS;
return;
/*
* Here on the a*b case. Unlike the above two cases, we want to immediately
* handle this case since it is the highest precedence of the four operators.
*/
case '*':
ct->execute_state = EXEC_C_STORE1;
ct->ctx.state = STATE_C_OPERAND;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_TIMES;
return;
/*
* Here on the a/b case. This is just like the multiply state
*/
case '/':
ct->execute_state = EXEC_C_STORE1;
ct->ctx.state = STATE_C_OPERAND;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_DIVIDE;
return;
/*
* Here on the n%q case. This just collapses to the value of the Q-register
* after it has been incremented by 'n'
*/
case '%':
ct->ctx.state = STATE_C_PERCENT_OPERAND;
return;
/*
* Here on the 'A' command which returns a value
*/
case 'A': case 'a':
ct->execute_state = EXEC_C_ACOMMAND;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here on a close parenthesis. This will force the end of a subexpression
* parse even though it would not normally have terminated yet. Note the check
* for the guy typing too many of these.
*/
case ')':
if(ct->ctx.pnest == 0){
error_message("?No Matching Open Parenthesis");
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.pnest -= 1;
ct->ctx.state = STATE_C_RETURN;
return;
/*
* If this is not an operator character, it must be the end of the expression.
* In this case, we must be back at the zero level for parenthesis nesting.
*/
default:
if(ct->ctx.pnest > 0){
sprintf(tmp_message,"?Missing %d Close Parenthesis",
ct->ctx.pnest);
error_message(tmp_message);
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End Switch */
/*
* Here when we have the 'b' part of an a+b expression. This happens either
* when the end of the expression has been completed, or a parenthesis has
* forced a temporary end as in: (a+b)
*/
case STATE_C_PLUS:
switch(ct->input_byte){
case '-':
case '+':
case ')':
ct->execute_state = EXEC_C_PLUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
default:
ct->ctx.state = STATE_C_OPERATOR;
ct->flags &= ~TOK_M_EAT_TOKEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_DELAYED_PLUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
case STATE_C_DELAYED_PLUS:
ct->execute_state = EXEC_C_PLUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here when we have the 'b' part of an a-b expression. This happens either
* when the end of the expression has been completed, or a parenthesis has
* forced a temporary end as in: (a-b)
*/
case STATE_C_MINUS:
switch(ct->input_byte){
case '-':
case '+':
case ')':
ct->execute_state = EXEC_C_MINUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
default:
ct->ctx.state = STATE_C_OPERATOR;
ct->flags &= ~TOK_M_EAT_TOKEN;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_DELAYED_MINUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End Switch */
case STATE_C_DELAYED_MINUS:
ct->execute_state = EXEC_C_MINUS;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here when we have both arguments to a multiply. Because multiply is a higher
* precedence operator than +-, it happens immediately unless parenthesis get
* involved.
*/
case STATE_C_TIMES:
ct->execute_state = EXEC_C_TIMES;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here when we have both arguments to a divide. Because division is a higher
* precedence operator than -+, it generally happens immediately. Note that we
* check to be sure that the divisor is non-zero.
*/
case STATE_C_DIVIDE:
ct->execute_state = EXEC_C_DIVIDE;
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_OPERATOR;
return;
/*
* Here to parse any legal TECO operand. These would include numbers and
* commands that return values. Since open parenthesis occur in the same place
* as operands, we also catch them here.
*/
case STATE_C_OPERAND:
switch(ct->input_byte){
/*
* If we see a numeric digit, call a substate which will continue eating bytes
* until a non-digit is seen.
*/
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ct->ctx.state = STATE_C_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte - '0';
return;
/*
* Here if he wants to input in a different radix
*/
case '^':
ct->ctx.state = STATE_C_RADIX;
ct->ctx.tmpval = 0;
return;
/*
* Here if he is specifying the numeric value of a q-register. We have to go
* to another state to determine which q-register he wants to access.
*/
case 'Q': case 'q':
ct->ctx.state = STATE_C_QOPERAND;
return;
/*
* Here when he has specified . This will return as a value the current
* position in the edit buffer.
*/
case '.':
ct->ctx.state = STATE_C_RETURN;
ct->execute_state = EXEC_C_DOTVALUE;
return;
#ifdef CLASSIC_B_BEHAVIOR
/*
* B is a special operand which returns the address of the first character
* in the buffer. This is currently always 0, but may change if some really
* obscure features get put in one of these days.
*/
case 'B': case 'b':
ct->ctx.state = STATE_C_RETURN;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = 0;
return;
#endif
/*
* Z is a special operand which is the number of characters currently in the
* edit buffer.
*/
case 'Z': case 'z':
ct->ctx.state = STATE_C_RETURN;
ct->execute_state = EXEC_C_ZEEVALUE;
return;
/*
* BACKSLASH with no argument actually looks in the buffer at the current
* position and eats numeric digits until it hits a non-digit. It then
* returns the number as a value.
*/
case '\\':
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_BACKSLASH;
ct->ctx.tmpval = 10;
ct->execute_state = EXEC_C_STOREVAL;
ct = allocate_cmd_token(ct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->execute_state = EXEC_C_STORE1;
return;
/*
* If we see a parenthesis in place of an operand, we want to parse it as
* a complete expression of its own until a matching close parenthesis.
*/
case '(':
ct->ctx.state = STATE_C_SUBEXPRESSION;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
/*
* We really should not get here if the guy is typing good syntax, so we
* tell him it is junk and we don't allow it.
*/
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,
"?Unexpected character <%c> in operand",
ct->input_byte);
error_message(tmp_message);
return;
}/* End Switch */
/*
* The following is a substate to parse a number. It will continue until
* it sees a non-digit, and then return to the calling state.
*/
case STATE_C_NUMBER_SUBSTATE:
switch(ct->input_byte){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ct->ctx.state = STATE_C_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->ctx.tmpval * 10 + ct->input_byte - '0';
return;
default:
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End Switch*/
/*
* The following is a substate to parse a hex number. It will continue until
* it sees a non hex digit, and then return to the calling state.
*/
case STATE_C_HEX_NUMBER_SUBSTATE:
switch(ct->input_byte){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval *= 16;
ct->ctx.tmpval += ct->input_byte - '0';
return;
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval *= 16;
ct->ctx.tmpval += ct->input_byte - 'a' + 10;
return;
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval *= 16;
ct->ctx.tmpval += ct->input_byte - 'A' + 10;
return;
default:
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End Switch*/
/*
* The following is a substate to parse a octal number. It will continue until
* it sees a non octal digit, and then return to the calling state.
*/
case STATE_C_OCTAL_NUMBER_SUBSTATE:
switch(ct->input_byte){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
ct->ctx.state = STATE_C_OCTAL_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval *= 8;
ct->ctx.tmpval += ct->input_byte - '0';
return;
case '8': case '9':
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,"?Illegal octal digit <%c> in operand",
ct->input_byte);
error_message(tmp_message);
return;
default:
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End Switch*/
/*
* The following state gets the value of the specified q-register
*/
case STATE_C_QOPERAND:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_RETURN;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_QVALUE;
return;
/*
* This is just like QOPERAND except the execute state is different
*/
case STATE_C_PERCENT_OPERAND:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_OPERATOR;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_PERCENT_VALUE;
return;
/*
* Here on a input radix character
*/
case STATE_C_RADIX:
switch(ct->input_byte){
case 'X': case 'x':
ct->ctx.state = STATE_C_HEX_NUMBER;
ct->execute_state = EXEC_C_STOREVAL;
return;
case 'O': case 'o':
ct->ctx.state = STATE_C_OCTAL_NUMBER;
ct->execute_state = EXEC_C_STOREVAL;
return;
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,"?Unknown radix ^<%c> in operand",
ct->input_byte);
error_message(tmp_message);
return;
}/* End Switch */
case STATE_C_HEX_NUMBER:
switch(ct->input_byte){
case '\\':
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_BACKSLASH;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = 16;
return;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte - '0';
return;
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte - 'a' + 10;
return;
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte - 'A' + 10;
return;
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,"?Illegal hex digit <%c> in operand",
ct->input_byte);
error_message(tmp_message);
return;
}/* End Switch */
case STATE_C_OCTAL_NUMBER:
switch(ct->input_byte){
case '\\':
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_BACKSLASH;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = 8;
return;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
ct->ctx.state = STATE_C_OCTAL_NUMBER_SUBSTATE;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte - '0';
return;
default:
ct->ctx.state = STATE_C_ERRORSTATE;
sprintf(tmp_message,"?Illegal octal digit <%c> in operand",
ct->input_byte);
error_message(tmp_message);
return;
}/* End Switch*/
/*
* The next state is the string substate. Outside states call in through here
* which does the initial tec_alloc of the string storage. Strings are limited
* to PARSER_STRING_MAX bytes at this time.
*/
case STATE_C_STRING:
if((ct->ctx.flags & CTOK_M_ATSIGN_SEEN) == 0){
ct->ctx.delimeter = ESCAPE;
ct->flags &= ~TOK_M_EAT_TOKEN;
}
else {
ct->ctx.delimeter = ct->input_byte;
}
ct->ctx.state = STATE_C_STRING1;
return;
case STATE_C_STRING1:
/*
* First, check if this is the termination character. This would normally be
* an escape, but could be another character if the user used the @ form.
*/
if( ( (ct->input_byte == ESCAPE) &&
(!(ct->ctx.flags & CTOK_M_ATSIGN_SEEN))
) ||
( (ct->ctx.flags & CTOK_M_ATSIGN_SEEN) &&
(ct->input_byte == ct->ctx.delimeter) &&
(ct->ctx.carg != NULL)
)
){
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_RETURN;
return;
}/* End IF */
/*
* Here when we see a normal character inside of the string. Note that we
* have to do the undo stuff to manage the string since it gets stored in
* a regular string array rather than being spread through the command
* tokens.
*/
{/* Local Block */
register char *cp;
register struct undo_token *ut;
/*
* Get the address of the string
*/
cp = ct->ctx.carg;
/*
* If there is no string allocated yet, then we have to allocate one.
* Also, if an @ was seen, then record the delimeter character.
*/
if(cp == NULL){
ct->ctx.carg = cp = tec_alloc(TYPE_C_CBUFF,PARSER_STRING_MAX);
if(cp == NULL){
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
*cp = '\0';
ut = allocate_undo_token(uct);
if(ut == NULL){
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ut->opcode = UNDO_C_MEMFREE;
ut->carg1 = cp;
}/* End IF */
/*
* Each time we append a character to the string, we also have to allocate
* an undo token which will shorten the string if the input character gets
* rubbed out.
*/
ut = allocate_undo_token(uct);
if(ut == NULL){
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ut->opcode = UNDO_C_SHORTEN_STRING;
ut->carg1 = cp;
ut->iarg1 = 0;
while(*cp){
ut->iarg1 += 1;
cp++;
}/* End While */
if(ut->iarg1 >= (PARSER_STRING_MAX-1)){
sprintf(tmp_message,"?Exceeded maximum string length of %d",
PARSER_STRING_MAX-1);
error_message(tmp_message);
ct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
/*
* Finally, we append the input byte to the string
*/
*cp++ = ct->input_byte;
*cp++ = '\0';
ct->ctx.state = STATE_C_STRING1;
return;
}/* End Local Block */
/*
* The following state gets the Q register that will be used with the
* G command.
*/
case STATE_C_GQREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_GQREGISTER;
return;
/*
* The following state gets the Q register that will be used with the
* M command.
*/
case STATE_C_MQREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_MQREGISTER;
return;
/*
* The following two states implement the EQ command which reads a file into
* the specified Q-register.
*/
case STATE_C_EQQREGISTER1:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.tmpval = ct->input_byte;
ct->ctx.state = STATE_C_STRING;
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.caller_token = oct;
ct->ctx.return_state = STATE_C_EQQREGISTER2;
return;
case STATE_C_EQQREGISTER2:
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_EQQREGISTER;
if(ct->ctx.flags & CTOK_M_COLON_SEEN){
oct = ct;
ct = allocate_cmd_token(oct);
if(ct == NULL){
oct->ctx.state = STATE_C_ERRORSTATE;
return;
}/* End IF */
ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
ct->execute_state = EXEC_C_STORE1;
}/* End IF */
return;
/*
* The following state gets the Q register that will be loaded with the
* U command.
*/
case STATE_C_UQREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_UQREGISTER;
return;
/*
* The following state gets the Q register that will be loaded with the
* X command.
*/
case STATE_C_XQREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_XQREGISTER;
return;
/*
* The following state gets the Q register that will be loaded with the
* previous command line in response to the '*' command.
*/
case STATE_C_SAVECOMMAND:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_SAVECOMMAND;
return;
/*
* The following state gets the Q register which will be pushed onto the
* Q register stack.
*/
case STATE_C_PUSH_QREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_PUSH_QREGISTER;
return;
/*
* The following state gets the Q register which will be popped from the
* Q register stack.
*/
case STATE_C_POP_QREGISTER:
if(!parse_check_qname(ct,ct->input_byte)) return;
ct->ctx.state = STATE_C_INITIALSTATE;
ct->q_register = ct->input_byte;
ct->execute_state = EXEC_C_POP_QREGISTER;
return;
/*
* Here to insert characters. The insert state continues until an escape
* is seen. Escapes can also be quoted if they are to be inserted into the
* buffer. If there are many escapes to be inserted, it may be easier to use
* the @ insert command...
*/
case STATE_C_INSERT:
switch(ct->input_byte){
/*
* Here when we see a non-quoted escape. This terminates the insert.
*/
case ESCAPE:
ct->flags &= ~TOK_M_EAT_TOKEN;
ct->ctx.state = STATE_C_INITIALSTATE;
return;
/*
* Here to enter a quote character. This lets you insert characters that would
* normally be handled otherwise. ESCAPE is a good example of this.
*/
case CNTRL_V:
ct->ctx.state = STATE_C_QUOTED_INSERT;
return;
/*
* Here to handle a normal character. It will simply be inserted into the
* edit buffer.
*/
default:
ct->ctx.state = STATE_C_INSERT;
ct->execute_state = EXEC_C_INSERT;
return;
}/* End Switch */
/*
* Here to insert a character directly following the quote character. If
* this is a special character such as an escape, it will be inserted and
* will not terminate the insert command.
*/
case STATE_C_QUOTED_INSERT:
ct->ctx.state = STATE_C_INSERT;
ct->execute_state = EXEC_C_INSERT;
return;
/*
* Here for the alternate form of insert, @I/text/
*/
case STATE_C_ATINSERT:
ct->ctx.tmpval = ct->input_byte;
ct->execute_state = EXEC_C_STOREVAL;
ct->ctx.state = STATE_C_ATINSERT_PART2;
return;
case STATE_C_ATINSERT_PART2:
if(ct->input_byte == ct->ctx.tmpval){
ct->ctx.state = STATE_C_INITIALSTATE;
return;
}/* End IF */
ct->ctx.state = STATE_C_ATINSERT_PART2;
ct->execute_state = EXEC_C_INSERT;
return;
/*
* Here after seeing an equals command. This lets us test for two or three
* in a row which output in octal and hex respectively.
*/
case STATE_C_ONE_EQUALS:
if(ct->input_byte != '='){
ct->ctx.state = STATE_C_INITIALSTATE;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End IF */
ct->ctx.state = STATE_C_TWO_EQUALS;
ct->execute_state = EXEC_C_TWO_EQUALS;
return;
case STATE_C_TWO_EQUALS:
if(ct->input_byte != '='){
ct->ctx.state = STATE_C_INITIALSTATE;
ct->flags &= ~TOK_M_EAT_TOKEN;
return;
}/* End IF */
ct->ctx.state = STATE_C_INITIALSTATE;
ct->execute_state = EXEC_C_THREE_EQUALS;
return;
/*
* Here when we have seen a vertical bar. We have to put a mark here so
* that if the condition is not met, the else clause can be found.
*/
case STATE_C_SKIP_ELSE:
ct->opcode = TOK_C_CONDITIONAL_ELSE;
ct->ctx.state = STATE_C_INITIALSTATE;
return;
case STATE_C_BACKSLASH:
ct->ctx.state = STATE_C_RETURN;
ct->execute_state = EXEC_C_BACKSLASHARG;
return;
default:
sprintf(tmp_message,"?Dispatched unknown state %d",ct->ctx.state);
error_message(tmp_message);
return;
}/* End Switch */
}/* End Routine */
/**
* \brief Check that the specified Q-register name is ok
*
* This routine is called when a parse state wants to verify that the
* specified Q-register name is syntactically correct. It does not
* verify that the Q-register exists, because the execute phase may just
* not have got around to that yet.
*/
int
parse_check_qname( struct cmd_token *ct, char name )
{
char tmp_message[LINE_BUFFER_SIZE];
PREAMBLE();
switch(name){
case '*':
case '_':
case '-':
case '@':
case '?':
return(SUCCESS);
}/* End Switch */
if(isalnum((int)name)) return(SUCCESS);
sprintf(tmp_message,"?Illegal Q-register Name <%c>",name);
error_message(tmp_message);
ct->ctx.state = STATE_C_ERRORSTATE;
return(FAIL);
}/* End Routine */