aboutsummaryrefslogtreecommitdiff
path: root/tecstate.c
diff options
context:
space:
mode:
Diffstat (limited to 'tecstate.c')
-rw-r--r--tecstate.c2374
1 files changed, 2374 insertions, 0 deletions
diff --git a/tecstate.c b/tecstate.c
new file mode 100644
index 0000000..d908e74
--- /dev/null
+++ b/tecstate.c
@@ -0,0 +1,2374 @@
+char *tecstate_c_version = "tecstate.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:21 $
+ * $Source: /cvsroot/videoteco/videoteco/tecstate.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecstate.c
+ * Main SWITCH/CASE statements to implement the parser syntax stage
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ extern char immediate_execute_flag;
+ extern char trace_mode_flag;
+ extern char suspend_is_okay_flag;
+
+
+
+/* PARSE_INPUT_CHARACTER - Continue the parse with the supplied character
+ *
+ * Function:
+ *
+ * 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(ct,uct)
+register 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<cmd>)
+ */
+ 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;
+ screen_message(trace_mode_flag ? "Trace Mode ON" :
+ "Trace Mode OFF");
+ 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.iarg1_flag == YES){
+ ct->execute_state = EXEC_C_ECCOMMAND;
+ 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_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;
+ 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 <dot>. 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 */
+
+
+
+/* PARSE_CHECK_QNAME - Check that the specified Q-register name is ok
+ *
+ * Function:
+ *
+ * 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(ct,name)
+register struct cmd_token *ct;
+register 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 */