aboutsummaryrefslogtreecommitdiff
path: root/teccmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'teccmd.c')
-rw-r--r--teccmd.c2668
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 */