char *tecbuf_c_version = "tecbuf.c: $Revision: 1.4 $";
struct buff_header *buff_create( char *name, char internal_flag );
/*
* $Date: 2007/12/26 13:28:30 $
* $Source: /cvsroot/videoteco/videoteco/tecbuf.c,v $
* $Revision: 1.4 $
* $Locker: $
*/
/**
* \file tecbuf.c
* \brief Subroutines to handle the edit buffers
*/
/*
* Copyright (C) 1985-2007 BY Paul Cantrell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "teco.h"
#include "tecparse.h"
static const 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;
/**
* \brief Find the named buffer
*
* 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( 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 and MS-DOS, it is case insensitive.
*/
for(cp1 = name, cp2 = bp->name;;cp1++,cp2++){
#if defined(VMS) || defined(MSDOS)
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 */
/**
* \brief Find the named Q register
*
* 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( 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 */
/**
* \brief Create a new buffer
*
* 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( 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);
memset(bp,0,sizeof(*bp));
MAGIC_UPDATE(bp, MAGIC_BUFFER);
bp->buf_hash = stringHash( name );
bp->name = tec_alloc(TYPE_C_CBUFF,strlen(name)+1);
if(bp->name == NULL){
MAGIC_UPDATE(bp, 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;
/*
* Create the first line buffer structure
*/
lbp = allocate_line_buffer(1);
if(lbp == NULL){
MAGIC_UPDATE(bp, 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;
/*
* 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 */
/**
* \brief Make a duplicate of a buffer
*
* 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( 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);
MAGIC_UPDATE(dbp, 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 */
/**
* \brief Copy the specified number of bytes
*
* This routine copies 'n' bytes from the source to the
* destination.
*/
void
movc3( char *source, char *dest, int count )
{
PREAMBLE();
while(count-- > 0) *dest++ = *source++;
}/* End Routine */
/**
* \brief Delete an existing buffer
*
* 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( 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 */
/**
* \brief Find the line structure that \a position is on
*
* This routine is called to find the \p line_buffer structure that
* the buffer position resides on.
*/
struct buff_line *
buff_find_line( struct buff_header *hbp, unsigned long position )
{
register struct buff_line *lbp;
register unsigned long 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 %lu specified in buffer %s, z=%lu",
position,hbp->name,hbp->zee);
tec_panic(panic_string);
printf("NULL ptr %lu 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 */
/**
* \brief Find offset onto line structure that \a position is on
*
* This routine is called to find the offset into the \p line_buffer of
* the specified position.
*/
int
buff_find_offset( struct buff_header *hbp,
struct buff_line *lbp, long 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 */
/**
* \brief Return the character at the specified position
*
* 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( struct buff_header *hbp, long position )
{
register struct buff_line *lbp;
register int i;
PREAMBLE();
/*
* Insure that the specified position is legal
*/
if((unsigned long)position > hbp->zee || position < 0){
char panic_string[LINE_BUFFER_SIZE];
sprintf(panic_string,
"buff_contents: illegal position %ld 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 */
/**
* \brief Return the character and positional information
*
* 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( struct buff_header *hbp,
long position, struct position_cache *cache )
{
register struct buff_line *lbp;
register int i;
PREAMBLE();
/*
* Insure that the specified position is legal
*/
if(position < 0 || (unsigned long)position > hbp->zee){
char tmp_message[LINE_BUFFER_SIZE];
sprintf(tmp_message,"buff_contents: illegal position %lu %s Z = %lu\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 */
/**
* \brief Routine to initialize the buffer routines
*
* 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 */
/**
* \brief Routine to read a file into a TECO edit buffer
*
* 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( 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 */
/**
* \brief Routine to open buffer number 'n'
*
* This routine is called with the number of a buffer which is to be
* made the 'current' edit buffer.
*/
int
buff_openbuffnum( 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 */
/**
* \brief Called by undo to re-create an edit buffer
*
* This routine is called to place a buffer back onto the buffer
* list.
*/
void
buff_reopenbuff( 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 */
/**
* \brief Routine to read a file into an existing TECO edit buffer
*
* This routine is called to read a file into the specified edit buffer.
* The edit buffer must already exist.
*/
int
buff_read( 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 */
/**
* \brief Reads the file descriptor into the specified buffer
*
* 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( 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;
unsigned long 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' || (unsigned)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 */
/**
* \brief Write the specified buffer out to a file
*
* This routine is generally called on an EW command when the user
* wishes to write out the contents of the buffer.
*/
int
buff_write( struct buff_header *hbp, int chan, unsigned long start, unsigned long 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)){
/* NOTE: status *should* be -1, errno *should* be set */
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){
/* NOTE: status *should* be -1, errno *should* be set */
return(FAIL);
}/* End IF */
}/* End IF */
return(SUCCESS);
}/* End Routine */
/**
* \brief Switch the current edit buffer
*
* This routine is called when we wish to switch the current buffer
* to some other edit buffer.
*/
int
buff_switch( struct buff_header *hbp, int map_flag )
{
char tmp_message[LINE_BUFFER_SIZE];
char *cp;
PREAMBLE();
curbuf = curwin->win_buffer = hbp;
sprintf(tmp_message," %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 */
/**
* \brief Build a map of the existing buffers.
*
* 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 const char *cp;
int max_length;
PREAMBLE();
buff_delete(curbuf,0,curbuf->zee);
for(i = 0; (unsigned)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);
snprintf(tmp_buffer,sizeof(tmp_buffer)," %s%s %s %6lu 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);
snprintf(tmp_buffer,sizeof(tmp_buffer)," %s%s %s %6lu 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));
(void) strcpy(tmp_buffer,"Munged by Robin Haberkorn");
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 */
/**
* \brief Insert a string at current position
*
* This routine is called to insert a string into the specified buffer
* at the buffer's current location.
*/
int
buff_insert( struct buff_header *hbp, unsigned long position, char *buffer, unsigned long 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);
memset(&fake_header,0,sizeof(fake_header));
fake_header.pos_cache.lbp = fake_line;
fake_header.first_line = fake_line;
memset(fake_line,0,sizeof(*fake_line));
fake_line->buffer_size = LINE_BUFFER_SIZE;
fake_line->buffer = buffer;
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 */
/**
* \brief Copy bytes from another buffer
*
* 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( struct cmd_token *ct,
struct buff_header *dbp,
unsigned long dest_position,
struct buff_header *sbp,
unsigned long src_position,
size_t length )
{
register unsigned 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;
size_t bytes_to_copy_from_source;
size_t bytes_required_for_line,bytes_to_allocate;
char *new_buffer;
register char *dcp,*scp;
size_t bytes_inserted_so_far = 0;
#ifdef DEBUG
char outbuf[1024];
term_goto(0,0);
term_clrtobot();
term_flush();
#endif /* DEBUG */
PREAMBLE();
/*
* Insure that the specified position is legal
*/
if(src_position > sbp->zee){
char tmp_message[LINE_BUFFER_SIZE];
sprintf(
tmp_message,
"buff_insert_from_buffer_with_undo: bad src pos %lu %s Z = %lu\n",
src_position,
sbp->name,sbp->zee
);
tec_panic(tmp_message);
}/* End IF */
if(dest_position > dbp->zee){
char tmp_message[LINE_BUFFER_SIZE];
sprintf(
tmp_message,
"buff_insert_from_buffer_with_undo: bad dest pos %lu %s Z = %lu\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 LARGEST_LOOKASIDE_BLOCK > 0
if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
bytes_to_allocate = dlbp->buffer_size * 2;
}/* End IF */
#endif
if(bytes_to_allocate < bytes_required_for_line){
char tmp_message[1024];
sprintf(
tmp_message,
"buff_insert_from_buffer_with_undo need %lu alloced %lu\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 LARGEST_LOOKASIDE_BLOCK > 0
if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
bytes_to_allocate = dlbp->buffer_size * 2;
}/* End IF */
#endif
if(bytes_to_allocate < bytes_required_for_line){
char tmp_message[1024];
sprintf(
tmp_message,
"buff_insert_from_buffer_with_undo needs %lu alloced %lu\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 */
/**
* \brief Insert a string and arrange for undo capability
*
* 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( struct cmd_token *ct,
struct buff_header *hbp,
unsigned long position,
char *buffer,
unsigned long 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 */
/**
* \brief Insert the specified character into the current position
*
* 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( struct buff_header *hbp,
unsigned long 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 %lu %s Z = %lu\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 < (unsigned int)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 */
/**
* \brief Insert a single char with undo capability
*
* This is an envelope routine which guarentees that the character
* to be input can be undone if necessary.
*/
int
buff_insert_char_with_undo( struct cmd_token *ct,
struct buff_header *hbp,
unsigned long 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 */
/**
* \brief Delete specified number of characters
*
* This routine is called to delete the specified number of characters
* at the specified location in the buffer.
*/
void
buff_delete( struct buff_header *hbp,
unsigned long position,
unsigned long 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 %lu-%lu %s Z = %lu\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 */
/**
* \brief Delete the character at the current position
*
* This routine is called to delete the single character which is at
* the current buffer position.
*/
int
buff_delete_char( struct buff_header *hbp,
unsigned long position )
{
register struct buff_line *lbp;
struct buff_line *nlbp;
register char *cp,*ocp;
register int i;
register unsigned int 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 %lu %s Z = %lu\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 */
/**
* \brief Clobber buffer positions and build undo list
*
* 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( struct cmd_token *ct,
struct buff_header *hbp,
unsigned long position,
unsigned long count )
{
register char *cp;
struct undo_token *ut;
register struct buff_line *lbp;
struct buff_line *top_lbp;
unsigned long top_offset,bulk_position;
long local_count;
unsigned long 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 abc and deleted 'a', we would have bc.
* Then if we undo, it's an insert of 'a' at position zero which causes
* dot to shift so we would now have abc.
*/
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 && (ssize_t)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((ssize_t)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 */
/**
* \brief Insert a list of line buffer structures
*
* This routine is used to insert a list of line buffer structures at
* the specified position in the buffer.
*/
void
buff_bulk_insert( struct buff_header *hbp,
unsigned long position,
long count,
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 */
/**
* \brief Routine to allocate a line buffer structure
*
* 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( 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;
MAGIC_UPDATE(lbp, 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);
memset(lbp,0,sizeof(*lbp));
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 */
MAGIC_UPDATE(lbp, MAGIC_LINE);
return(lbp);
}/* End Routine */
/**
* \brief Free up the associated storage
*
* 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( 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.
*/
MAGIC_UPDATE(lbp, 0);
if(lbp->buffer_size == INITIAL_LINE_BUFFER_SIZE){
MAGIC_UPDATE(lbp, 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 */
/**
* \brief Release a whole list of line buffers
*
* This routine will delete an entire list of line buffers
*/
void
buff_free_line_buffer_list( 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 */
/**
* \brief Free up lookaside list
*
* 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;
MAGIC_UPDATE(lbp, 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,"");
}/* 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;
}