diff options
author | paulcantrell <paulcantrell> | 2007-12-10 21:59:20 +0000 |
---|---|---|
committer | paulcantrell <paulcantrell> | 2007-12-10 21:59:20 +0000 |
commit | 2ee4787d1df9784d40a7eda91e61691da3d56f74 (patch) | |
tree | 0947afb6c37ffeb4ae193401a187255cf27b0ed3 /tecstate.c | |
download | videoteco-fork-2ee4787d1df9784d40a7eda91e61691da3d56f74.tar.gz |
Initial revision
Diffstat (limited to 'tecstate.c')
-rw-r--r-- | tecstate.c | 2374 |
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 */ |