aboutsummaryrefslogtreecommitdiff
path: root/tecbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tecbuf.c')
-rw-r--r--tecbuf.c2710
1 files changed, 2710 insertions, 0 deletions
diff --git a/tecbuf.c b/tecbuf.c
new file mode 100644
index 0000000..1dd93e9
--- /dev/null
+++ b/tecbuf.c
@@ -0,0 +1,2710 @@
+char *tecbuf_c_version = "tecbuf.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecbuf.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecbuf.c
+ * Subroutines to handle the edit buffers
+ *
+ * COPYRIGHT (c) 1985-2004 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"
+
+char rcs_date[] = AUTO_DATE;
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ * First off are all the flags.
+ */
+
+/*
+ * Global Variables
+ */
+ struct buff_header *buffer_headers;
+ struct buff_header *curbuf;
+ struct buff_header *qregister_push_down_list;
+
+/*
+ * Global structures
+ */
+ struct buff_line *line_buffer_lookaside_list = NULL;
+/*
+ * Forward References
+ */
+ struct buff_line *allocate_line_buffer(int);
+ void movc3(char *,char *,int);
+ void buff_buffer_map( void );
+ unsigned int stringHash( char *str );
+
+ extern FILE *teco_debug_log;
+ extern int term_columns;
+ extern struct window *curwin;
+ extern char checkpoint_modified;
+
+/* BUFF_FIND - Find the named buffer
+ *
+ * Function:
+ *
+ * This routine is called with the name of the buffer that we want to
+ * find. It searches all the buffer headers until it finds the name.
+ * It will either return the address of the buffer header, or null if
+ * it could not find one with the proper name.
+ */
+struct buff_header *
+buff_find(name)
+char *name;
+{
+register struct buff_header *bp;
+register char *cp1,*cp2;
+unsigned int hash = stringHash( name );
+
+ PREAMBLE();
+ for(bp = buffer_headers; bp != NULL; bp = bp->next_header){
+ if( hash != bp->buf_hash )
+ {
+// printf("skipping because hash 0x%08x != 0x%08x %s %s\n",
+// hash, bp->buf_hash, name, bp->name);
+ continue;
+ }
+
+ printf("hash match! %s %s\n",name,bp->name);
+/*
+ * This loop implements the equivalent of strcmp, except that in the
+ * case of VMS, it is case insensitive.
+ */
+ for(cp1 = name, cp2 = bp->name;;cp1++,cp2++){
+#ifdef VMS
+ if(UPCASE(*cp1) != UPCASE(*cp2)) break;
+#else
+ if(*cp1 != *cp2) break;
+#endif
+ if(*cp1 == '\0') return(bp);
+ }/* End FOR */
+
+ }/* End FOR */
+
+ return(NULL);
+
+}/* End Routine */
+
+
+
+/* BUFF_QFIND - Find the named Q register
+ *
+ * Function:
+ *
+ * This routine is called with the name of the Q register that we
+ * want to find. It constructs the internal name of the Q register
+ * and then calls buff_find to find the buffer. If the buffer is not
+ * found and the create flag is set, we call buff_create to cause
+ * the Q register to be created.
+ */
+struct buff_header *
+buff_qfind(name,create_flag)
+char name;
+char create_flag;
+{
+register struct buff_header *hbp;
+register int i;
+char tmp_buffer[LINE_BUFFER_SIZE],tmp_message[LINE_BUFFER_SIZE];
+struct buff_header *buff_create();
+
+ PREAMBLE();
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ i = strlen(tmp_buffer);
+ name = UPCASE((int)name);
+ tmp_buffer[i] = name; tmp_buffer[i+1] = '\0';
+ hbp = buff_find(tmp_buffer);
+ if(hbp == NULL){
+ if(create_flag){
+ hbp = buff_create(tmp_buffer,1);
+ if(hbp == (struct buff_header *)NULL){
+ sprintf(tmp_message,"?Cannot create Q-register %c",name);
+ error_message(tmp_message);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+
+ return(hbp);
+
+}/* End Routine */
+
+
+
+/* BUFF_CREATE - Create a new buffer
+ *
+ * Function:
+ *
+ * This routine is called when we need to create a new buffer. The caller
+ * should have already verified that this doesn't already exist. The
+ * routine returns the address of the new buffer, or NULL if there is a
+ * problem of some sort.
+ */
+struct buff_header *
+buff_create(name,internal_flag)
+char *name;
+char internal_flag;
+{
+register struct buff_header *bp;
+register struct buff_header *obp;
+register struct buff_line *lbp;
+register int i = 0;
+
+ PREAMBLE();
+
+ if(buff_find(name)) return(NULL);
+
+/*
+ * Create the buffer header itself.
+ */
+ bp = (struct buff_header *)tec_alloc(
+ TYPE_C_BHDR,
+ sizeof(struct buff_header)
+ );
+
+ if(bp == NULL) return(NULL);
+ bp->buf_magic = MAGIC_BUFFER;
+ bp->buf_hash = stringHash( name );
+
+ bp->name = tec_alloc(TYPE_C_CBUFF,strlen(name)+1);
+ if(bp->name == NULL){
+ bp->buf_magic = 0;
+ tec_release(TYPE_C_BHDR,(char *)bp);
+ return(NULL);
+ }/* End IF */
+ (void) strcpy(bp->name,name);
+
+ obp = buffer_headers;
+ if(obp) i = obp->buffer_number;
+ while(obp){
+ if(internal_flag){
+ if(obp->buffer_number < i) i = obp->buffer_number;
+ }
+ else {
+ if(obp->buffer_number > i) i = obp->buffer_number;
+ }
+ obp = obp->next_header;
+ }/* End While */
+
+ if(internal_flag) bp->buffer_number = i - 1;
+ else bp->buffer_number = i + 1;
+
+ bp->ismodified = NO;
+ bp->isreadonly = NO;
+ bp->isbackedup = NO;
+ bp->dot = 0;
+ bp->zee = 0;
+
+/*
+ * Create the first line buffer structure
+ */
+ lbp = allocate_line_buffer(1);
+ if(lbp == NULL){
+ bp->buf_magic = 0;
+ tec_release(TYPE_C_CBUFF,bp->name);
+ tec_release(TYPE_C_BHDR,(char *)bp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ bp->first_line = lbp;
+/*
+ * Initialize the rest of the special locations
+ */
+ bp->pos_cache.lbp = NULL;
+ bp->pos_cache.base = 0;
+
+/*
+ * Link it into the list of buffer headers, in order by buffer number
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(obp->next_header == NULL) break;
+ if(obp->next_header->buffer_number > bp->buffer_number) break;
+ obp = obp->next_header;
+ }/* End While */
+
+ if(obp == NULL || buffer_headers->buffer_number > bp->buffer_number){
+ bp->next_header = buffer_headers;
+ buffer_headers = bp;
+ }/* End IF */
+
+ else {
+ bp->next_header = obp->next_header;
+ obp->next_header = bp;
+ }/* End Else */
+
+ return(bp);
+
+}/* End Routine */
+
+
+
+/* BUFF_DUPLICATE - Make a duplicate of a buffer
+ *
+ * Function:
+ *
+ * This routine is called to duplicate the specified buffer and return
+ * a pointer to the duplicate. The current requirement for this is for
+ * the push Q register command ('[').
+ */
+struct buff_header *
+buff_duplicate(sbp)
+register struct buff_header *sbp;
+{
+register struct buff_header *dbp;
+register struct buff_line *slp;
+register struct buff_line *dlp;
+
+ PREAMBLE();
+/*
+ * Create the buffer header itself.
+ */
+ dbp = (struct buff_header *)
+ tec_alloc(TYPE_C_BHDR,sizeof(struct buff_header));
+ if(dbp == NULL){
+ return((struct buff_header *)NULL);
+ }/* End IF */
+
+ dbp->name = tec_alloc(TYPE_C_CBUFF,strlen(sbp->name)+1);
+ if(dbp->name == NULL){
+ tec_release(TYPE_C_BHDR,(char *)dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+
+ (void) strcpy(dbp->name,sbp->name);
+
+ dbp->buf_magic = MAGIC_BUFFER;
+ dbp->ismodified = sbp->ismodified;
+ dbp->isreadonly = sbp->isreadonly;
+ dbp->isbackedup = sbp->isbackedup;
+ dbp->dot = sbp->dot;
+ dbp->zee = sbp->zee;
+ dbp->ivalue = sbp->ivalue;
+ dbp->pos_cache.lbp = NULL;
+ dbp->pos_cache.base = 0;
+ dbp->first_line = NULL;
+
+/*
+ * Copy the line buffer structures. If an allocation error occurs, we
+ * have to return all the allocated memory, and return an error.
+ */
+ slp = sbp->first_line;
+
+ if(slp){
+ dbp->first_line = dlp = allocate_line_buffer(slp->byte_count);
+ if(dlp == NULL){
+ buff_destroy(dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ movc3(slp->buffer,dlp->buffer,slp->byte_count);
+ dlp->byte_count = slp->byte_count;
+
+ while((slp = slp->next_line)){
+ dlp->next_line = allocate_line_buffer(slp->byte_count);
+ if(dlp->next_line == NULL){
+ buff_destroy(dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ dlp->next_line->prev_line = dlp;
+ dlp = dlp->next_line;
+ movc3(slp->buffer,dlp->buffer,slp->byte_count);
+ dlp->byte_count = slp->byte_count;
+ }/* End While */
+
+ }/* End IF */
+
+ return(dbp);
+
+}/* End Routine */
+
+
+
+/* MOVC3 - Copy the specified number of bytes
+ *
+ * Function:
+ *
+ * This routine copies 'n' bytes from the source to the
+ * destination.
+ */
+void
+movc3(source,dest,count)
+register char *source;
+register char *dest;
+register int count;
+{
+
+ PREAMBLE();
+ while(count-- > 0) *dest++ = *source++;
+
+}/* End Routine */
+
+
+
+/* BUFF_DESTROY - Delete an existing buffer
+ *
+ * Function:
+ *
+ * This routine is called to delete an existing buffer. The routine is
+ * supplied the address of the buffer structure. We have to clean up
+ * all the lines in the buffer, all the display lines, etc.
+ */
+void
+buff_destroy(hbp)
+register struct buff_header *hbp;
+{
+register struct buff_header *bp;
+register struct buff_line *lbp;
+
+ PREAMBLE();
+/*
+ * There are certain buffers we don't allow to be destroyed
+ */
+ if(strcmp(hbp->name,"TECO_INTERNAL-Main") == 0){
+ return;
+ }/* End IF */
+/*
+ * Clean up all the data in the buffer first
+ */
+ lbp = hbp->first_line;
+ hbp->pos_cache.lbp = NULL;
+ hbp->first_line = NULL;
+ buff_free_line_buffer_list(lbp);
+
+/*
+ * Now unlink this buffer header from the header list.
+ * Check to see if it is at the head of the list.
+ */
+ bp = buffer_headers;
+ if(bp == hbp){
+ buffer_headers = bp->next_header;
+ }/* End IF */
+
+/*
+ * If not at the head of the list, we have to search the list till
+ * we find it's father. Then we make it's father's child be it's
+ * child.
+ */
+ else {
+ while(bp){
+ if(bp->next_header == hbp){
+ bp->next_header = hbp->next_header;
+ break;
+ }/* End IF */
+ bp = bp->next_header;
+ }/* End While */
+
+ }/* End Else */
+
+/*
+ * Now give back the storage for the name, and for the structure itself.
+ */
+ if(hbp->name) tec_release(TYPE_C_CBUFF,(char *)hbp->name);
+ tec_release(TYPE_C_BHDR,(char *)hbp);
+
+ return;
+
+}/* End Routine */
+
+
+
+/* BUFF_FIND_LINE - Find the line structure that <position> is on
+ *
+ * Function:
+ *
+ * This routine is called to find the line_buffer structure that
+ * the buffer position resides on.
+ */
+struct buff_line *
+buff_find_line(hbp,position)
+register struct buff_header *hbp;
+int position;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+
+ if(hbp == NULL) return(NULL);
+
+/*
+ * First step is to see if the current position cache in the buffer header will
+ * help us out. Our position must be on this line if it is to be of help to us.
+ */
+ lbp = hbp->pos_cache.lbp;
+ if(lbp){
+
+ i = hbp->pos_cache.base + lbp->byte_count;
+/*
+ * Test to see if it is a currently existing character position that is mapped
+ * by our position cache.
+ */
+ if(position >= hbp->pos_cache.base && position < i){
+ return(lbp);
+ }/* End IF */
+/*
+ * If we are inserting at the end of the buffer, the position doesn't really
+ * exist, but we can still resolve it if we happen to have the final line
+ * cached. The only case where we don't want to do this is when the line
+ * already has a trailing newline. Then we want to fall down into the code
+ * below which will recognize this and construct a new line buffer.
+ */
+ if(position == hbp->zee && position == i){
+ if(lbp->byte_count == 0) return(lbp);
+ if(lbp->buffer[lbp->byte_count - 1] != '\n') return(lbp);
+ }/* End IF */
+
+ }/* End IF */
+
+/*
+ * We get here if the current cached location does not help us out. In this
+ * case we need to hunt till we find the correct place in the buffer. We also
+ * update our cache to remember this.
+ */
+ i = position;
+ lbp = hbp->first_line;
+
+ if(lbp == NULL){
+ return(NULL);
+ }/* End IF */
+
+/*
+ * Now, even though the cache did not give us the correct line, it can still
+ * help us out in the search if it maps a position before the one we are
+ * looking for, since we can begin our search at it's base.
+ */
+ if(hbp->pos_cache.lbp && hbp->pos_cache.base < position){
+ i -= hbp->pos_cache.base;
+ lbp = hbp->pos_cache.lbp;
+ }/* End IF */
+
+ while(1){
+ if(lbp->byte_count >= i) break;
+ i -= lbp->byte_count;
+ if(lbp->next_line == NULL){
+ char panic_string[LINE_BUFFER_SIZE];
+ sprintf(panic_string,
+ "buff_find_line: position %d specified in buffer %s, z=%d",
+ position,hbp->name,hbp->zee);
+ tec_panic(panic_string);
+ printf("NULL ptr %d bytes still to go\n",i);
+ }/* End IF */
+ lbp = lbp->next_line;
+ }/* End While */
+
+ if(i == lbp->byte_count && i && lbp->buffer[i-1] == '\n'){
+ i = 0;
+ if(lbp->next_line == NULL){
+ lbp->next_line = allocate_line_buffer(1);
+ if(lbp->next_line == NULL) return((struct buff_line *)NULL);
+ lbp->next_line->prev_line = lbp;
+ }/* End IF */
+ lbp = lbp->next_line;
+ }/* End IF */
+
+ hbp->pos_cache.lbp = lbp;
+ hbp->pos_cache.base = position - i;
+
+ return(lbp);
+
+}/* End Routine */
+
+
+
+/* BUFF_FIND_OFFSET - Find offset onto line structure that <position> is on
+ *
+ * Function:
+ *
+ * This routine is called to find the offset into the line_buffer of
+ * the specified position.
+ */
+int
+buff_find_offset(hbp,lbp,position)
+register struct buff_header *hbp;
+register struct buff_line *lbp;
+int position;
+{
+
+ PREAMBLE();
+ if(hbp == NULL) return(-1);
+/*
+ * First step is to see if the current position cache in the buffer header will
+ * help us out. Our position must be on this line if it is to be of help to us.
+ */
+ if( lbp == NULL || hbp->pos_cache.lbp != lbp){
+ lbp = buff_find_line(hbp,position);
+ if(lbp == NULL) return(-1);
+ }/* End IF */
+
+/*
+ * Now we simply calculate the position's offset from the line structure base.
+ */
+ return( position - hbp->pos_cache.base );
+
+}/* End Routine */
+
+
+
+/* BUFF_CONTENTS - Return the character at the specified position
+ *
+ * Function:
+ *
+ * This routine is called to fetch the single character which is at
+ * the specified buffer position. This routine is generally called
+ * by routines which are low frequence or only handle a small number
+ * of characters at any one time.
+ */
+int
+buff_contents(hbp,position)
+register struct buff_header *hbp;
+register int position;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee || position < 0){
+ char panic_string[LINE_BUFFER_SIZE];
+ sprintf(panic_string,
+ "buff_contents: illegal position %d specified in buffer %s",
+ position,hbp->name);
+ tec_panic(panic_string);
+ }/* End IF */
+
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+ if(i == -1) return(-1);
+
+ return(lbp->buffer[i] & 0xFF);
+
+}/* End Routine */
+
+/* BUFF_CACHED_CONTENTS - Return the character and positional information
+ *
+ * Function:
+ *
+ * This routine is called to fetch the single character which is at
+ * the specified buffer position. This routine is generally called
+ * by routines which are low frequence or only handle a small number
+ * of characters at any one time.
+ */
+int
+buff_cached_contents(hbp,position,cache)
+register struct buff_header *hbp;
+int position;
+struct position_cache *cache;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee || position < 0){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_contents: illegal position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ if(lbp == NULL) return(-1);
+ i = buff_find_offset(hbp,lbp,position);
+ if(i == -1) return(-1);
+/*
+ * If he has specified a local position cache, copy that information back to
+ * his local cache. It's up to him to invalidate it if the buffer gets changed.
+ */
+ if(cache){
+ cache->lbp = lbp;
+ cache->base = i;
+ }/* End IF */
+
+ return(lbp->buffer[i] & 0xFF);
+
+}/* End Routine */
+
+
+
+/* BUFF_INIT - Routine to initialize the buffer routines
+ *
+ * Function:
+ *
+ * This routine is called before the first buffer operations to
+ * initialize the buffer routines.
+ */
+int
+buff_init()
+{
+ register struct buff_header *hbp;
+ char tmp_buffer[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+/*
+ * Create the buffer map buffer
+ */
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ (void) strcat(tmp_buffer,"BUFFER-MAP");
+ hbp = buff_create(tmp_buffer,1);
+ if(hbp == NULL) return(FAIL);
+ hbp->buffer_number = 0;
+
+/*
+ * Create a default edit buffer
+ */
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ (void) strcat(tmp_buffer,"Main");
+ curbuf = hbp = buff_create(tmp_buffer,1);
+ if(hbp == NULL) return(FAIL);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_OPENBUFFER - Routine to read a file into a TECO edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to load a file into the named buffer. If the
+ * buffer already exists, we simply switch to it and don't modify it.
+ */
+int
+buff_openbuffer(name,buffer_number,readonly_flag)
+char *name;
+int buffer_number;
+int readonly_flag;
+{
+ register struct buff_header *hbp = NULL;
+
+ PREAMBLE();
+/*
+ * If no name is supplied then the buffer_number argument is significant,
+ * otherwise we ignore it.
+ */
+ if(name == NULL){
+ hbp = buffer_headers;
+ while(hbp){
+ if(hbp->buffer_number == buffer_number){
+ name = hbp->name;
+ break;
+ }/* End IF */
+ hbp = hbp->next_header;
+ }/* End While */
+ }/* End IF */
+/*
+ * If no name is supplied, the buffer_number argument must already exist
+ * or an error is declared.
+ */
+ if(name == NULL){
+ error_message("?No such buffer number exists");
+ return(FAIL);
+ }/* End IF */
+/*
+ * First determine if a buffer of that name already exists
+ */
+ if(hbp == NULL){
+ hbp = buff_find(name);
+ }/* End IF */
+
+ if(hbp){
+ buff_switch(hbp,1);
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If there is not such buffer in existence, we need to create one
+ */
+ hbp = buff_create(name,0);
+ if(hbp == NULL) return(FAIL);
+ if(readonly_flag == YES) hbp->isreadonly = YES;
+
+/*
+ * Ignore possible open error, since he may be creating a new file
+ */
+ buff_read(hbp,name);
+
+ hbp->dot = 0;
+ hbp->ismodified = NO;
+
+ buff_switch(hbp,1);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_OPENBUFFNUM - Routine to open buffer number 'n'
+ *
+ * Function:
+ *
+ * This routine is called with the number of a buffer which is to be
+ * made the 'current' edit buffer.
+ */
+int
+buff_openbuffnum(buffer_number,map_flag)
+int buffer_number;
+int map_flag;
+{
+ register struct buff_header *hbp;
+
+ PREAMBLE();
+/*
+ * Search the list of buffers for the specified buffer number
+ */
+ hbp = buffer_headers;
+ while(hbp){
+ if(hbp->buffer_number == buffer_number){
+ buff_switch(hbp,map_flag);
+ return(SUCCESS);
+ }/* End IF */
+ hbp = hbp->next_header;
+ }/* End While */
+
+ return(FAIL);
+
+}/* End Routine */
+
+
+
+/* BUFF_REOPENBUFF - Called by undo to re-create an edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to place a buffer back onto the buffer
+ * list.
+ */
+void
+buff_reopenbuff(bp)
+register struct buff_header *bp;
+{
+register struct buff_header *obp;
+
+ PREAMBLE();
+/*
+ * Make sure there isn't another buffer of this name already here. The
+ * way this can happen is automatically created Q-registers like the
+ * search string and rubout string Q-registers might be created. Since
+ * they don't get undone, they would stick around and then we might
+ * try to undo the removal of an earlier copy of one of them. This way,
+ * we make sure that any earlier copy replaces any more recent copy.
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(strcmp(obp->name,bp->name) == 0){
+ buff_destroy(obp);
+ break;
+ }/* End IF */
+ obp = obp->next_header;
+ }/* End While */
+/*
+ * Insert this buffer in the proper place on the buffer list.
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(obp->next_header == NULL) break;
+ if(obp->next_header->buffer_number > bp->buffer_number) break;
+ obp = obp->next_header;
+ }/* End While */
+
+ if(obp == NULL || buffer_headers->buffer_number > bp->buffer_number){
+ bp->next_header = buffer_headers;
+ buffer_headers = bp;
+ }/* End IF */
+
+ else {
+ bp->next_header = obp->next_header;
+ obp->next_header = bp;
+ }/* End Else */
+
+}/* End Routine */
+
+
+
+/* BUFF_READ - Routine to read a file into an existing TECO edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to read a file into the specified edit buffer.
+ * The edit buffer must already exist.
+ */
+int
+buff_read(hbp,name)
+struct buff_header *hbp;
+char *name;
+{
+ int iochan;
+ char tmp_message[LINE_BUFFER_SIZE];
+ int status;
+
+ PREAMBLE();
+/*
+ * Attempt to open the file
+ */
+ iochan = open(name,O_RDONLY);
+ if(iochan < 0){
+ sprintf(tmp_message,"?Cannot open <%s>: %s",name,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ status = buff_readfd(hbp,name,iochan);
+ close(iochan);
+ return(status);
+
+}/* End Routine */
+
+
+
+/* BUFF_READFD - Reads the file descriptor into the specified buffer
+ *
+ * Function:
+ *
+ * This routine is called with a buffer and a file descriptor. The
+ * entire contents of the file descriptor are read into the specified
+ * buffer.
+ */
+int
+buff_readfd(hbp,name,iochan)
+struct buff_header *hbp;
+char *name;
+int iochan;
+{
+ char iobuf[IO_BUFFER_SIZE];
+ char linebuf[IO_BUFFER_SIZE];
+ int linebuf_cnt;
+ char tmp_message[LINE_BUFFER_SIZE];
+ register int bcount;
+ register char *cp,*iop = 0;
+ register int i;
+ register struct buff_line *lbp;
+ struct buff_line *olbp;
+ int original_zee = hbp->zee;
+ int error_status = SUCCESS;
+
+ PREAMBLE();
+/*
+ * Read the file and insert the characters into the buffer
+ */
+ cp = linebuf;
+ linebuf_cnt = bcount = 0;
+
+ while(1){
+ if(bcount <= 0){
+ bcount = read(iochan,iobuf,sizeof(iobuf));
+ iop = iobuf;
+ if(bcount == 0) break;
+ if(bcount < 0){
+ sprintf(tmp_message,"?Error reading <%s>: %s",
+ name,error_text(errno));
+ error_message(tmp_message);
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ }/* End IF */
+
+ linebuf_cnt += 1;
+ bcount -= 1;
+ if((*cp++ = *iop++) == '\n' || linebuf_cnt >= sizeof(linebuf)){
+ lbp = buff_find_line(hbp,hbp->dot);
+ if(lbp == NULL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ i = buff_find_offset(hbp,lbp,hbp->dot);
+ if(i == -1){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ if(i == 0){
+ olbp = lbp;
+ lbp = allocate_line_buffer(linebuf_cnt);
+ if(lbp == NULL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+
+ lbp->next_line = olbp;
+ lbp->prev_line = olbp->prev_line;
+ olbp->prev_line = lbp;
+ if(lbp->prev_line) lbp->prev_line->next_line = lbp;
+ if(hbp->first_line == olbp) hbp->first_line = lbp;
+ movc3(linebuf,lbp->buffer,linebuf_cnt);
+ lbp->byte_count = linebuf_cnt;
+ hbp->pos_cache.lbp = lbp;
+ hbp->pos_cache.base = hbp->dot;
+ hbp->dot += linebuf_cnt;
+ hbp->zee += linebuf_cnt;
+ }/* End IF */
+
+ else {
+ if(buff_insert(hbp,hbp->dot,linebuf,linebuf_cnt) == FAIL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ }/* End Else */
+
+ cp = linebuf;
+ linebuf_cnt = 0;
+ continue;
+ }/* End IF */
+
+ }/* End While */
+
+ if(error_status == SUCCESS && linebuf_cnt != 0){
+ if(buff_insert(hbp,hbp->dot,linebuf,linebuf_cnt) == FAIL){
+ error_status = FAIL;
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(hbp->ismodified == NO && hbp->zee != original_zee){
+ if(hbp == curbuf){
+ if(screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED) == FAIL){
+ error_status = FAIL;
+ }/* End IF */
+ }/* End IF */
+ hbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+ return(error_status);
+
+}/* End Routine */
+
+
+
+/* BUFF_WRITE - Write the specified buffer out to a file
+ *
+ * Function:
+ *
+ * This routine is generally called on an EW command when the user
+ * wishes to write out the contents of the buffer.
+ */
+int
+buff_write(hbp,chan,start,end)
+struct buff_header *hbp;
+int chan;
+int start;
+int end;
+{
+register int bcount;
+register struct buff_line *lbp;
+register char *cp1,*cp2;
+register int line_bcount;
+char iobuf[IO_BUFFER_SIZE];
+int status;
+
+ PREAMBLE();
+
+ bcount = 0;
+ cp2 = iobuf;
+
+ lbp = buff_find_line(hbp,start);
+ line_bcount = buff_find_offset(hbp,lbp,start);
+ cp1 = lbp->buffer + line_bcount;
+ line_bcount = lbp->byte_count - line_bcount;
+
+ while(start < end){
+ if(bcount == sizeof(iobuf)){
+ status = write(chan,iobuf,sizeof(iobuf));
+ if(status != sizeof(iobuf)){
+ return(FAIL);
+ }/* End IF */
+ bcount = 0;
+ cp2 = iobuf;
+ }/* End IF */
+
+ if(line_bcount == 0){
+ lbp = lbp->next_line;
+ cp1 = lbp->buffer;
+ line_bcount = lbp->byte_count;
+ }/* End IF */
+
+ *cp2++ = *cp1++;
+ bcount += 1;
+ line_bcount -= 1;
+ start += 1;
+
+ }/* End While */
+
+ if(bcount > 0){
+ status = write(chan,iobuf,(unsigned)bcount);
+ if(status != bcount){
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_SWITCH - Switch the current edit buffer
+ *
+ * Function:
+ *
+ * This routine is called when we wish to switch the current buffer
+ * to some other edit buffer.
+ */
+int
+buff_switch(hbp,map_flag)
+struct buff_header *hbp;
+int map_flag;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+char *cp;
+
+ PREAMBLE();
+
+ curbuf = curwin->win_buffer = hbp;
+
+ sprintf(tmp_message,"<Buffer %d> %s",hbp->buffer_number,hbp->name);
+ screen_label_line(hbp,tmp_message,LABEL_C_FILENAME);
+
+ cp = "";
+ if(hbp->isreadonly) cp = "(READONLY)";
+ screen_label_line(hbp,cp,LABEL_C_READONLY);
+
+ cp = "";
+ if(hbp->ismodified) cp = "(modified)";
+ screen_label_line(hbp,cp,LABEL_C_MODIFIED);
+
+ if(hbp->buffer_number == 0 && map_flag){
+ buff_buffer_map();
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_BUFFER_MAP - Build a map of the existing buffers.
+ *
+ * Function:
+ *
+ * This routine is called when the buffer map buffer is entered. It is
+ * used to fill this routine with the appropriate text.
+ */
+void
+buff_buffer_map()
+{
+register struct buff_header *hbp;
+char tmp_buffer[LINE_BUFFER_SIZE],padd_buffer[LINE_BUFFER_SIZE];
+register int i;
+int count;
+register char *cp;
+int max_length;
+
+ PREAMBLE();
+
+ buff_delete(curbuf,0,curbuf->zee);
+
+ for(i = 0; i < sizeof(padd_buffer); i++){
+ padd_buffer[i] = ' ';
+ }/* End FOR */
+ padd_buffer[sizeof(padd_buffer)-1] = '\0';
+
+ max_length = 0;
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number == 0) continue;
+ i = strlen(hbp->name);
+ if(i > max_length) max_length = i;
+ }/* End FOR */
+
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number <= 0) continue;
+ i = max_length - strlen(hbp->name);
+ sprintf(tmp_buffer,"<Buffer %-4d> %s%s %s %6d bytes\n",
+ hbp->buffer_number,hbp->name,&padd_buffer[sizeof(padd_buffer)-1-i],
+ hbp->ismodified ? "(modified)" : " ",hbp->zee);
+ i = strlen(tmp_buffer);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,i);
+ }/* End FOR */
+
+ buff_insert(curbuf,curbuf->dot,"\n",1);
+
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number >= 0) continue;
+ i = max_length - strlen(hbp->name);
+ sprintf(tmp_buffer,"<Buffer %-4d> %s%s %s %6d bytes\n",
+ hbp->buffer_number,hbp->name,&padd_buffer[sizeof(padd_buffer)-1-i],
+ hbp->ismodified ? "(modified)" : " ",hbp->zee);
+ i = strlen(tmp_buffer);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,i);
+ }/* End FOR */
+
+/*
+ * Here to tell him if any q registers are currently on the push down
+ * list.
+ */
+ i = count = 0;
+ for(hbp = qregister_push_down_list; hbp != NULL ; hbp = hbp->next_header){
+ i += 1;
+ count += hbp->zee;
+ }/* End FOR */
+
+ if(i > 0){
+ sprintf(tmp_buffer,
+ "\nQ register push down list contains %d register%s, %d bytes\n",
+ i,(i > 1)?"s":"",count);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+ }/* End IF */
+
+/*
+ * Insert our TITLE
+ */
+ max_length = term_columns <= 80 ? term_columns : 80;
+
+ buff_insert(curbuf,curbuf->dot,"\n\n",2);
+ (void) strcpy(tmp_buffer,"Video TECO");
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ (void) strcpy(tmp_buffer," by Paul Cantrell & Joyce Nishinaga");
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+#if 0
+ (void) strcpy(tmp_buffer,id_string);
+ if(tmp_buffer[0] == '%'){
+ tmp_buffer[0] = '\0';
+ cp = rcs_id;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Revision: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = 0;
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+ if(i <= 2) sprintf(tmp_buffer,"*%d.%d* ",VMAJOR,VMINOR);
+
+ cp = rcs_date;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Date: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = strlen(tmp_buffer);
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+
+ }/* End IF */
+#endif
+ sprintf(tmp_buffer,"*%d.%d* ",VMAJOR,VMINOR);
+ cp = rcs_date;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Date: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = strlen(tmp_buffer);
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ tecmem_stats();
+
+ curbuf->dot = 0;
+ return;
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT - Insert a string at current position
+ *
+ * Function:
+ *
+ * This routine is called to insert a string into the specified buffer
+ * at the buffer's current location.
+ */
+int
+buff_insert(hbp,position,buffer,length)
+struct buff_header *hbp;
+register int position;
+register char *buffer;
+register int length;
+{
+struct buff_header fake_header;
+struct buff_line *fake_line;
+register char *cp;
+
+ PREAMBLE();
+
+ fake_line =
+ (struct buff_line *)tec_alloc(TYPE_C_LINE,sizeof(struct buff_line));
+
+ if(fake_line == NULL) return(FAIL);
+
+ fake_header.dot = 0;
+ fake_header.zee = 0;
+ fake_header.pos_cache.lbp = fake_line;
+ fake_header.pos_cache.base = 0;
+ fake_header.first_line = fake_line;
+
+ fake_line->buffer_size = LINE_BUFFER_SIZE;
+ fake_line->buffer = buffer;
+ fake_line->next_line = NULL;
+ fake_line->prev_line = NULL;
+ fake_line->format_line = NULL;
+
+ while(length){
+ cp = fake_line->buffer;
+ fake_line->byte_count = 0;
+ while(1){
+ fake_line->byte_count += 1;
+ length -= 1;
+ if(*cp == '\n' || length == 0){
+ buff_insert_from_buffer_with_undo(
+ NULL,
+ hbp,
+ position,
+ &fake_header,
+ 0,
+ fake_line->byte_count
+ );
+/*
+ * The following is a terrible kludge. The buff_find_line code ends up
+ * inserting a null line buffer after our fake line, in order to
+ * represent the location following the last character. If we don't
+ * free it, it will get lost and consume memory...
+ */
+ if(fake_line->next_line){
+ buff_free_line_buffer(fake_line->next_line);
+ fake_line->next_line = NULL;
+ }/* End IF */
+ fake_line->buffer += fake_line->byte_count;
+ position += fake_line->byte_count;
+ break;
+ }/* End IF */
+ cp++;
+ }/* End While */
+ }/* End While */
+
+ tec_release(TYPE_C_LINE,(char *)fake_line);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT_FROM_BUFFER_WITH_UNDO - Copy bytes from another buffer
+ *
+ * Function:
+ *
+ * This routine copies bytes from one buffer to another, arranging
+ * undo capability so that we can reverse the process. Because it
+ * knows the lengths of each line, this should be a very efficient
+ * method of inserting data, and should certainly have preference
+ * over using the byte-at-a-time routines.
+ */
+int
+buff_insert_from_buffer_with_undo(ct,dbp,dest_position,sbp,src_position,length)
+struct cmd_token *ct;
+struct buff_header *dbp;
+int dest_position;
+struct buff_header *sbp;
+int src_position;
+int length;
+{
+register int i;
+struct undo_token *ut = 0;
+struct buff_line *dlbp,*sb_lbp,*se_lbp,*nlbp;
+int doff,sb_off,se_off;
+int newline_included_in_src_data;
+int bytes_to_copy_from_source;
+int bytes_required_for_line,bytes_to_allocate;
+char *new_buffer;
+register char *dcp,*scp;
+int bytes_inserted_so_far = 0;
+
+#ifdef DEBUG
+char outbuf[1024];
+char inbuf[10];
+extern int tty_input_chan;
+ term_goto(0,0);
+ term_clrtobot();
+ term_flush();
+#endif /* DEBUG */
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(src_position < 0 || src_position > sbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo: bad src pos %d %s Z = %d\n",
+ src_position,
+ sbp->name,sbp->zee
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+ if(dest_position < 0 || dest_position > dbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo: bad dest pos %d %s Z = %d\n",
+ dest_position,
+ dbp->name,dbp->zee
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ if(ct){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ }/* End IF */
+
+/*
+ * Determine which line the current insert point is on by calling the
+ * find routines.
+ */
+ dlbp = buff_find_line(dbp,dest_position);
+ doff = buff_find_offset(dbp,dlbp,dest_position);
+
+ sb_lbp = buff_find_line(sbp,src_position);
+ sb_off = buff_find_offset(sbp,sb_lbp,src_position);
+
+ se_lbp = buff_find_line(sbp,src_position+length);
+ se_off = buff_find_offset(sbp,se_lbp,src_position+length);
+
+#ifdef DEBUG
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n') outbuf[dlbp->byte_count-1] = '\0';
+ fprintf(stderr,"'%s' dest begin doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(sb_lbp->buffer,outbuf,sb_lbp->byte_count);
+ outbuf[sb_lbp->byte_count] = '\0';
+ if(outbuf[sb_lbp->byte_count-1] == '\n'){
+ outbuf[sb_lbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' source begin sb_off %d\n",outbuf,sb_off);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[sb_off+1] = '^'; outbuf[sb_off+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(se_lbp->buffer,outbuf,se_lbp->byte_count);
+ outbuf[se_lbp->byte_count] = '\0';
+ if(outbuf[se_lbp->byte_count-1] == '\n'){
+ outbuf[se_lbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' source end se_off %d\n",outbuf,se_off);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[se_off+1] = '^'; outbuf[se_off+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+#endif /* DEBUG */
+
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(dbp->ismodified == NO){
+ if(dbp == curbuf){
+ screen_label_line(dbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ dbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * Since we are modifying the destination line buffer structure, we
+ * want to delete any format structures so that the screen display
+ * code will know to reformat it.
+ */
+ if(dlbp->format_line){
+ screen_free_format_lines(dlbp->format_line);
+ dlbp->format_line = NULL;
+ }/* End IF */
+/*
+ * The cases we have to handle are much more complex if there are newlines
+ * being inserted, so we determine now whether this is the case or not.
+ */
+ newline_included_in_src_data = 1;
+ if(sb_lbp->buffer[sb_lbp->byte_count-1] != '\n'){
+ newline_included_in_src_data = 0;
+ }/* End IF */
+
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "newline_included_in_src_data = %d\n",
+ newline_included_in_src_data
+ );
+#endif /* DEBUG */
+
+ bytes_to_copy_from_source = sb_lbp->byte_count - sb_off;
+ if(bytes_to_copy_from_source > length){
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "bytes_to_copy_from_source %d > length %d\n",
+ bytes_to_copy_from_source,
+ length
+ );
+#endif /* DEBUG */
+ bytes_to_copy_from_source = length;
+ newline_included_in_src_data = 0;
+ }/* End IF */
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "bytes_to_copy_from_source is now %d\n",
+ bytes_to_copy_from_source
+ );
+#endif /* DEBUG */
+
+/*
+ * Test for a simple case - one in which there are no newlines in the source
+ * data. In this case, we know that we simply have to insert it into the
+ * current line buffer. If the buffer area is not big enough, we may have
+ * to allocate a new buffer.
+ */
+ if(newline_included_in_src_data == 0){
+/*
+ * The total line length will be the current line length, plus the amount
+ * of data being inserted.
+ */
+ bytes_required_for_line = dlbp->byte_count + bytes_to_copy_from_source;
+
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "no newline, bytes_required_for_line %d \n",
+ bytes_required_for_line
+ );
+#endif /* DEBUG */
+
+/*
+ * If the current buffer is too small, we allocate a new buffer and copy
+ * all the data into it, and then free the old buffer.
+ */
+ if(bytes_required_for_line > dlbp->buffer_size){
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "new allocation required\n"
+ );
+#endif /* DEBUG */
+ bytes_to_allocate =
+ bytes_required_for_line + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ bytes_to_allocate -=
+ bytes_to_allocate % INCREMENTAL_LINE_BUFFER_SIZE;
+ if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
+ bytes_to_allocate = dlbp->buffer_size * 2;
+ }/* End IF */
+
+ if(bytes_to_allocate < bytes_required_for_line){
+ char tmp_message[1024];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo need %d alloced %d\n",
+ bytes_required_for_line,
+ bytes_to_allocate
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ new_buffer =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ bytes_to_allocate
+ );
+
+ if(new_buffer == NULL){
+ return(FAIL);
+ }/* End IF */
+
+ dcp = new_buffer;
+/*
+ * Copy up to the current insertion point in the new buffer, then the
+ * bytes coming from the source line, then if the source line didn't
+ * contribute a newline, the rest of the bytes coming from the original
+ * destination line.
+ */
+ movc3(dlbp->buffer,dcp,doff);
+ dcp += doff;
+ movc3(sb_lbp->buffer+sb_off,dcp,bytes_to_copy_from_source);
+ dcp += bytes_to_copy_from_source;
+ movc3(&dlbp->buffer[doff],dcp,dlbp->byte_count-doff);
+ bytes_inserted_so_far += bytes_to_copy_from_source;
+
+ tec_release(TYPE_C_LINEBUF,dlbp->buffer);
+ dlbp->buffer = new_buffer;
+ dlbp->buffer_size = bytes_to_allocate;
+ dlbp->byte_count = bytes_required_for_line;
+ }/* End IF */
+/*
+ * If the current buffer is large enough, make a hole to insert the new
+ * data into, and copy in the new data.
+ */
+ else {
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "it will fit into the current size of %d\n",
+ dlbp->buffer_size
+ );
+#endif /* DEBUG */
+ dcp = &dlbp->buffer[bytes_required_for_line-1];
+ scp = &dlbp->buffer[dlbp->byte_count-1];
+ for(i = 0; i < dlbp->byte_count - doff; i++){
+ *dcp-- = *scp--;
+ }/* End FOR */
+ movc3(
+ &sb_lbp->buffer[sb_off],
+ &dlbp->buffer[doff],
+ bytes_to_copy_from_source
+ );
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += bytes_to_copy_from_source;
+ }/* End Else */
+
+#ifdef DEBUG
+ doff += bytes_to_copy_from_source;
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n'){
+ outbuf[dlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new dest doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+#endif /* DEBUG */
+ }/* End IF */
+
+/*
+ * Else, if there is a newline involved, things get more complicated...
+ * The first thing we do is construct a line buffer which holds any
+ * trailing part of the source data (i.e. not terminated by a newline)
+ * along with the trailing part of the line we are inserting into.
+ */
+ else {
+ bytes_required_for_line = dlbp->byte_count - doff + se_off;
+ if(bytes_required_for_line > 0){
+ nlbp = allocate_line_buffer(bytes_required_for_line);
+ if(nlbp == NULL) return(FAIL);
+
+ nlbp->next_line = dlbp->next_line;
+ dlbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = dlbp;
+ movc3(se_lbp->buffer,nlbp->buffer,se_off);
+ movc3(
+ &dlbp->buffer[doff],
+ &nlbp->buffer[se_off],
+ dlbp->byte_count-doff
+ );
+ nlbp->byte_count = bytes_required_for_line;
+ dlbp->byte_count -= ( dlbp->byte_count - doff );
+ bytes_inserted_so_far += se_off;
+ }/* End IF */
+/*
+ * Now we want to merge the data from the begining of the original
+ * destinatio line, and the data up to the first newline from the
+ * source data.
+ */
+ bytes_required_for_line =
+ dlbp->byte_count + sb_lbp->byte_count - sb_off;
+ if(bytes_required_for_line > dlbp->buffer_size){
+ bytes_to_allocate =
+ bytes_required_for_line + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ bytes_to_allocate -=
+ bytes_to_allocate % INCREMENTAL_LINE_BUFFER_SIZE;
+ if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
+ bytes_to_allocate = dlbp->buffer_size * 2;
+ }/* End IF */
+
+ if(bytes_to_allocate < bytes_required_for_line){
+ char tmp_message[1024];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo needs %d alloced %d\n",
+ bytes_required_for_line,
+ bytes_to_allocate
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ new_buffer =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ bytes_to_allocate
+ );
+ if(new_buffer != NULL){
+ dcp = new_buffer;
+ movc3(dlbp->buffer,dcp,dlbp->byte_count);
+ dcp += dlbp->byte_count;
+ movc3(&sb_lbp->buffer[sb_off],dcp,sb_lbp->byte_count - sb_off);
+ tec_release(TYPE_C_LINEBUF,dlbp->buffer);
+ dlbp->buffer = new_buffer;
+ dlbp->buffer_size = bytes_to_allocate;
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += (sb_lbp->byte_count - sb_off);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ movc3(
+ &sb_lbp->buffer[sb_off],
+ &dlbp->buffer[doff],
+ sb_lbp->byte_count - sb_off
+ );
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += (sb_lbp->byte_count - sb_off);
+ }/* End Else */
+
+/*
+ * Now we want to copy all the full-length lines in between the starting
+ * and ending line buffers.
+ */
+ sb_lbp = sb_lbp->next_line;
+ while(sb_lbp){
+ if(sb_lbp == se_lbp) break;
+ bytes_required_for_line = sb_lbp->byte_count;
+ nlbp = allocate_line_buffer(bytes_required_for_line);
+ if(nlbp == NULL) break;
+ nlbp->next_line = dlbp->next_line;
+ dlbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = dlbp;
+ movc3(sb_lbp->buffer,nlbp->buffer,bytes_required_for_line);
+ nlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += bytes_required_for_line;
+ dlbp = nlbp;
+ sb_lbp = sb_lbp->next_line;
+ }/* End While */
+
+#ifdef DEBUG
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n'){
+ outbuf[dlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new dest doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(nlbp->buffer,outbuf,nlbp->byte_count);
+ outbuf[nlbp->byte_count] = '\0';
+ if(outbuf[nlbp->byte_count-1] == '\n'){
+ outbuf[nlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new line\n",outbuf);
+#endif /* DEBUG */
+ }/* End Else */
+
+
+ if(ct){
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)dbp;
+ ut->iarg1 = dest_position;
+ ut->iarg2 = bytes_inserted_so_far;
+ }/* End IF */
+
+/*
+ * Depending on whether or not the insert position precedes dot or not, we
+ * may have to increment dot.
+ */
+ if(dest_position <= dbp->dot) dbp->dot += bytes_inserted_so_far;
+ dbp->zee += bytes_inserted_so_far;
+/*
+ * If we are inserting characters before the position cache, it will
+ * shift our cached position down by the length, so we adjust the cache.
+ */
+ if(dest_position < dbp->pos_cache.base){
+ dbp->pos_cache.base += bytes_inserted_so_far;
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+/* BUFF_INSERT_WITH_UNDO - Insert a string and arrange for undo capability
+ *
+ * Function:
+ *
+ * This routine is called when characters need to be inserted, and
+ * also need to be un-inserted if the command is undone.
+ */
+int
+buff_insert_with_undo(ct,hbp,position,buffer,length)
+struct cmd_token *ct;
+struct buff_header *hbp;
+register int position;
+register char *buffer;
+register int length;
+{
+struct undo_token *ut;
+
+ PREAMBLE();
+
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)hbp;
+ ut->iarg1 = position;
+ ut->iarg2 = length;
+
+ if(buff_insert(hbp,position,buffer,length) == FAIL){
+ ut->iarg2 = 0;
+ return(FAIL);
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT_CHAR - Insert the specified character into the current position
+ *
+ * Function:
+ *
+ * This routine is called to insert the single character into the buffer
+ * at the current position (dot). It has to worry about such things as
+ * noting that the buffer has been modified, the screen display may be
+ * invalid, etc.
+ */
+int
+buff_insert_char(hbp,position,data)
+register struct buff_header *hbp;
+int position;
+char data;
+{
+register struct buff_line *lbp;
+struct buff_line *nlbp;
+register char *cp,*ocp;
+register int i,j;
+
+ PREAMBLE();
+
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_insert_char: bad position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * Determine which line the current insert point is on by calling the
+ * find routines.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+/*
+ * Depending on whether or not the specified position precedes dot or not, we
+ * may have to decrement dot.
+ */
+ if(position <= hbp->dot) hbp->dot += 1;
+ hbp->zee += 1;
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(hbp->ismodified == NO){
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ hbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are inserting a character before the position cache, it will
+ * shift our cached position down by one, so we adjust the cache.
+ */
+ if(position < hbp->pos_cache.base){
+ hbp->pos_cache.base += 1;
+ }/* End IF */
+/*
+ * If there is a format_line structure associated with this line, we make it
+ * go away so that the screen display routine will rebuild the display contents
+ * of this line.
+ */
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * If this is a newline, we do things a little differently because this will
+ * actually cause the line to be broken up. In this case we actually have to
+ * create a new line buffer structure.
+ */
+ if(data == '\n'){
+/*
+ * Calculate how many bytes are to move to the new line
+ */
+ j = lbp->byte_count - i;
+ if(j > 0){
+ nlbp = allocate_line_buffer(j);
+ if(nlbp == NULL) return(FAIL);
+ nlbp->next_line = lbp->next_line;
+ lbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = lbp;
+
+/*
+ * Now copy the data into the new line buffer
+ */
+ cp = lbp->buffer + i;
+ ocp = nlbp->buffer;
+
+ while(nlbp->byte_count < j){
+ *ocp++ = *cp++;
+ nlbp->byte_count++;
+ lbp->byte_count--;
+ }/* End While */
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * If this line buffer does not have room for the data, we need to expand it
+ */
+ if(lbp->byte_count == lbp->buffer_size){
+ ocp =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ lbp->buffer_size + INCREMENTAL_LINE_BUFFER_SIZE
+ );
+ if(ocp == NULL) return(FAIL);
+ movc3(lbp->buffer,ocp,lbp->buffer_size);
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ lbp->buffer = ocp;
+ lbp->buffer_size += INCREMENTAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+
+
+/*
+ * Make a hole in which to place the byte.
+ */
+ if(lbp->byte_count){
+ ocp = lbp->buffer + lbp->byte_count;
+ cp = ocp - 1;
+ j = lbp->byte_count - i;
+ while(j--){
+ *ocp-- = *cp--;
+ }/* End While */
+ }/* End IF */
+
+ cp = lbp->buffer + i;
+/*
+ * Ok, now insert the data into the hole
+ */
+ *cp = data;
+ lbp->byte_count += 1;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+/* BUFF_INSERT_CHAR_WITH_UNDO - Insert a single char with undo capability
+ *
+ * Function:
+ *
+ * This is an envelope routine which guarentees that the character
+ * to be input can be undone if necessary.
+ */
+int
+buff_insert_char_with_undo(ct,hbp,position,data)
+struct cmd_token *ct;
+register struct buff_header *hbp;
+int position;
+char data;
+{
+struct undo_token *ut;
+
+ PREAMBLE();
+
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)hbp;
+ ut->iarg1 = position;
+ ut->iarg2 = 1;
+
+ if(buff_insert_char(hbp,position,data) == FAIL){
+ ut->iarg2 = 0;
+ return(FAIL);
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE - Delete specified number of characters
+ *
+ * Function:
+ *
+ * This routine is called to delete the specified number of characters
+ * at the specified location in the buffer.
+ */
+void
+buff_delete(hbp,position,count)
+struct buff_header *hbp;
+int position;
+register int count;
+{
+
+ PREAMBLE();
+/*
+ * Insure that there are that many character in the buffer after the specified
+ * position.
+ */
+ if((position + count) > hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_delete: illegal range %d-%d %s Z = %d\n",
+ position,position+count,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * For now we are cheap and just repeatedly call the single character delete
+ * routine, because this is simple. It is also very expensive and will have to
+ * be fixed eventually.
+ */
+ while(count){
+ buff_delete_char(hbp,position);
+ count -= 1;
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE_CHAR - Delete the character at the current position
+ *
+ * Function:
+ *
+ * This routine is called to delete the single character which is at
+ * the current buffer position.
+ */
+int
+buff_delete_char(hbp,position)
+register struct buff_header *hbp;
+int position;
+{
+register struct buff_line *lbp;
+struct buff_line *nlbp;
+register char *cp,*ocp;
+register int i,j;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position >= hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_delete_char: bad position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+/*
+ * If there is a format_line structure associated with this line, we make it
+ * go away so that the screen display routine will rebuild the display contents
+ * of this line.
+ */
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+/*
+ * Get a pointer to the character that is about to be deleted
+ */
+ cp = lbp->buffer + i;
+/*
+ * Depending on whether or not the specified position precedes dot or not, we
+ * may have to decrement dot.
+ */
+ if(position < hbp->dot) hbp->dot -= 1;
+ hbp->zee -= 1;
+/*
+ * If this has modified the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are deleting a character before the position cache, it will
+ * shift our cached position up by one, so we adjust the cache.
+ */
+ if(position < hbp->pos_cache.base){
+ hbp->pos_cache.base -= 1;
+ }/* End IF */
+/*
+ * If this is not a newline, then things are pretty simple. Just
+ * make the character go away...
+ */
+ if(*cp != '\n'){
+ movc3(cp+1,cp,lbp->byte_count-i-1);
+ lbp->byte_count -= 1;
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If this is a newline, we do things a little differently because this
+ * actually causes the two lines to become concatenated (unless this is the
+ * final line in the buffer). If the buffer area is not big enough to hold
+ * both lines, we have to create a bigger area.
+ */
+ nlbp = lbp->next_line;
+ if(nlbp == NULL){
+ lbp->byte_count -= 1;
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If there is a format_line structure associated with the second line, we make
+ * it go away since this line is about to be trashed.
+ */
+ if(nlbp->format_line){
+ screen_free_format_lines(nlbp->format_line);
+ nlbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * Test whether or not the buffer is big enough to hold the data from both
+ * lines.
+ */
+ j = lbp->byte_count + nlbp->byte_count - 1;
+ if(lbp->buffer_size < j){
+ i = j + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ i -= i % INCREMENTAL_LINE_BUFFER_SIZE;
+ cp = tec_alloc(TYPE_C_LINEBUF,i);
+ if(cp == NULL) return(FAIL);
+ movc3(lbp->buffer,cp,lbp->buffer_size);
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ lbp->buffer = cp;
+ lbp->buffer_size = i;
+ }/* End IF */
+
+/*
+ * Ok, now concatenate the two line buffers by copying the second line
+ * into the first. Because we copy on top of the newline, it effectively
+ * goes away, so we have to decrement the line count by one.
+ */
+ lbp->byte_count -= 1;
+ cp = lbp->buffer + lbp->byte_count;
+ ocp = nlbp->buffer;
+ i = nlbp->byte_count;
+ while(i--){
+ *cp++ = *ocp++;
+ lbp->byte_count++;
+ }/* End While */
+
+/*
+ * Fixup the next and previous pointers so that they point around the
+ * line that we are about to remove.
+ */
+ lbp->next_line = nlbp->next_line;
+ if(nlbp->next_line) nlbp->next_line->prev_line = lbp;
+
+ if(hbp->pos_cache.lbp == nlbp) hbp->pos_cache.lbp = NULL;
+ buff_free_line_buffer(nlbp);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE_WITH_UNDO - Clobber buffer positions and build undo list
+ *
+ * Function:
+ *
+ * This routine is called when we want to delete a range of bytes in the
+ * buffer, and build an undo list so that we could replace them if we need
+ * to. This happens to be pretty efficient for a bulk delete since we can
+ * just move the line buffers over to the undo list.
+ */
+int
+buff_delete_with_undo(ct,hbp,position,count)
+struct cmd_token *ct;
+struct buff_header *hbp;
+int position;
+int count;
+{
+register char *cp;
+struct undo_token *ut;
+register struct buff_line *lbp;
+struct buff_line *top_lbp;
+int top_offset,local_count,bulk_position;
+int bytes_deleted_so_far = 0;
+
+ PREAMBLE();
+
+/* ???
+ * I think I may need this to insure that dot doesn't get changed. For
+ * instance, if we had <dot>abc and deleted 'a', we would have <dot>bc.
+ * Then if we undo, it's an insert of 'a' at position zero which causes
+ * dot to shift so we would now have a<dot>bc.
+ */
+ if(hbp->dot == (position+count)){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ }/* End IF */
+
+/*
+ * Because some of the undo functions only work in the 'current' buffer, if
+ * the user is deleting from a buffer other than the 'current' one, we have
+ * to set up some undo tokens to switch the current buffer back and forth
+ * during the undo process, should it occur.
+ */
+ if(hbp != curbuf){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = curbuf->buffer_number;
+ }/* End IF */
+
+ top_lbp = buff_find_line(hbp,position);
+ top_offset = buff_find_offset(hbp,top_lbp,position);
+
+/*
+ * First step will be to delete the (hopefully) large block of line buffer
+ * structures in the middle. This will leave a partial line at the top and
+ * also at the bottom. To start, if the begining is in the middle of a line,
+ * we need to skip over the bytes on that line.
+ */
+ local_count = count;
+ lbp = top_lbp;
+ bulk_position = position;
+ if(top_offset){
+ local_count -= lbp->byte_count - top_offset;
+ bulk_position = position + lbp->byte_count - top_offset;
+ lbp = lbp->next_line;
+ }/* End IF */
+ if(lbp) top_lbp = lbp->prev_line;
+
+/*
+ * Ok, now we want to chain over all the line buffer structures which can be
+ * removed in bulk. lbp gets left pointing at the final line which can be bulk
+ * removed.
+ */
+ if(lbp && lbp->byte_count <= local_count){
+/*
+ * We will get to bulk delete at least one line, so allocate an undo buffer.
+ * Point the undo token at the head of the list of line buffers. Remember
+ * the buffer position of the first byte of bulk data in iarg1, and keep a
+ * running count of the number of bytes of bulk data in iarg2.
+ */
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_BULK_INSERT;
+ ut->carg1 = (char *)lbp;
+ ut->iarg1 = bulk_position;
+ ut->iarg2 = 0;
+/*
+ * Now scan down until we reach the bottom of the bulk region. Keep track of
+ * how many characters this all represents, and clean up any associated format
+ * line structures.
+ */
+ while(1){
+ ut->iarg2 += lbp->byte_count;
+ local_count -= lbp->byte_count;
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+ if(lbp->next_line == NULL) break;
+ if(lbp->next_line->byte_count > local_count) break;
+ lbp = lbp->next_line;
+ }/* End While */
+/*
+ * Unchain the bulk area from the rest of the buffer. First, make the parent
+ * structure's forward pointer point to the next line buffer after the final
+ * one in the bulk deletion region.
+ */
+ if(top_lbp) top_lbp->next_line = lbp->next_line;
+ else hbp->first_line = lbp->next_line;
+ if(hbp->first_line == NULL) hbp->first_line = allocate_line_buffer(1);
+/*
+ * Now we have to fixup the backwards pointer of the trailing line buffer
+ * structure, if it exists.
+ */
+ if(lbp->next_line){
+ lbp->next_line->prev_line = top_lbp;
+ }/* End IF */
+/*
+ * Finally, clobber the 'next' pointer in lbp which is the end of the bulk
+ * area.
+ */
+ lbp->next_line = NULL;
+/*
+ * Update the remaining count of bytes to be deleted.
+ */
+ bytes_deleted_so_far += ut->iarg2;
+/*
+ * Depending on whether or not the bulk position precedes dot or not, we
+ * may have to decrement dot. In any case, we have to update zee.
+ */
+ if(bulk_position < hbp->dot){
+ if(hbp->dot > bulk_position + ut->iarg2) hbp->dot -= ut->iarg2;
+ else hbp->dot = bulk_position;
+ }/* End IF */
+ hbp->zee -= ut->iarg2;
+/*
+ * If this has modified the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are deleting from before the position cache, it will invalidate the
+ * information there, so we have to set it invalid so it does not get used.
+ */
+ if(hbp->pos_cache.base >= bulk_position){
+ hbp->pos_cache.lbp = NULL;
+ }/* End IF */
+
+ }/* End IF */
+
+/*
+ * Now, just brute force clobber the remaining few bytes
+ */
+ if(bytes_deleted_so_far < count){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_MEMFREE;
+ ut->carg1 = cp = tec_alloc(TYPE_C_CBUFF,count-bytes_deleted_so_far);
+ if(cp != NULL){
+/*
+ * Set up the undo token to put the text back into the q-register, and copy
+ * it into our save buffer as we delete it from the q-register.
+ */
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_INSERT;
+ ut->iarg1 = position;
+ ut->iarg2 = 0;
+ ut->carg1 = cp;
+ while(count > bytes_deleted_so_far){
+ *cp++ = buff_contents(hbp,position);
+ if(buff_delete_char(hbp,position) == FAIL) return(FAIL);
+ bytes_deleted_so_far += 1;
+ ut->iarg2 += 1;
+ }/* End While */
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Insure that the undo commands are working on the buffer the user specified
+ */
+ if(hbp != curbuf){
+ ut = allocate_undo_token(ct);
+ if(ut == FAIL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = hbp->buffer_number;
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_BULK_INSERT - Insert a list of line buffer structures
+ *
+ * Function:
+ *
+ * This routine is used to insert a list of line buffer structures at
+ * the specified position in the buffer.
+ */
+void
+buff_bulk_insert(hbp,position,count,lbp)
+struct buff_header *hbp;
+int position;
+int count;
+register struct buff_line *lbp;
+{
+register struct buff_line *olbp;
+int offset;
+
+ PREAMBLE();
+/*
+ * If this will modify the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * We special case position zero because this is an easy case, and is common
+ * when HK is being used a lot
+ */
+ if(position == 0){
+ if((olbp = hbp->first_line) != NULL){
+ if(olbp->byte_count == 0){
+ olbp = olbp->next_line;
+ buff_free_line_buffer(hbp->first_line);
+ }/* End IF */
+ }/* End IF */
+ hbp->first_line = lbp;
+ while(lbp->next_line) lbp = lbp->next_line;
+ lbp->next_line = olbp;
+ if(olbp) olbp->prev_line = lbp;
+ hbp->zee += count;
+ hbp->dot += count;
+ hbp->pos_cache.lbp = NULL;
+ return;
+ }/* End IF */
+
+/*
+ * Also special check for position zee since this is also an easy case
+ */
+ if(position == hbp->zee){
+ olbp = hbp->first_line;
+ if(hbp->pos_cache.lbp) olbp = hbp->pos_cache.lbp;
+ while(olbp->next_line) olbp = olbp->next_line;
+ if(olbp->byte_count == 0){
+ olbp = olbp->prev_line;
+ buff_free_line_buffer(olbp->next_line);
+ }/* End IF */
+ olbp->next_line = lbp;
+ lbp->prev_line = olbp;
+ if(hbp->dot == hbp->zee) hbp->dot += count;
+ hbp->zee += count;
+ hbp->pos_cache.lbp = NULL;
+ return;
+ }/* End IF */
+
+/*
+ * Ok, if the insertion is not at the begining or the end of the buffer, then
+ * we just have to do it the hard way...
+ */
+ olbp = buff_find_line(hbp,position);
+ offset = buff_find_offset(hbp,olbp,position);
+
+/*
+ * Bulk inserts should always be guarenteed aligned.
+ */
+ if(offset){
+ error_message("BULK_INSERT: internal error! non-aligned bulk insert");
+ return;
+ }/* End IF */
+
+ if(olbp->prev_line) olbp->prev_line->next_line = lbp;
+ lbp->prev_line = olbp->prev_line;
+
+ while(lbp->next_line) lbp = lbp->next_line;
+ lbp->next_line = olbp;
+ olbp->prev_line = lbp;
+
+ if(hbp->dot >= position) hbp->dot += count;
+ if(hbp->pos_cache.base >= position) hbp->pos_cache.base += count;
+ hbp->zee += count;
+
+ return;
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_LINE_BUFFER - Routine to allocate a line buffer structure
+ *
+ * Function:
+ *
+ * This routine is used to allocate a line buffer structure with
+ * a data buffer large enough to accommodate the specified number
+ * of bytes.
+ */
+struct buff_line *
+allocate_line_buffer(size)
+register int size;
+{
+register struct buff_line *lbp;
+
+ PREAMBLE();
+/*
+ * If he is asking for a size which will result in the minimum allocation, try
+ * to satisfy the request off of the lookaside list.
+ */
+ if(size <= INITIAL_LINE_BUFFER_SIZE){
+ if((lbp = line_buffer_lookaside_list) != NULL){
+ line_buffer_lookaside_list = lbp->next_line;
+ lbp->next_line = NULL;
+ lbp->prev_line = NULL;
+ lbp->format_line = NULL;
+ lbp->byte_count = 0;
+ lbp->lin_magic = MAGIC_LINE;
+ return(lbp);
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Lookaside list is empty, or he is asking for a non-standard size
+ */
+ lbp = (struct buff_line *)tec_alloc(TYPE_C_LINE,sizeof(struct buff_line));
+ if(lbp == NULL) return(NULL);
+
+ if(size <= INITIAL_LINE_BUFFER_SIZE){
+ lbp->buffer_size = INITIAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+ else if(size < INITIAL_LINE_BUFFER_SIZE + INCREMENTAL_LINE_BUFFER_SIZE){
+ lbp->buffer_size = INITIAL_LINE_BUFFER_SIZE +
+ INCREMENTAL_LINE_BUFFER_SIZE;
+ }
+ else {
+ lbp->buffer_size = size + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ lbp->buffer_size -= lbp->buffer_size % INCREMENTAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+
+ lbp->buffer = tec_alloc(TYPE_C_LINEBUF,lbp->buffer_size);
+ if(lbp->buffer == NULL){
+ tec_release(TYPE_C_LINE,(char *)lbp);
+ return(NULL);
+ }/* End IF */
+
+ lbp->byte_count = 0;
+ lbp->next_line = NULL;
+ lbp->prev_line = NULL;
+ lbp->format_line = NULL;
+ lbp->lin_magic = MAGIC_LINE;
+ return(lbp);
+
+}/* End Routine */
+
+
+/* BUFF_FREE_LINE_BUFFER - Free up the associated storage
+ *
+ * Function:
+ *
+ * This routine is called when a line buffer is deleted. It cleans up any
+ * associated storage as well as the buffer itself.
+ */
+void
+buff_free_line_buffer(lbp)
+register struct buff_line *lbp;
+{
+
+ PREAMBLE();
+
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * If this is a standard size line buffer, place it back on the lookaside list
+ * rather than giving it back to tec_alloc.
+ */
+ lbp->lin_magic = 0;
+
+ if(lbp->buffer_size == INITIAL_LINE_BUFFER_SIZE){
+ lbp->lin_magic = MAGIC_LINE_LOOKASIDE;
+ lbp->next_line = line_buffer_lookaside_list;
+ line_buffer_lookaside_list = lbp;
+ return;
+ }/* End IF */
+
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ tec_release(TYPE_C_LINE,(char *)lbp);
+
+}/* End Routine */
+
+/* BUFF_FREE_LINE_BUFFER_LIST - Release a whole list of line buffers
+ *
+ * Function:
+ *
+ * This routine will delete an entire list of line buffers
+ */
+void
+buff_free_line_buffer_list(lbp)
+register struct buff_line *lbp;
+{
+register struct buff_line *olbp;
+
+ PREAMBLE();
+
+ while((olbp = lbp)){
+ lbp = lbp->next_line;
+ buff_free_line_buffer(olbp);
+ }/* End While */
+
+}/* End Routine */
+
+/* BUFF_DEALLOCATE_LINE_BUFFER_LOOKASIDE_LIST - Free up lookaside list
+ *
+ * Function:
+ *
+ * This routine frees up any format_lines we've cached on our local
+ * lookaside list.
+ */
+void
+buff_deallocate_line_buffer_lookaside_list()
+{
+register struct buff_line *lbp;
+
+ PREAMBLE();
+
+ while((lbp = line_buffer_lookaside_list) != NULL){
+ line_buffer_lookaside_list = lbp->next_line;
+ lbp->lin_magic = 0;
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ tec_release(TYPE_C_LINE,(char *)lbp);
+ }/* End While */
+
+}/* End Routine */
+
+
+
+#ifdef DEBUG_DUMPING
+buff_dump_buffer(who_string,hbp,channel)
+char *who_string;
+register struct buff_header *hbp;
+FILE *channel;
+{
+register struct buff_line *lbp;
+register int i,j;
+
+ PREAMBLE();
+
+ fprintf(channel,"%s requested buffer dump:\n");
+
+ for(i = 0, lbp = hbp->first_line; lbp != NULL; i++,lbp = lbp->next_line){
+ fprintf(channel,"line %4d (count %4d)'",i,lbp->byte_count);
+ buff_dump_line(lbp,channel);
+ fprintf(channel,"'\n");
+ }/* End FOR */
+
+ fflush(debug_chan);
+
+}/* End Routine */
+
+buff_dump_line(lbp,channel)
+register struct buff_line *lbp;
+FILE *channel;
+{
+register struct buff_header *hbp = curbuf;
+register int i,j;
+
+ PREAMBLE();
+
+ for(j = 0; j < lbp->byte_count; j++){
+ if(lbp->buffer[j] == '\n'){
+ fprintf(channel,"<newline>");
+ }/* End IF */
+ else fprintf(channel,"%c",lbp->buffer[j]);
+ }/* End FOR */
+
+}/* End Routine */
+
+#endif
+
+unsigned int
+stringHash( char *str )
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++) & 0xFF )
+ {
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ }
+
+ return hash;
+}