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 /teccmd.c | |
download | videoteco-fork-2ee4787d1df9784d40a7eda91e61691da3d56f74.tar.gz |
Initial revision
Diffstat (limited to 'teccmd.c')
-rw-r--r-- | teccmd.c | 2668 |
1 files changed, 2668 insertions, 0 deletions
diff --git a/teccmd.c b/teccmd.c new file mode 100644 index 0000000..f55af95 --- /dev/null +++ b/teccmd.c @@ -0,0 +1,2668 @@ +char *teccmd_c_version = "teccmd.c: $Revision: 1.1 $"; + +/* + * $Date: 2007/12/10 21:59:20 $ + * $Source: /cvsroot/videoteco/videoteco/teccmd.c,v $ + * $Revision: 1.1 $ + * $Locker: $ + */ + +/* teccmd.c + * Subroutines which implement (usually large) TECO commands + * + * 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" + +/* + * Define constants for search table code + */ +#define SEARCH_M_MATCHREPEAT 0x80 +#define SEARCH_M_NOT 0x40 + +#define SEARCH_M_TYPE 0x0F +#define SEARCH_C_BYMASK 1 +#define SEARCH_C_ATLEASTONE 2 +#define SEARCH_C_QREGNUM 3 +#define SEARCH_C_QREGCHARS 4 + +/* + * Globals + */ + int last_search_pos1; + int last_search_pos2; + int last_search_status; + struct tags *current_tags; + +#ifdef CHECKPOINT + static char *checkpoint_filename = NULL; +#endif /* CHECKPOINT */ + +/* + * External Routines, etc. + */ + extern int forced_height,forced_width; + extern char eight_bit_output_flag,hide_cr_flag; + +#ifdef CHECKPOINT + void cmd_checkpoint(void); +#endif /* CHECKPOINT */ + + struct tags *tag_load_file(); + + void srch_setbits(int *bit_table,char *string); + void srch_setbit(int *bit_table,unsigned char value); + void srch_setallbits(int *bit_table); + void srch_invert_sense(int *bit_table); + int cmd_writebak(int,char *,char *,char *,int); + + extern struct buff_header *curbuf; + extern struct buff_header *buffer_headers; + extern char waiting_for_input_flag; + extern char alternate_escape_character; + extern char alternate_delete_character; + extern char intr_flag; + extern int term_lines; + extern char checkpoint_flag; + extern char checkpoint_enabled; + extern int checkpoint_interval; + extern char checkpoint_modified; + extern char resize_flag; + extern unsigned int IntBits[BITS_PER_INT]; + extern struct search_buff search_string; + extern char suspend_is_okay_flag; + +/* CMD_SEARCH - Execute a search command + * + * Function: + * + * This is the runtime execution of the search command. ARG1 and ARG2 may + * specify a buffer range a,bS<mumble> in which case the search is + * constrained within the range. In this case, the direction of the search + * is determined by whether ARG1 is greater than or less than ARG2. + * If ARG2 == -1, then it is an ignored argument and ARG1 specifies the + * direction of search and the number of times to search. + */ +int +cmd_search(arg1,arg2,search_tbl) +int arg1; +int arg2; +struct search_buff *search_tbl; +{ +int count; +char forwards; +int status; +int pos1; +int pos2; +int original_dot; + + PREAMBLE(); + +/* + * Compile the search Q-register into a search table + */ + if(compile_search_string(search_tbl) == FAIL) return(FAIL); + +/* + * Before looking at the arguments, set up the defaults + */ + original_dot = curbuf->dot; + count = 1; + forwards = 1; + pos1 = 0; + pos2 = curbuf->zee; + +/* + * If ARG2 is -1, then he did not specify a range, but rather a count + */ + if(arg2 == -1){ + if(arg1 < 0){ + forwards = 0; + count = -arg1; + }/* End IF */ + + else count = arg1; + }/* End IF */ +/* + * Else, if ARG2 != -1, then he specified a range for the search + */ + else { + if(arg1 < arg2){ + curbuf->dot = pos1 = arg1; + pos2 = arg2; + }/* End IF */ + + else { + forwards = 0; + pos1 = arg2; + curbuf->dot = pos2 = arg1; + }/* End Else */ + }/* End Else */ + +/* + * Now implement the search + */ + while(count--){ + if(forwards){ + status = cmd_forward_search(pos1,pos2,search_tbl); + }/* End IF */ + + else { + status = cmd_reverse_search(pos1,pos2,search_tbl); + }/* End IF */ + + if(status == FAIL){ + curbuf->dot = original_dot; + last_search_pos1 = last_search_pos2 = -1; + last_search_status = 0; + return(FAIL); + }/* End IF */ + }/* End While */ + + last_search_status = -1; + + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_FORWARD_SEARCH - Search in a forward direction + * + * Function: + * + * This routine is used when the search progresses forward + */ +int +cmd_forward_search(pos1,pos2,search_tbl) +int pos1; +int pos2; +struct search_buff *search_tbl; +{ +register struct search_element *ep; +register unsigned char buffer_char; +register int table_position; +int dotpos; +char token_match; +char tmp_message[LINE_BUFFER_SIZE]; +struct position_cache base_position; +struct position_cache running_position; + + PREAMBLE(); + + dotpos = curbuf->dot; +/* + * Insure the search string is non-null + */ + if(search_tbl->length <= 0){ + error_message("?Null Search String"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + + if(dotpos < pos1) return(FAIL); + + table_position = 0; + ep = &search_tbl->data[table_position]; + buffer_char = buff_cached_contents(curbuf,dotpos,&base_position); + running_position = base_position; + + while(dotpos < pos2){ + + if(table_position == 0){ + last_search_pos1 = dotpos; + base_position = running_position; + }/* End IF */ + + buffer_char = BUFF_CACHE_CONTENTS(&running_position); + BUFF_INCREMENT_CACHE_POSITION(&running_position); + dotpos++; + token_match = 1; + + switch(ep->type & SEARCH_M_TYPE){ + case SEARCH_C_BYMASK: + if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] & + IntBits[buffer_char % BITS_PER_INT]) == 0){ + token_match = 0; + break; + }/* End IF */ + if(ep->type & SEARCH_M_MATCHREPEAT){ + while(dotpos < pos2){ + buffer_char = BUFF_CACHE_CONTENTS(&running_position); + if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] & + IntBits[buffer_char % BITS_PER_INT]) == 0) break; + BUFF_INCREMENT_CACHE_POSITION(&running_position); + dotpos++; + }/* End While */ + }/* End IF */ + break; + case SEARCH_C_QREGCHARS: + { struct buff_header *qbp; + register int i; + qbp = buff_qfind(ep->value,0); + token_match = 0; + if(qbp == NULL) break; + for(i = 0; i < qbp->zee; i++){ + if(buffer_char == buff_contents(qbp,i)) token_match = 1; + }/* End FOR */ + if(ep->type & SEARCH_M_NOT){ + token_match = token_match ? 0 : 1; + } + }/* End Block */ + break; + case SEARCH_C_ATLEASTONE: + error_message("?^EM not implemented..."); + search_tbl->error_message_given = YES; + return(FAIL); + case SEARCH_C_QREGNUM: + { struct buff_header *qbp; + qbp = buff_qfind(ep->value,0); + token_match = 0; + if(qbp == NULL) break; + if(buffer_char == qbp->ivalue) token_match = 1; + if(ep->type & SEARCH_M_NOT){ + token_match = token_match ? 0 : 1; + }/* End IF */ + }/* End Block */ + break; + default: + sprintf(tmp_message, + "SEARCH: Illegal token type %d - internal error", + ep->type); + tec_panic(tmp_message); + return(FAIL); + }/* End Switch */ + + if(token_match){ + table_position += 1; + ep++; + if(table_position == search_tbl->length){ + curbuf->dot = last_search_pos2 = dotpos; + {/* Local Block */ + register struct buff_header *hbp; + hbp = buff_qfind('_',1); + if(hbp == NULL) return(FAIL); + hbp->ivalue = last_search_pos1; + }/* End Block */ + return(SUCCESS); + }/* End IF */ + }/* End IF */ + + else { + if(intr_flag){ + error_message("?Search aborted"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + table_position = 0; + ep = &search_tbl->data[table_position]; + dotpos = last_search_pos1 + 1; + running_position = base_position; + BUFF_INCREMENT_CACHE_POSITION(&running_position); + }/* End IF */ + + }/* End While */ + + return(FAIL); + +}/* End Routine */ + + + +/* CMD_REVERSE_SEARCH - Search in a reverse direction + * + * Function: + * + * This routine is used when the search progresses backwards + */ +int +cmd_reverse_search(pos1,pos2,search_tbl) +int pos1; +int pos2; +struct search_buff *search_tbl; +{ +register struct search_element *ep; +register unsigned char buffer_char; +register int table_position; +int dotpos; +char token_match; +char tmp_message[LINE_BUFFER_SIZE]; +struct position_cache base_position; +struct position_cache running_position; + + PREAMBLE(); + + dotpos = curbuf->dot; +/* + * Insure the search string is non-null + */ + if(search_tbl->length <= 0){ + error_message("?Null Search String"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + + if(dotpos > pos2) return(FAIL); + + table_position = search_tbl->length - 1; + ep = &search_tbl->data[table_position]; + buffer_char = buff_cached_contents(curbuf,dotpos,&base_position); + running_position = base_position; + + while(dotpos > pos1){ + + if(table_position == (search_tbl->length - 1)){ + last_search_pos2 = dotpos; + base_position = running_position; + }/* End IF */ + + dotpos--; + BUFF_DECREMENT_CACHE_POSITION(&running_position); + buffer_char = BUFF_CACHE_CONTENTS(&running_position); + token_match = 1; + + switch(ep->type & SEARCH_M_TYPE){ + case SEARCH_C_BYMASK: + if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] & + IntBits[buffer_char % BITS_PER_INT]) == 0){ + token_match = 0; + break; + }/* End IF */ + if(ep->type & SEARCH_M_MATCHREPEAT){ + while(dotpos > pos1){ + BUFF_DECREMENT_CACHE_POSITION(&running_position); + dotpos--; + buffer_char = BUFF_CACHE_CONTENTS(&running_position); + if(ep->bitmask.intarray[buffer_char / BITS_PER_INT] & + IntBits[buffer_char % BITS_PER_INT]) continue; + dotpos++; + BUFF_INCREMENT_CACHE_POSITION(&running_position); + break; + }/* End While */ + }/* End IF */ + break; + case SEARCH_C_QREGCHARS: + { struct buff_header *qbp; + register int i; + qbp = buff_qfind(ep->value,0); + token_match = 0; + if(qbp == NULL) break; + for(i = 0; i < qbp->zee; i++){ + if(buffer_char == buff_contents(qbp,i)) token_match = 1; + }/* End FOR */ + }/* End Block */ + break; + case SEARCH_C_ATLEASTONE: + error_message("?^EM not implemented..."); + search_tbl->error_message_given = YES; + return(FAIL); + case SEARCH_C_QREGNUM: + { struct buff_header *qbp; + qbp = buff_qfind(ep->value,0); + token_match = 0; + if(qbp == NULL) break; + if(buffer_char == qbp->ivalue) token_match = 1; + }/* End Block */ + break; + default: + sprintf(tmp_message, + "SEARCH: Illegal token type %d - internal error", + ep->type); + tec_panic(tmp_message); + return(FAIL); + }/* End Switch */ + + if(token_match){ + table_position -= 1; + ep--; + if(table_position < 0){ + curbuf->dot = last_search_pos1 = dotpos; + {/* Local Block */ + register struct buff_header *hbp; + hbp = buff_qfind('_',1); + if(hbp == NULL) return(FAIL); + hbp->ivalue = last_search_pos2; + }/* End Block */ + return(SUCCESS); + }/* End IF */ + }/* End IF */ + + else { + if(intr_flag){ + error_message("?Search aborted\n"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + table_position = search_tbl->length - 1; + ep = &search_tbl->data[table_position]; + dotpos = last_search_pos2 - 1; + running_position = base_position; + BUFF_DECREMENT_CACHE_POSITION(&running_position); + }/* End IF */ + + }/* End While */ + + return(FAIL); + +}/* End Routine */ + + + +/* SET_SEARCH_STRING_WITH_UNDO - Sets Q-register '_' to the new search string + * + * Function: + * + * This routine is called from the exec functions when a search string + * is to be set. It takes care of loading Q-register '_' with the new + * search string, as well as setting up all the undo tokens to insure + * that this is all reversable. + */ +int +set_search_string_with_undo(string,uct) +char *string; +struct cmd_token *uct; +{ +register struct buff_header *qbp; +register struct undo_token *ut; +register int i,c; +register char *cp; +int new_length; + + PREAMBLE(); + +/* + * Get a pointer to the special Q-register which holds the search string. If + * it does not exist, have it be created automatically. + */ + qbp = buff_qfind('_',1); + if(qbp == NULL){ + return(FAIL); + }/* End IF */ + +/* + * This code allows us to load the numeric half of the search string + * Q-register with the length of the string we just found. In order + * to be able to undo it, we have to protect the previous value, and + * it just happens that by doing it here we probably catch all the + * places where the search could have taken place. Then again, maybe + * not (search really should not be calling this routine repeatedly + * in an iteration or macro, for instance). If this gets cleaned up, + * then this code might not be appropriate... XXX + */ + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_UQREGISTER; + ut->iarg1 = '_'; + ut->iarg2 = qbp->ivalue; + + if(string == NULL) return(SUCCESS); + if((new_length = strlen(string)) == 0) return(SUCCESS); + +/* + * Set up undo tokens for the search_state and search_position information + * which typically gets used to pass search information between halves of + * a search / replace type command. + */ + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_SET_SEARCH_GLOBALS; + ut->iarg1 = last_search_pos1; + ut->iarg2 = last_search_pos2; + ut->carg1 = (char *)last_search_status; + +/* + * If the new length is the same as the old length, there is a chance we are + * just setting the same search string over again. To find out, we actually + * have to loop through comparing bytes in the Q-register to bytes in the + * supplied string. If none of them are different, we can just return without + * changing the search string. This happens often in iterations... + */ + if(new_length == qbp->zee){ + cp = string; + for(i = 0; i < new_length; i++){ + c = buff_contents(qbp,i); + if(*cp++ != c) break; + }/* End FOR */ + + if(i == new_length) return(SUCCESS); + + }/* End IF */ +/* + * At this point we set an undo token so that if the following stuff gets + * undone, we will re-compile the search string. + */ + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_SET_SEARCH_STRING; +/* + * Delete the current contents of the search string Q-register to make room + * for the new contents. + */ + if(qbp->zee > 0) buff_delete_with_undo(uct,qbp,0,qbp->zee); +/* + * Set up the undo token to delete the text we are going to insert into the + * search string Q-register. + */ + buff_insert_with_undo(uct,qbp,0,string,new_length); + + return(SUCCESS); + +}/* End Routine */ + + + +/* COMPILE_SEARCH_STRING - Creates the search table for a given input string + * + * Function: + * + * This routine parses the input search string and creates the search + * table which will describe to the search routines how to match the + * buffer characters. Note that the search table must be constructed + * in such a way that it works equally well backward or forward. + */ +int +compile_search_string(search_tbl) +struct search_buff *search_tbl; +{ +register int position; +register char c; +register char *cp; +register struct buff_header *qbp; +register struct search_element *ep; +int radix; +int bracket_bits[256/BITS_PER_INT]; +char tmp_message[LINE_BUFFER_SIZE]; +char either_case_flag = YES; +char bracket_wildcard = NO; +char bracket_not_flag = NO; +char not_flag = NO; +char matchrepeat_flag = NO; + + PREAMBLE(); + +#define QBUF_CHAR() \ + (position < qbp->zee ? *cp++ = buff_contents(qbp,position++) : 0) + + search_tbl->error_message_given = NO; + +/* + * Get a pointer to the special Q-register which holds the search string. If + * it does not exist, have it be created automatically. + */ + qbp = buff_qfind('_',1); + if(qbp == NULL){ + error_message("Internal error - qfind returned null"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + + if(qbp->zee >= SEARCH_STRING_MAX){ + error_message("Search Q-register too long"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + + search_string.length = 0; + position = 0; + cp = search_string.input; + ep = search_string.data; + bzero(&ep->bitmask,sizeof(ep->bitmask)); + + while((c = QBUF_CHAR())){ +/* + * Check for CNTRL_E which is the standard lead-in for wildcard search + * strings. + */ + if(c == CNTRL_E){ + c = QBUF_CHAR(); + switch(c){ +/* + * [ specifies a list of possible matches, ANY of which satisfy the + * position. + */ + case '[': + bzero(bracket_bits,sizeof(bracket_bits)); + bracket_wildcard = YES; + if(not_flag){ + not_flag = NO; + bracket_not_flag = YES; + }/* End IF */ + continue; +/* + * A specifies any ALPHABETIC character as a match. Thus any upper or lower + * case character in the range a-z will match. + */ + case 'A': case 'a': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask, + "abcdefghijklmnopqrstuvwxyz"); + srch_setbits((int *)&ep->bitmask, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + break; +/* + * B specifies any separator character, when separator is defined as any + * non-alphanumeric character. + */ + case 'B': case 'b': + { + register int i; + ep->type = SEARCH_C_BYMASK; + for(i = 0; i < 256; i++){ + if(!isalnum(i)) srch_setbit((int *)&ep->bitmask,i); + }/* End FOR */ + break; + } +/* + * C specifies any symbol constituent. This is any alpha-numeric character, + * or dot (.), dollar sign ($), or underscore (_). + */ + case 'C': case 'c': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask, + "abcdefghijklmnopqrstuvwxyz"); + srch_setbits((int *)&ep->bitmask, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + srch_setbits((int *)&ep->bitmask,"0123456789.$_"); + break; +/* + * D specifies that any digit (0 to 9) is acceptable. + */ + case 'D': case 'd': + ep->type = SEARCH_C_BYMASK | SEARCH_M_MATCHREPEAT; + srch_setbits((int *)&ep->bitmask,"0123456789"); + break; +/* + * E or ^E is a Video TECO only definition which means that whether searches + * are case sensitive or not should be toggled. + */ + case 'E': case 'e': case CNTRL_E: + either_case_flag ^= 1; + continue; +/* + * G followed by a Q register name matches any character which is in the + * text portion of the Q register. + */ + case 'G': case 'g': + if(bracket_wildcard){ + error_message("^EG inside ^E[,,,] illegal"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + ep->type = SEARCH_C_QREGCHARS; + ep->value = QBUF_CHAR(); + break; +/* + * L matches any line terminator character. + */ + case 'L': case 'l': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask,"\n\r\f"); + break; +/* + * M is a flag that says any number of the following character (but at least + * one) are acceptable. Thus ^EMA would match A or AA or AAA, etc. + */ + case 'M': case 'm': + matchrepeat_flag = YES; + continue; +/* + * R matches any alphanumeric + */ + case 'R': case 'r': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask, + "abcdefghijklmnopqrstuvwxyz"); + srch_setbits((int *)&ep->bitmask, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + srch_setbits((int *)&ep->bitmask,"0123456789"); + break; +/* + * S matches any number of tabs and spaces, in any mixture (but there must + * be at least one). + */ + case 'S': case 's': + ep->type = SEARCH_C_BYMASK | SEARCH_M_MATCHREPEAT; + srch_setbits((int *)&ep->bitmask," \t"); + break; +/* + * U matches against the ASCII code which is contained in the Q register's + * numeric storage. + */ + case 'U': case 'u': + if(bracket_wildcard){ + error_message("^EU inside ^E[,,,] illegal"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End IF */ + ep->type = SEARCH_C_QREGNUM; + ep->value = QBUF_CHAR(); + break; +/* + * V matches any lowercase letter. + */ + case 'V': case 'v': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask, + "abcdefghijklmnopqrstuvwxyz"); + break; +/* + * W matches any uppercase letter + */ + case 'W': case 'w': + ep->type = SEARCH_C_BYMASK; + srch_setbits((int *)&ep->bitmask, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + break; +/* + * X is similar to ^X in that it matches any character + */ + case 'X': case 'x': + ep->type = SEARCH_C_BYMASK; + srch_setallbits((int *)&ep->bitmask); + break; +/* + * The < character opens the sequence for ^E<nnn> where nnn is a numeric + * (ASCII) code to search for. Classic TECO says that this code is octal, + * but Video TECO defaults to decimal, octal if the first digit is zero, + * or hex if the first two digits are 0x. + */ + case '<': + ep->value = 0; + radix = 10; + c = QBUF_CHAR(); + if(c == '0'){ + radix = 8; + c = QBUF_CHAR(); + if(c == 'x' || c == 'X'){ + radix = 16; + c = QBUF_CHAR(); + }/* End IF */ + }/* End IF */ + while(c && c != '>'){ + if(c >= '0' && c <= '7'){ + ep->value *= radix; + ep->value += c - '0'; + c = QBUF_CHAR(); + continue; + }/* End IF */ + if(c >= '8' && c <= '9' && radix > 8){ + ep->value *= radix; + ep->value += c - '0'; + c = QBUF_CHAR(); + continue; + }/* End IF */ + if(c >= 'a' && c <= 'f' && radix == 16){ + c = TOUPPER(c); + }/* End IF */ + if(c >= 'A' && c <= 'F' && radix == 16){ + ep->value *= radix; + ep->value += c - 'A' + 10; + c = QBUF_CHAR(); + continue; + }/* End IF */ + sprintf(tmp_message, + "?Illegal ^E code '%c' in search string",c); + search_tbl->error_message_given = YES; + error_message(tmp_message); + return(FAIL); + }/* End While */ + if(c == '>') c = QBUF_CHAR(); + else { + error_message("?Missing '>' in ^E<nnn> search string"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End Else */ + ep->type = SEARCH_C_BYMASK; + srch_setbit((int *)&ep->bitmask,ep->value); + break; +/* + * This case is really just to catch the user typing ^E followed by the + * end of the search string. We just make it search for ^E in this case. + */ + case '\0': + ep->type = SEARCH_C_BYMASK; + srch_setbit((int *)&ep->bitmask,CNTRL_E); + break; +/* + * Any unrecognized ^E code just searches for the exact character which + * follows the ^E. This is probably not very good, we should issue an + * error message instead. + */ + default: + ep->type = SEARCH_C_BYMASK; + srch_setbit((int *)&ep->bitmask,c); + break; + }/* End Switch */ + }/* End IF */ +/* + * The ^X code embedded in a search string means match any character at + * this position. + */ + else if(c == CNTRL_X){ + ep->type = SEARCH_C_BYMASK; + srch_setallbits((int *)&ep->bitmask); + }/* End Else */ +/* + * The ^N code embedded in a search string means match any character EXCEPT + * the one that follows. + */ + else if(c == CNTRL_N){ + not_flag = YES; + continue; + }/* End Else */ +/* + * Else, here if it is just a normal character which should be searched for + */ + else { + ep->type = SEARCH_C_BYMASK; + srch_setbit((int *)&ep->bitmask,c); + if(either_case_flag){ + if(islower((int)c)) srch_setbit((int *)&ep->bitmask,TOUPPER((int)c)); + if(isupper((int)c)) srch_setbit((int *)&ep->bitmask,TOLOWER((int)c)); + }/* End IF */ + }/* End Else */ + +/* + * If sense has been inverted with ^N, invert the bits + */ + if(not_flag){ + if(ep->type & SEARCH_C_BYMASK){ + srch_invert_sense((int *)&ep->bitmask); + }/* End IF */ + ep->type &= ~SEARCH_M_MATCHREPEAT; + ep->type |= SEARCH_M_NOT; + not_flag = NO; + }/* End IF */ +/* + * If already inside a ^E[a,b,c] type wildcard, check for comma or close + * bracket. + */ + if(bracket_wildcard){ + c = QBUF_CHAR(); + switch(c){ + case ',': + { + register int i; + for(i = 0; i < 256 / BITS_PER_INT; i++){ + bracket_bits[i] |= ep->bitmask.intarray[i]; + }/* End FOR */ + bzero(&ep->bitmask,sizeof(ep->bitmask)); + continue; + } + case ']': + { + register int i; + for(i = 0; i < 256 / BITS_PER_INT; i++){ + ep->bitmask.intarray[i] |= bracket_bits[i]; + }/* End FOR */ + if(bracket_not_flag){ + srch_invert_sense((int *)&ep->bitmask); + ep->type &= ~SEARCH_M_MATCHREPEAT; + bracket_not_flag = NO; + }/* End IF */ + bracket_wildcard = NO; + break; + } + default: + error_message("Illegal syntax in [,,,] construct"); + search_tbl->error_message_given = YES; + return(FAIL); + }/* End Switch */ + }/* End IF */ +/* + * Test for match repeating characters flag + */ + if(matchrepeat_flag){ + ep->type |= SEARCH_M_MATCHREPEAT; + matchrepeat_flag = NO; + }/* End IF */ +/* + * Point to the next entry in the search string table + */ + ep++; + bzero(&ep->bitmask,sizeof(ep->bitmask)); + search_string.length += 1; + + }/* End While */ + + *cp = '\0'; + + return(SUCCESS); + +}/* End Routine */ + + + +/* SRCH_SETALLBITS - Set ALL the bits on for a search table entry + * + * Function: + * + * This routine makes ANY character match the search string table + * entry. + */ +void +srch_setallbits(bit_table) +register int *bit_table; +{ +register int i; + + PREAMBLE(); + + for(i = 0; i < 256 / BITS_PER_INT; i++){ + bit_table[i] = ~0; + }/* End FOR */ + +}/* End Routine */ + +/* SRCH_INVERT_SENSE - Invert the sense of a search table entry + * + * Function: + * + * This routine is called when a 'NOT' modifier has been used, + * meaning that just the opposite matching should occur. We + * simply invert the setting of the bits in the search table entry. + */ +void +srch_invert_sense(bit_table) +register int *bit_table; +{ +register int i; + + PREAMBLE(); + + for(i = 0; i < 256 / BITS_PER_INT; i++){ + bit_table[i] ^= ~0; + }/* End FOR */ + +}/* End Routine */ + +/* SRCH_SETBITS - Set the corresponding search bits to 'on' + * + * Function: + * + * This routine is called with a zero terminated string of characters. + * It sets each corresponding bit in the search table to a 1. + */ +void +srch_setbits(bit_table,string) +register int *bit_table; +register char *string; +{ +register int c; + + PREAMBLE(); + + while((c = *string++)){ + bit_table[c/BITS_PER_INT] |= IntBits[c%BITS_PER_INT]; + }/* End While */ + +}/* End Routine */ + +/* SRCH_SETBIT - Set the corresponding search bit to 'on' + * + * Function: + * + * This routine is called with a char whose corresponding bit should + * be set in the search table. + */ +void +srch_setbit(bit_table,value) +register int *bit_table; +register unsigned char value; +{ + + PREAMBLE(); + + bit_table[value/BITS_PER_INT] |= IntBits[value%BITS_PER_INT]; + +}/* End Routine */ + + + +/* CMD_WRITE - Implements the EW command by writing out a buffer + * + * Function: + * + * This routine will write out the contents of the buffer after creating + * the appropriate backup files. If there is not already a file with the + * a .OLD extension, this is created. Otherwise, a .BAK file is created. + * If the name is so long that we can't append a of suffix, we have to + * hack it up a bit. This is not guaranteed to work, but... + */ +int +cmd_write(hbp,filename) +struct buff_header *hbp; +char *filename; +{ +char base_filename[TECO_FILENAME_COMPONENT_LENGTH + 1]; +char path_name[TECO_FILENAME_TOTAL_LENGTH + 1]; +char tmp_filename[TECO_FILENAME_TOTAL_LENGTH + 1]; +char tmp_message[LINE_BUFFER_SIZE]; +register char *cp1,*cp2; +register int i; +int path_len; +int file_len; +int combined_len; +int fi; +int status; + + PREAMBLE(); + +#ifdef UNIX +/* + * First step is to separate the path elements from the filename itself. + * To do this, we search for the directory path separator. + */ + cp1 = cp2 = filename; + while(*cp1){ + if(*cp1++ == '/') cp2 = cp1; + }/* End While */ + + combined_len = strlen(filename); + file_len = strlen(cp2); + path_len = combined_len - file_len; + +/* + * The filename really should not be longer than this. + */ + if(file_len > TECO_FILENAME_COMPONENT_LENGTH){ + sprintf(tmp_message,"?Filename is too long <%s>",cp2); + error_message(tmp_message); + return(FAIL); + }/* End IF */ +/* + * Make a copy of the filename portion for safe keeping. + */ + (void) strcpy(base_filename,cp2); +/* + * And do the same for the path portion. + */ + cp1 = path_name; + cp2 = filename; + for(i = 0; i < path_len; i++){ + *cp1++ = *cp2++; + }/* End FOR */ + *cp1 = '\0'; + +/* + * Now the first thing to do is see if the file exists, because this + * will impact us on whether we want to attempt .BAK files + */ + if(hbp->isbackedup == NO){ + + fi = open(filename,O_RDONLY); + if(fi >= 0){ + +#ifdef CREATE_OLD_FILES +#ifdef HAVE_LONG_FILE_NAMES + (void) strcpy(tmp_filename,base_filename); + (void) strcat(tmp_filename,".OLD"); + status = cmd_writebak(fi,filename,path_name,tmp_filename,O_EXCL); +#else + (void) strcpy(tmp_filename,path_name); + (void) strcat(tmp_filename,".TECOLD"); + status = cmd_writebak(fi,filename,tmp_filename,base_filename,O_EXCL); +#endif + if(status == SUCCESS) hbp->isbackedup = YES; + + if(status == FAIL && errno != EEXIST){ + sprintf(tmp_message,"?Error opening <%s>: %s", + filename,error_text(errno)); + error_message(tmp_message); + close(fi); + return(FAIL); + }/* End IF */ +#endif + + if(hbp->isbackedup == NO){ +#ifdef HAVE_LONG_FILE_NAMES + (void) strcpy(tmp_filename,base_filename); + (void) strcat(tmp_filename,".BAK"); + status = cmd_writebak(fi,filename,path_name,tmp_filename,0); +#else + (void) strcpy(tmp_filename,path_name); + (void) strcat(tmp_filename,".TECBAK"); + status = cmd_writebak(fi,filename,tmp_filename,base_filename,0); +#endif + if(status == SUCCESS) hbp->isbackedup = YES; + if(status == FAIL){ + sprintf(tmp_message,"?Error opening <%s>: %s", + filename,error_text(errno)); + error_message(tmp_message); + close(fi); + return(FAIL); + }/* End IF */ + }/* End IF */ + }/* End IF */ + + close(fi); + + }/* End IF */ + +/* END OF UNIX CONDITIONAL CODE */ + +#endif + +#ifdef VMS + fi = creat(filename,0,"mrs = 0"); +#else + fi = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666); +#endif + + if(fi < 0){ + sprintf(tmp_message,"?Error opening <%s>: %s", + filename,error_text(errno)); + error_message(tmp_message); + return(FAIL); + }/* End IF */ + + status = buff_write(hbp,fi,0,hbp->zee); + + close(fi); + + if(status == FAIL){ + sprintf(tmp_message,"?Error writing <%s>: %s", + filename,error_text(errno)); + error_message(tmp_message); + return(FAIL); + }/* End IF */ + + hbp->ismodified = NO; + if(hbp == curbuf){ + screen_label_line(hbp,"",LABEL_C_MODIFIED); + }/* End IF */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_WRITEBAK - Routine to open the BAK or OLD file + * + * Function: + * + * This routine opens a channel to the backup file and creates any + * subdirectories that may be necessary. + */ +int +cmd_writebak(fi,input_filename,pathname,output_filename,open_flags) +int fi; +char *pathname; +char *input_filename; +char *output_filename; +int open_flags; +{ +char tmp_filename[TECO_FILENAME_TOTAL_LENGTH + 1]; +char tmp_message[LINE_BUFFER_SIZE]; +char iobuf[IO_BUFFER_SIZE]; +int fo; +register int i; +register int status; + + PREAMBLE(); + +#ifndef HAVE_LONG_FILE_NAMES + if(mkdir(pathname,0777)){ + if(errno != EEXIST){ + sprintf(tmp_message,"?Error creating subdirectory <%s>: %s", + pathname,error_text(errno)); + error_message(tmp_message); + return(FAIL); + }/* End IF */ + }/* End IF */ +#endif + + (void) strcpy(tmp_filename,pathname); + if(strlen(tmp_filename)){ + (void) strcat(tmp_filename,"/"); + }/* End IF */ + + (void) strcat(tmp_filename,output_filename); + chmod(tmp_filename,0666); + fo = open(tmp_filename,O_WRONLY|O_CREAT|O_TRUNC|open_flags,0666); + if(fo < 0){ + if(errno == EEXIST){ + chmod(tmp_filename,0444); + return(FAIL); + }/* End IF */ + sprintf(tmp_message,"?Error opening <%s>: %s", + tmp_filename,error_text(errno)); + error_message(tmp_message); + return(FAIL); + }/* End IF */ + + while(1){ + i = read(fi,iobuf,IO_BUFFER_SIZE); + if(i < 0){ + sprintf(tmp_message,"?Error reading <%s>: %s", + input_filename,error_text(errno)); + error_message(tmp_message); + close(fo); + return(FAIL); + }/* End IF */ + + status = write(fo,iobuf,(unsigned)i); + if(status < 0){ + sprintf(tmp_message,"?Error writing <%s>: %s", + tmp_filename,error_text(errno)); + error_message(tmp_message); + close(fo); + return(FAIL); + }/* End IF */ + + if(i < IO_BUFFER_SIZE) break; + + }/* End While */ + + close(fo); + chmod(tmp_filename,0444); + + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_WORDMOVE - Here to move within the edit buffer by words + * + * Function: + * + * Here in response to one of the 'word' commands. We move the + * current edit position forward or backward by the specified + * number of words. + */ +int +cmd_wordmove(count) +register int count; +{ + register int c; + + PREAMBLE(); + + while(count > 0){ + while(1){ + if(curbuf->dot == curbuf->zee) break; + c = buff_contents(curbuf,curbuf->dot); + if(isspace(c)) break; + curbuf->dot++; + }/* End While */ + + while(1){ + if(curbuf->dot == curbuf->zee) break; + c = buff_contents(curbuf,curbuf->dot); + if(!isspace(c)) break; + curbuf->dot++; + }/* End While */ + + count -= 1; + + }/* End While */ + + while(count < 0){ + while(1){ + if(curbuf->dot == 0) break; + c = buff_contents(curbuf,curbuf->dot-1); + if(isspace(c)) break; + curbuf->dot--; + }/* End While */ + + while(1){ + if(curbuf->dot == 0) break; + c = buff_contents(curbuf,curbuf->dot-1); + if(!isspace(c)) break; + curbuf->dot--; + }/* End While */ + + count += 1; + + }/* End While */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_SUSPEND - We get here on a suspend signal from the tty + * + * Function: + * + * Here when the user has typed ^Z. We want to suspend back to the + * original shell. + */ +void +cmd_suspend() +{ +#if defined(UNIX) || defined(VMS) +extern char susp_flag; +#endif + + PREAMBLE(); + +#ifdef UNIX + if(waiting_for_input_flag == YES){ + pause_while_in_input_wait(); + return; + }/* End IF */ + +#ifdef JOB_CONTROL + if(susp_flag++ >= 4){ + signal(SIGTSTP,(void (*)())SIG_DFL); + }/* End IF */ +#endif + +#endif + +#ifdef VMS + if(suspend_is_okay_flag == YES){ + susp_flag++; + }/* End IF */ +#endif + +}/* End Routine */ + +/* CMD_PAUSE - Here when we are ready to suspend back to the shell + * + * Function: + * + * Here when the parser has noticed that the suspend flag is set, and + * is ready for us to suspend the process. + */ +void +cmd_pause() +{ +extern char susp_flag; + +#ifdef VMS +long owner_pid; +long status; + +struct itmlst { + short buffer_length; + short item_code; + char *buffer_address; + long *length_address; + long terminating_zero; +}list; +#endif + + PREAMBLE(); + + restore_tty(); + +#if defined(JOB_CONTROL) || defined(VMS) + +#ifdef CHECKPOINT + alarm(0); +#endif /* CHECKPOINT */ + +#ifdef UNIX + if(suspend_is_okay_flag == YES){ + kill(getpid(),SIGSTOP); + signal(SIGTSTP,(void (*)())cmd_suspend); + } +#endif /* UNIX */ + +#ifdef VMS + owner_pid = 0; + list.buffer_length = sizeof(owner_pid); + list.item_code = JPI$_OWNER; + list.buffer_address = (char *)&owner_pid; + list.length_address = 0; + list.terminating_zero = 0; + sys$getjpiw(0,0,0,&list,0,0,0); + + if(owner_pid){ + status = lib$attach(&owner_pid); + if(status != SS$_NORMAL){ + error_message("?LIB$ATTACH failed"); + susp_flag = 0; + return; + }/* End IF */ + }/* End IF */ + + else { + error_message("?Cannot suspend. No parent process for LIB$ATTACH"); + susp_flag = 0; + return; + }/* End IF */ +#endif + +#ifdef CHECKPOINT + alarm(checkpoint_interval); +#endif /* CHECKPOINT */ + +#endif /* JOB_CONTROL */ + + initialize_tty(); + screen_redraw(); + susp_flag = 0; + +}/* End Routine */ + + + +/* CMD_WINCH - We get here when the OS says the window changed size + * + * Function: + * + * Here when the OS says our window changed size. We just wanna + * call the screen package's resize entry point so it can build + * a new screen for us. + */ +void +cmd_winch() +{ + PREAMBLE(); + +#ifdef ATT_UNIX + signal(SIGWINCH,(void (*)())cmd_winch); +#endif + +#ifdef SEQUOIA + screen_message("WINCH!\g\g"); + screen_refresh(); + return; +#endif /* SEQUOIA */ + + resize_flag = YES; + + if(waiting_for_input_flag){ + screen_resize(); + screen_refresh(); + }/* End IF */ + +}/* End Routine */ + + + +/* CMD_INTERRUPT - We get here on an interrupt signal from the user + * + * Function: + * + * Here when the user has typed ^C. We want to cause any current commands + * to abort. This gives the user a way to break out of infinite iterations + * and macros. + */ +void +cmd_interrupt() +{ + PREAMBLE(); + +#ifdef ATT_UNIX + signal(SIGINT,(void (*)())cmd_interrupt); +#endif + + if(intr_flag++ >= 4){ + if(waiting_for_input_flag == NO){ + restore_tty(); + fprintf(stderr,"TECO aborted...\n"); + signal(SIGINT,(void (*)())SIG_DFL); + kill(getpid(),SIGINT); + }/* End IF */ + }/* End IF */ + + screen_message("interrupt!"); + +}/* End Routine */ + + + +#ifdef CHECKPOINT + +/* CMD_ALARM - Here to when the interval timer expires + * + * Function: + * + * This function is called periodically when the interval timer says + * it is time for us to checkpoint all our buffers. + */ +cmd_alarm() +{ + + PREAMBLE(); + +#if defined(ATT_UNIX) || defined(VMS) + signal(SIGALRM,(void (*)())cmd_alarm); +#endif + + if(waiting_for_input_flag == 0){ + checkpoint_flag = YES; + }/* End IF */ + + else { + if(checkpoint_enabled == YES){ + cmd_checkpoint(); + } + checkpoint_flag = NO; + }/* End Else */ + +#ifdef VMS + alarm(0); +#endif /* VMS */ + alarm(checkpoint_interval); + +}/* End Routine */ + +#endif /* CHECKPOINT */ + + + +#ifdef CHECKPOINT + +/* CMD_CHECKPOINT - Here to write all our buffers to a checkpoint file + * + * Function: + * + * Here we check out all the buffers and write out any which have + * ever been modified. + */ +void +cmd_checkpoint() +{ +register struct buff_header *bp; +int fd = 0; +char temp_buffer[TECO_FILENAME_TOTAL_LENGTH+20]; +char message_save_buff[SCREEN_NOMINAL_LINE_WIDTH]; +char filename_buffer[64]; +register char *cp; +int i,status; + + PREAMBLE(); + + if(checkpoint_modified == NO) return; + checkpoint_modified = NO; + + if(checkpoint_filename == NULL){ + temp_buffer[0] = '\0'; + if(cp = (char *)getenv("HOME")){ + (void) strcpy(temp_buffer,cp); +#ifndef VMS + (void) strcat(temp_buffer,"/"); +#endif /* VMS */ + }/* End IF */ + +#ifndef VMS + strcpy(filename_buffer,".tecXXXXXX"); +#else + strcpy(filename_buffer,"tecXXXXXX.CKP"); +#endif /* VMS */ + cp = mktemp(filename_buffer); + + strcat(temp_buffer,cp); + checkpoint_filename = tec_alloc(TYPE_C_CPERM,strlen(temp_buffer)+1); + if(checkpoint_filename == NULL) return; + strcpy(checkpoint_filename,temp_buffer); + }/* End IF */ + + status = 0; + for(bp = buffer_headers; bp != NULL; bp = bp->next_header){ + if(bp->ismodified){ + status = 1; + }/* End IF */ + }/* End FOR */ + + if(status == 0) return; + + screen_save_current_message(message_save_buff,sizeof(message_save_buff)); + screen_message("checkpointing..."); + screen_refresh(); + + fd = open(checkpoint_filename,O_WRONLY|O_CREAT|O_TRUNC,0666); + if(fd == 0 || fd == -1){ + sprintf(temp_buffer,"?Checkpoint file %s open failed %s", + checkpoint_filename,error_text(errno)); + error_message(temp_buffer); + screen_refresh(); + sleep(2); + return; + }/* End IF */ + + + for(bp = buffer_headers; bp != NULL; bp = bp->next_header){ + if(bp->ismodified == NO && bp->buffer_number <= 0) continue; + if(bp->buffer_number == 0)continue; + i = (78 - strlen(bp->name) - 4) / 2; + cp = temp_buffer; + *cp++ = '\n'; + while(i-- > 0)*cp++ = '*'; + *cp++ = '('; + strcpy(cp,bp->name); + while(*cp)cp++; + *cp++ = ')'; + if(bp->ismodified) *cp++ = '+'; + while(cp < &temp_buffer[78]) *cp++ = '*'; + *cp++ = '\n'; + *cp++ = '\0'; + i = strlen(temp_buffer); + + if(write(fd,temp_buffer,i) != i){ + sprintf(temp_buffer,"?checkpoint failed %s",error_text(errno)); + error_message(temp_buffer); + screen_refresh(); + sleep(2); + close(fd); + return; + }/* End IF */ + + + if(bp->ismodified){ + status = buff_write(bp,fd,0,bp->zee); + }/* End IF */ + + }/* End FOR */ + + close(fd); + + screen_message("checkpointing... done"); + screen_refresh(); + screen_message(message_save_buff); + +}/* End Routine */ + +#endif /* CHECKPOINT */ + + + +#ifdef CHECKPOINT + +/* REMOVE_CHECKPOINT_FILE - On exit we clean up the checkpoint file + * + * Function: + * + * This routine is called when a clean exit is assured. We remove + * the checkpoint file so they don't clutter up the disk. + */ +void +remove_checkpoint_file() +{ + + PREAMBLE(); + + if(checkpoint_filename == NULL) return; +#ifndef VMS + unlink(checkpoint_filename); +#endif + +}/* End Routine */ + +#endif /* CHECKPOINT */ + + + +#if defined(VMS) || defined(STARDENT) || defined(STARDENT_860) || defined(SEQUOIA) || defined(LOCUS_SYSV) || defined(SCO_386) +/* BZERO - Zero an array of bytes + * + * Function: + * + * This routine zeros an array of bytes. For some reason the VMS + * library doesn't seem to know about it. + */ +bzero(array,length) +register char *array; +register int length; +{ + PREAMBLE(); + + while(length-- > 0) *array++ = '\0'; + +}/* End Routine */ +#endif /* VMS || STARDENT et al */ + + + +#ifdef UNIX +/* CMD_OSCMD - Issue a command to the operating system + * + * Function: + * + * This routine is called in response to the EC command which allows the + * user to execute operating system commands from within the editor. + */ +int +cmd_oscmd(ct) +register struct cmd_token *ct; +{ +register char *cp; +int pid,w,status; +int pipe_desc[2]; +int last_intr_flag; +int line_cnt; +char tmpbuf[LINE_BUFFER_SIZE]; +char pipebuff[IO_BUFFER_SIZE]; +extern char susp_flag; +int pipe_buf_flag = 0; +int buf_pipe[2]; + + PREAMBLE(); + + if(ct->ctx.flags & CTOK_M_COLON_SEEN){ + pipe_buf_flag = 1; + status = pipe(buf_pipe); + if (status != 0) { + error_message("?Error creating buffer pipe"); + return(FAIL); + } + }/* End IF */ + + cp = ct->ctx.carg; +/* + * Create a PIPE so that we can capture the output of the child process + */ + status = pipe(pipe_desc); + if(status != 0){ + sprintf(tmpbuf,"?Error creating PIPE: %s",error_text(errno)); + error_message(tmpbuf); + return(FAIL); + }/* End IF */ +/* + * Fork to get a process to run the shell + */ +#ifdef LINUX + if((pid = fork()) == 0){ + close(1); dup(pipe_desc[1]); + close(2); dup(pipe_desc[1]); + close(pipe_desc[0]); + close(pipe_desc[1]); + execl("/bin/bash","bash","-c",cp,0); + _exit(127); + }/* End IF */ +#else + if((pid = fork()) == 0){ + if (pipe_buf_flag) { + close(0); dup(buf_pipe[0]); + close(buf_pipe[1]); + } else { + close(pipe_desc[0]); + } + close(1); dup(pipe_desc[1]); + close(2); dup(pipe_desc[1]); + close(pipe_desc[0]); + close(pipe_desc[1]); + execl("/bin/csh","csh","-cf",cp,0); + _exit(127); + }/* End IF */ +#endif + if(pid == -1){ + close(pipe_desc[0]); + close(pipe_desc[1]); + sprintf(tmpbuf,"?Error performing FORK: %s",error_text(errno)); + return(FAIL); + }/* End IF */ + + if (pipe_buf_flag) { + struct buff_line *tmp_line = curbuf->first_line; + + close(buf_pipe[0]); + while (tmp_line) { + int n = tmp_line->byte_count; + char *lb = tmp_line->buffer; + + w = write(buf_pipe[1], lb, n); + while (w > 0) { + n -= w; + lb += w; + if (n == 0) + break; + w = write(pipe_desc[1], lb, n); + } + tmp_line = tmp_line->next_line; + } + close(buf_pipe[1]); + } + close(pipe_desc[1]); + +/* + * Loop reading stuff coming back from the pipe until we get an + * EOF which means the process has finished. Update the screen + * on newlines so the user can see what is going on. + */ + last_intr_flag = intr_flag; + line_cnt = 0; + + while((w = read(pipe_desc[0],pipebuff,sizeof(pipebuff))) > 0){ + + buff_insert(curbuf,curbuf->dot,pipebuff,w); + + if(intr_flag != last_intr_flag){ + kill(pid,SIGINT); + last_intr_flag = intr_flag; + }/* End IF */ + + if(susp_flag){ + cmd_pause(); + }/* End IF */ + + cp = pipebuff; + while(w--){ + if(intr_flag != last_intr_flag) break; + if(*cp++ == '\n' && line_cnt++ > (term_lines / 4)){ + line_cnt = 0; + if(tty_input_pending()) break; + screen_format_windows(); + screen_refresh(); + }/* End IF */ + }/* End While */ + + }/* End While */ + + close(pipe_desc[0]); + +/* + * The wait here is required so that the process we forked doesn't + * stay around as a zombie. + */ + status = 0; + while((w = wait(&status)) != pid && w != -1); + + return(SUCCESS); + +}/* End Routine */ + +/* END OF UNIX CONDITIONAL CODE */ + +#endif + + + +#ifdef VMS + +/* CMD_OSCMD - Issue a command to the operating system + * + * Function: + * + * This routine is called in response to the EC command which allows the + * user to execute operating system commands from within the editor. + */ +cmd_oscmd(ct) +register struct cmd_token *ct; +{ + PREAMBLE(); + + error_message("?VMS Does not currently support EC"); + return(FAIL); + +}/* End Routine */ + +#endif + + + +/* LOAD_QNAME_REGISTER - Loads buffer name into q-register * + * + * Function: + * + * This routine is called by a command operating on the text + * portion of a q-register if the specified q-register is *. + * In this case, we load the name of the current edit buffer + * into it so the user can easilly access it. + */ +void +load_qname_register() +{ +register struct buff_header *hbp; +register struct buff_header *qbp; + + PREAMBLE(); + + hbp = curbuf; + qbp = buff_qfind('*',1); + + if(qbp->zee > 0){ + buff_delete(qbp,0,qbp->zee); + }/* End IF */ + + buff_insert(qbp,qbp->dot,hbp->name,strlen(hbp->name)); + +}/* End Routine */ + +/* RENAME_EDIT_BUFFER - Change the name of the specified edit buffer + * + * Function: + * + * This routine is called by parser exec routines which have + * loaded q-register *. Since the text portion of this q-register + * is the buffer name, it has the effect of changing the name + * of the buffer. + */ +int +rename_edit_buffer(hbp,new_name,uct) +register struct buff_header *hbp; +register char *new_name; +register struct cmd_token *uct; +{ +register int i; +register struct undo_token *ut; +int length; + + PREAMBLE(); + + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_RENAME_BUFFER; + ut->carg1 = hbp->name; + + length = strlen(new_name); + hbp->name = tec_alloc(TYPE_C_CBUFF,length+1); + if(hbp->name == NULL) return(FAIL); + for(i = 0; i < length; i++){ + hbp->name[i] = new_name[i]; + }/* End FOR */ + hbp->name[length] = '\0'; + + buff_switch(hbp,0); + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_SETOPTIONS - Set runtime options via the EJ command + * + * Function: + * + * This routine is called when the user sets runtime variables using + * the EJ command. Although the command is a classic TECO command, the + * actual values are specific to Video TECO + */ +int +cmd_setoptions(arg1,arg2,uct) +int arg1; +int arg2; +struct undo_token *uct; +{ +struct undo_token fake_token; +extern int tab_width; + + PREAMBLE(); + + if(arg1 < SETOPTION_MIN_OPTION || arg1 > SETOPTION_MAX_OPTION){ + error_message("EJ option selector out of range"); + return(FAIL); + }/* End IF */ + + if(!uct) uct = &fake_token; + + switch(arg1){ + case SETOPTION_ALTERNATE_ESCAPE_CHAR: + uct->iarg1 = arg1; + uct->iarg2 = alternate_escape_character; + alternate_escape_character = arg2; + break; + case SETOPTION_SCREEN_WIDTH: + uct->iarg1 = arg1; + uct->iarg2 = forced_width; + forced_width = arg2; + screen_resize(); + break; + case SETOPTION_SCREEN_HEIGHT: + if(arg2 != 0 && arg2 < 3){ + error_message("TECO requires a larger window"); + return(FAIL); + }/* End IF */ + uct->iarg1 = arg1; + uct->iarg2 = forced_height; + forced_height = arg2; + screen_resize(); + break; + case SETOPTION_ALTERNATE_DELETE_CHAR: + uct->iarg1 = arg1; + uct->iarg2 = alternate_delete_character; + alternate_delete_character = arg2; + break; + case SETOPTION_FORCE_8_BIT_CHARS: + uct->iarg1 = arg1; + uct->iarg2 = eight_bit_output_flag; + eight_bit_output_flag = arg2 ? YES : NO; + screen_reformat_windows(); + break; + case SETOPTION_TAB_WIDTH: + uct->iarg1 = arg1; + uct->iarg2 = tab_width; + tab_width = arg2 >= 1 && arg2 <= 16 ? arg2 : NORMAL_TAB_WIDTH; + screen_reformat_windows(); + break; + case SETOPTION_HIDE_CR_CHARS: + uct->iarg1 = arg1; + uct->iarg2 = hide_cr_flag; + hide_cr_flag = arg2 ? YES : NO; + screen_reformat_windows(); + break; + + }/* End Switch */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* CMD_FTAGS - Perform UNIX tags function + * + * Function: + * + * This routine performs various unix tags functions. The following + * functions are supported: + * + * ARG1: Function: + * + * none Load tags file indicated in <string> + * 0 (same as <none>) + * 1 Find tag entry which corresponds with <string>. ARG2 + * is a flags word which says what to do with the tag + */ +int +cmd_tags(uct,arg_count,arg1,arg2,string) +struct cmd_token *uct; +int arg_count; +int arg1; +int arg2; +char *string; +{ +register struct undo_token *ut; +register struct tags *old_tags; +register struct tags *new_tags; +register struct tagent *tep = NULL; +int hashval,skip_cnt; + + PREAMBLE(); + + if(arg_count == 0) arg1 = 0; + if(arg_count < 2) arg2 = 0; + + if(arg1 < TAGS_MIN_OPTION || arg1 > TAGS_MAX_OPTION){ + error_message("FT option selector out of range"); + return(FAIL); + }/* End IF */ + + switch(arg1){ + case TAGS_LOAD_TAGS_FILE: + old_tags = current_tags; + new_tags = tag_load_file(string); + if(new_tags == NULL) return(FAIL); + current_tags = new_tags; + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_LOAD_TAGS; + ut->carg1 = (char *)old_tags; + break; + case TAGS_SEARCH_TAGS_FILE: + if(current_tags == NULL) return(FAIL); + hashval = tag_calc_hash(string); + tep = current_tags->tagents[hashval]; + skip_cnt = arg2; + while(tep){ + if(strcmp(string,tep->te_symbol) == 0){ + if(skip_cnt-- <= 0){ + if(current_tags->current_entry != tep){ + ut = allocate_undo_token(uct); + if(ut == NULL) return(FAIL); + ut->opcode = UNDO_C_SELECT_TAGS; + ut->carg1 = (char *)current_tags->current_entry; + }/* End IF */ + current_tags->current_entry = tep; + return(SUCCESS); + }/* End IF */ + }/* End IF */ + tep = tep->te_next; + }/* End While */ + return(FAIL); + case TAGS_TEST_FOR_LOADED_TAGS: + if(current_tags){ + return(SUCCESS); + }/* End IF */ + return(FAIL); + case TAGS_INSERT_TARGET_FILE: + if(current_tags->current_entry){ + if(current_tags->current_entry->te_filename){ + buff_insert_with_undo( + uct, + curbuf, + curbuf->dot, + current_tags->current_entry->te_filename, + strlen(current_tags->current_entry->te_filename) + ); + return(SUCCESS); + }/* End IF */ + }/* End IF */ + return(FAIL); + case TAGS_INSERT_SEARCH_STRING: + if(current_tags->current_entry){ + if(current_tags->current_entry->te_search_string){ + buff_insert_with_undo( + uct, + curbuf, + curbuf->dot, + current_tags->current_entry->te_search_string, + strlen(current_tags->current_entry->te_search_string) + ); + return(SUCCESS); + }/* End IF */ + }/* End IF */ + return(FAIL); + case TAGS_INSERT_LINENO: + if(current_tags->current_entry){ + if(current_tags->current_entry->te_lineno){ + buff_insert_with_undo( + uct, + curbuf, + curbuf->dot, + current_tags->current_entry->te_lineno, + strlen(current_tags->current_entry->te_lineno) + ); + return(SUCCESS); + }/* End IF */ + }/* End IF */ + return(FAIL); + case TAGS_INSERT_ALL: + if(current_tags){ + tag_dump_database(current_tags,uct); + return(SUCCESS); + }/* End IF */ + return(FAIL); + }/* End Switch */ + + return(SUCCESS); + +}/* End Routine */ + +/* TAG_LOAD_FILE - Read in a tags file + * + * Function: + * + * This is a huge monolithic nasty routine which reads either VI or + * EMACS tags files, and builds an internal representation. + */ +struct tags * +tag_load_file(string) +register char *string; +{ +FILE *fd = NULL; +register struct tags *tp = NULL; +register struct tagent *tep = NULL; +register char *cp; +char *outcp = NULL; +char *filename_cp = NULL; +int state = 0; +int hashval; +int c; +char emacs_flag = NO; +char comma_seen = 0; +char tmp_buffer[LINE_BUFFER_SIZE]; +char tmp_search_string[LINE_BUFFER_SIZE]; +char tmp_filename[LINE_BUFFER_SIZE]; +char tmp_symbol[LINE_BUFFER_SIZE]; +char tmp_number[LINE_BUFFER_SIZE]; +char tmp_message[LINE_BUFFER_SIZE]; +int line = 0; + + fd = fopen(string,"r"); + if(fd == NULL){ + sprintf( + tmp_message, + "Could not open TAGS file <%s>: %s", + string, + error_text(errno) + ); + error_message(tmp_message); + return(NULL); + }/* End IF */ + + tp = (struct tags *)tec_alloc(TYPE_C_TAGS,sizeof(struct tags)); + if(tp == NULL) return(NULL); + bzero((char *)tp,sizeof(struct tags)); + +/* + * Loop reading tag entries + */ + while(1){ + cp = tmp_buffer; + bzero(cp,sizeof(tmp_buffer)); + if(fgets(cp,sizeof(tmp_buffer),fd) == NULL) break; + line++; +/* + * Ok, we have the start of an entry. Check for continuation. + */ + while(cp[0]){ + if(cp[0] == '\n'){ + cp[1] = '\0'; + break; + }/* End IF */ + if(cp[0] == '\\' && cp[1] == '\n'){ + if(fgets(cp,sizeof(tmp_buffer)-(cp-tmp_buffer),fd) == NULL){ + cp[0] = '\n'; cp[1] = '\0'; + }/* End IF */ + line++; + }/* End IF */ + cp++; + }/* End While */ + +/* + * First step is to get the name of the symbol so that we can obtain the + * hash value + */ + cp = tmp_buffer; + + if(emacs_flag == NO) state = 0; + + while(*cp){ + if(state < 0) break; + switch(state){ +/* + * Skip over symbol name until we hit whitespace. Then calculate the hash + * value for that symbol, allocate the tag entry structure, and copy the + * symbol name into it. + */ + case 0: + if(cp[0] == '\f' && cp[1] == '\n'){ + emacs_flag = YES; + state = 100; + continue; + }/* End IF */ + + if(isspace((int)*cp)){ + c = *cp; + *cp = '\0'; + hashval = tag_calc_hash(tmp_buffer); + tep = (struct tagent *)tec_alloc( + TYPE_C_TAGENT, + sizeof(struct tagent) + ); + if(tep == NULL) goto err; + bzero((char *)tep,sizeof(*tep)); + + tep->te_next = tp->tagents[hashval]; + tp->tagents[hashval] = tep; + + tep->te_symbol = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(tmp_buffer) + 1 + ); + if(tep->te_symbol == NULL) goto err; + strcpy(tep->te_symbol,tmp_buffer); + + *cp = c; + state++; + }/* End IF */ + cp++; + continue; +/* + * Skip any amount of whitespace which seperates the symbol from the + * filename. + */ + case 1: + if(!isspace((int)*cp)){ + filename_cp = cp; + state++; + continue; + }/* End IF */ + cp++; + continue; +/* + * Find the length of the filename portion and copy it to the tagent + */ + case 2: + if(isspace((int)*cp)){ + c = *cp; + *cp = '\0'; + + tep->te_filename = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(filename_cp) + 1 + ); + if(tep->te_filename == NULL) goto err; + strcpy(tep->te_filename,filename_cp); + + *cp = c; + state++; + }/* End IF */ + cp++; + continue; +/* + * Skip any amount of whitespace which seperates the filename from the + * search string + */ + case 3: + if(!isspace((int)*cp)){ + state++; + continue; + }/* End IF */ + cp++; + continue; + +/* + * The next character should be a slash to begin the search string + */ + case 4: + if(*cp == '/'){ + outcp = tmp_search_string; + *outcp = '\0'; + state++; + } + cp++; + continue; +/* + * Find the length of the search string portion and copy it to the tagent + */ + case 5: + if(*cp == '/'){ + *outcp = '\0'; + + tep->te_search_string = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(tmp_search_string) + 1 + ); + if(tep->te_search_string == NULL) goto err; + strcpy(tep->te_search_string,tmp_search_string); + + state = -1; + continue; + }/* End IF */ + if(*cp == '\\'){ + state = state + 1; + cp++; + continue; + }/* End IF */ + *outcp++ = *cp++; + continue; + +/* + * Here on a backquote character. Just skip it and pass through the quoted + * character without testing it for termination of the search string. + */ + case 6: + *outcp++ = *cp++; + state = state - 1; + continue; + +/* + * Here if we encounter a form-feed. This is a giveaway that we have an + * emacs style tags file, so we have to parse it differently. + */ + case 100: + if(cp[0] != '\f' || cp[1] != '\n'){ + cp++; + continue; + }/* End IF */ + cp += 2; + state = 101; + outcp = tmp_filename; + comma_seen = 0; + continue; +/* + * Here we get the name of the source file + */ + case 101: + if(*cp == '\n'){ + if(comma_seen == 0){ + state = 100; + continue; + }/* End IF */ + outcp--; + while(*outcp != ',') outcp--; + *outcp = '\0'; + cp++; + state = 102; + outcp = tmp_symbol; + *outcp++ = 127; + continue; + }/* End IF */ + + if(*cp == ',') comma_seen = 1; + *outcp++ = *cp++; + continue; +/* + * Now we get symbol names + */ + case 102: + if(*cp == '\f'){ + state = 100; + continue; + }/* End IF */ + + if(*cp != 127){ + *outcp++ = *cp++; + continue; + }/* End IF */ +/* + * Here on a 127 code which terminates the symbol name. First eat back to + * the first symbol character. + */ + while((c = outcp[-1])){ + if(c == 127) break; + if( + isalnum(c) || + isdigit(c) || + c == '_' || + c == '$' || + c == '.' + ){ + *outcp = '\0'; + break; + }/* End IF */ + outcp--; + }/* End While */ +/* + * Now go back until we find the first non-symbol character. + */ + while((c = outcp[-1])){ + if(c == 127) break; + if( + isalnum(c) || + isdigit(c) || + c == '_' || + c == '$' || + c == '.' + ){ + outcp--; + continue; + }/* End IF */ + break; + }/* End While */ + + hashval = tag_calc_hash(outcp); + tep = (struct tagent *)tec_alloc( + TYPE_C_TAGENT, + sizeof(struct tagent) + ); + if(tep == NULL) goto err; + bzero((char *)tep,sizeof(*tep)); + + tep->te_next = tp->tagents[hashval]; + tp->tagents[hashval] = tep; + + tep->te_symbol = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(outcp) + 1 + ); + if(tep->te_symbol == NULL) goto err; + strcpy(tep->te_symbol,outcp); + tep->te_filename = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(tmp_filename) + 1 + ); + if(tep->te_filename == NULL) goto err; + strcpy(tep->te_filename,tmp_filename); + + state = 103; + continue; + +/* + * Eat until we reach a number, which is the line number + */ + case 103: + if(isdigit((int)*cp)){ + state = 104; + outcp = tmp_number; + continue; + }/* End IF */ + + if(*cp == '\f'){ + state = 100; + continue; + }/* End IF */ + + cp++; + continue; +/* + * Read in the line number + */ + case 104: + if(isdigit((int)*cp)){ + *outcp++ = *cp++; + continue; + }/* End IF */ + + *outcp++ = '\0'; + tep->te_lineno = (char *)tec_alloc( + TYPE_C_TAGSTR, + strlen(tmp_number) + 1 + ); + if(tep->te_lineno == NULL) goto err; + strcpy(tep->te_lineno,tmp_number); + + state = 105; + continue; +/* + * Eat until end of line + */ + case 105: + if(*cp == '\n'){ + cp++; + state = 102; + outcp = tmp_symbol; + continue; + }/* End IF */ + if(*cp == '\f'){ + state = 100; + continue; + }/* End IF */ + cp++; + continue; + }/* End Switch */ + }/* End While */ + + }/* End While */ + + fclose(fd); + return(tp); + +err: + + tag_free_struct(tp); + if(fd) fclose(fd); + return(NULL); + +}/* End Routine */ + +void +tag_free_struct(tp) +register struct tags *tp; +{ +register struct tagent **tepp; +register struct tagent *tep; +register int i; + + if(tp == NULL) return; + + for(i = 0, tepp = &tp->tagents[0]; i < ELEMENTS(tp->tagents); i++,tepp++){ + while((tep = *tepp)){ + *tepp = tep->te_next; + + if(tep->te_symbol){ + tec_release(TYPE_C_TAGSTR,tep->te_symbol); + }/* End IF */ + + if(tep->te_filename){ + tec_release(TYPE_C_TAGSTR,tep->te_filename); + }/* End IF */ + + if(tep->te_search_string){ + tec_release(TYPE_C_TAGSTR,tep->te_search_string); + }/* End IF */ + + tec_release(TYPE_C_TAGENT,(char *)tep); + + }/* End While */ + + }/* End FOR */ + + tec_release(TYPE_C_TAGS,(char *)tp); + +}/* End Routine */ + +int +tag_calc_hash(string) +register char *string; +{ +register int hash = 0; +register int c; +register int shift = 0; + + while((c = *string++)){ + shift = shift == 8 ? 0 : shift + 1; + hash += c << shift; + }/* End While */ + + return(hash % TAG_HASH_ENTRIES); + +}/* End Routine */ + +void +tag_dump_database(tp,uct) +register struct tags *tp; +struct cmd_token *uct; +{ +register struct tagent **tepp; +register struct tagent *tep; +register int i; +char tmp_output[LINE_BUFFER_SIZE]; + + if(tp == NULL) return; + + for(i = 0, tepp = &tp->tagents[0]; i < ELEMENTS(tp->tagents); i++,tepp++){ + tep = *tepp; + while(tep){ + + sprintf( + tmp_output, + "sym <%s> hash %d file <%s> line <%s> search <%s>\n", + tep->te_symbol ? tep->te_symbol : "", + tep->te_symbol ? tag_calc_hash(tep->te_symbol) : 0, + tep->te_filename ? tep->te_filename : "", + tep->te_lineno ? tep->te_lineno : "", + tep->te_search_string ? tep->te_search_string : "" + ); + + buff_insert_with_undo( + uct, + curbuf, + curbuf->dot, + tmp_output, + strlen(tmp_output) + ); + + tep = tep->te_next; + + }/* End While */ + }/* End FOR */ + + return; + +}/* End Routine */ |