home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware 1 2 the Maxx
/
sw_1.zip
/
sw_1
/
EDITORS
/
TDE20.ZIP
/
ED.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-06-05
|
67KB
|
2,196 lines
/******************* start of original comments ********************/
/*
* Written by Douglas Thomson (1989/1990)
*
* This source code is released into the public domain.
*/
/*
* Name: dte - Doug's Text Editor program - main editor module
* Purpose: This file contains the main editor module, and a number of the
* smaller miscellaneous editing commands.
* It also contains the code for dispatching commands.
* File: ed.c
* Author: Douglas Thomson
* System: this file is intended to be system-independent
* Date: October 1, 1989
* I/O: file being edited
* files read or written
* user commands and prompts
* Notes: see the file "dte.doc" for general program documentation
*/
/********************* end of original comments ********************/
/*
* The basic editor routines have been EXTENSIVELY rewritten. I have added
* support for lines longer than 80 columns and I have added line number
* support. I like to know the real line number that editor functions are
* working on and I like to know the total number of lines in a file.
*
* I rewrote the big series of ifs in the dispatch subroutine. It is now
* an array of pointers to functions. We know what function to call as soon
* as a key is pressed. It is also makes it easier to implement a configuration
* utility and macros.
*
* I added a few functions that I use quite often and I deleted a few that I
* rarely use. Added are Split Line, Join Line, and Duplicate Line. Deleted
* are Goto Marker 0-9 (others?).
*
* ************ In tde 1.3, I put Goto Marker 0-9 back in. ***************
*
* I felt that the insert routine should be separated into two routines. One
* for inserting the various combinations of newlines and one for inserting
* 'regular' text characters, ASCII and extended ASCII characters.
*
* One of Doug's design considerations was keeping screen updates to a minimum.
* I have expanded upon that idea and added support for updating windows
* LOCALly, GLOBALly, or NOT_LOCALly. For example, scrolling in one window
* does not affect the text in another window - LOCAL update. Adding, deleting,
* or modifying text in one window may affect text in other windows - GLOBAL
* update. Sometimes, updates to the current window are handled in the task
* routines so updates to other windows are done NOT_LOCALly.
*
* Also note that using functions copy_line and un_copy_line to change a line
* automatically adjusts the g_status.end_mem pointer. If a function bypasses
* those functions, adjusting the g_status.end_mem pointer must be done
* explicitly.
*
* New editor name: tde, the Thomson-Davis Editor.
* Author: Frank Davis
* Date: June 5, 1991, version 1.0
* Date: July 29, 1991, version 1.1
* Date: October 5, 1991, version 1.2
* Date: January 20, 1992, version 1.3
* Date: February 17, 1992, version 1.4
* Date: April 1, 1992, version 1.5
* Date: June 5, 1992, version 2.0
*
* This modification of Douglas Thomson's code is released into the
* public domain, Frank Davis. You may distribute it freely.
*/
#include "tdestr.h" /* typedefs for global variables */
#include "define.h" /* editor function defs */
#include "tdefunc.h" /* prototypes for all functions in tde */
#include "global.h" /* global variables */
#include "prompts.h" /* prompt assignments */
#include "default.h" /* default function key assignments */
/*
* Name: tab_key
* Purpose: To make the necessary changes after the user types the tab key.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If in insert mode, then this function adds the required
* number of spaces in the file.
* If not in insert mode, then tab simply moves the cursor right
* the required distance.
*/
int tab_key( WINDOW *window )
{
int spaces; /* the spaces to move to the next tab stop */
char *source; /* source for block move to make room for c */
char *dest; /* destination for block move */
int pad, len;
register int rcol;
int old_bcol;
register WINDOW *win; /* put window pointer in a register */
int rc;
win = window;
if (*win->cursor == CONTROL_Z)
return( OK );
rcol = win->rcol;
old_bcol = win->bcol;
show_ruler_char( win );
/*
* work out the number of spaces to the next tab stop
*/
if (mode.smart_tab)
spaces = next_smart_tab( win );
else
spaces = mode.tab_size - (rcol % mode.tab_size);
if (mode.insert && rcol + spaces < g_display.line_length) {
copy_line( win->cursor, win->bottom_line );
/*
* work out how many characters need to be inserted
*/
len = linelen( g_status.line_buff );
pad = rcol > len ? rcol - len : 0;
if (g_status.line_buff[len] == CONTROL_Z)
++pad;
if (len + pad + spaces >= g_display.line_length) {
/*
* line too long to add
*/
error( WARNING, win->bottom_line, ed1 );
rc = ERROR;
} else {
if (pad > 0 || spaces > 0) {
if (g_status.line_buff[len] == CONTROL_Z) {
g_status.line_buff[len] = '\n';
g_status.line_buff[len+1] = CONTROL_Z;
++win->file_info->length;
show_size( window );
--pad;
++len;
}
source = g_status.line_buff + rcol - pad;
dest = source + pad + spaces;
memmove( dest, source, len+pad-rcol+2 );
/*
* if padding was required, then put in the required spaces
*/
memset( source, ' ', pad + spaces );
}
win->file_info->dirty = GLOBAL;
show_changed_line( win );
rcol += spaces;
win->ccol += spaces;
rc = OK;
}
} else if (rcol + spaces <= g_display.line_length) {
/*
* advance the cursor without changing the text underneath
*/
rcol += spaces;
win->ccol += spaces;
rc = OK;
}
check_virtual_col( win, rcol, win->ccol );
if (old_bcol != win->bcol) {
make_ruler( win );
show_ruler( win );
}
return( rc );
}
/*
* Name: backtab
* Purpose: To make the necessary changes after the user presses the backtab.
* Date: November 1, 1991
* Passed: window: pointer to current window
* Notes: If in insert mode, then this function subs the required
* number of spaces in the file.
* If not in insert mode, then tab simply moves the cursor left
* the required distance.
*/
int backtab( WINDOW *window )
{
int spaces; /* the spaces to move to the next tab stop */
char *source; /* source for block move to make room for c */
char *dest; /* destination for block move */
int pad, len;
register int rcol;
int old_bcol;
register WINDOW *win; /* put window pointer in a register */
win = window;
rcol = win->rcol;
if (*win->cursor == CONTROL_Z || win->rcol == 0)
return( OK );
old_bcol = win->bcol;
show_ruler_char( win );
/*
* work out the number of spaces to the previous tab stop
*/
if (mode.smart_tab)
spaces = prev_smart_tab( win );
else
spaces = win->rcol % mode.tab_size;
if (spaces == 0)
spaces = mode.tab_size;
copy_line( win->cursor, win->bottom_line );
len = linelen( g_status.line_buff );
if (mode.insert && rcol - spaces < len) {
pad = rcol > len ? rcol - len : 0;
if (g_status.line_buff[len] == CONTROL_Z)
++pad;
if (pad > 0 || spaces > 0) {
if (g_status.line_buff[len] == CONTROL_Z) {
g_status.line_buff[len] = '\n';
g_status.line_buff[len+1] = CONTROL_Z;
++win->file_info->length;
show_size( win );
--pad;
++len;
}
/*
* if padding was required, then put in the required spaces
*/
if (pad > 0) {
source = g_status.line_buff + rcol - pad;
dest = source + pad;
memmove( dest, source, pad+2 );
memset( source, ' ', pad );
}
source = g_status.line_buff + rcol;
dest = source - spaces;
memmove( dest, source, len+pad-rcol+2 );
}
win->file_info->dirty = GLOBAL;
show_changed_line( win );
rcol -= spaces;
win->ccol -= spaces;
} else {
/*
* move the cursor without changing the text underneath
*/
rcol -= spaces;
if (rcol < 0)
rcol = 0;
win->ccol -= spaces;
}
check_virtual_col( win, rcol, win->ccol );
if (old_bcol != win->bcol) {
make_ruler( win );
show_ruler( win );
}
return( OK );
}
/*
* Name: next_smart_tab
* Purpose: To find next smart tab
* Date: June 5, 1992
* Passed: window: pointer to the current window
* Notes: To find a smart tab 1) find the first non-blank line above the
* current line, 2) find the first non-blank character after
* column of the cursor.
*/
int next_smart_tab( WINDOW *window )
{
register int spaces; /* the spaces to move to the next tab stop */
text_ptr s; /* pointer to text */
/*
* find first previous non-blank line above the cursor.
*/
s = find_prev( cpb( window->cursor ) );
while (s != NULL && is_line_blank( s ) )
s = find_prev( s );
if (s != NULL) {
/*
* if cursor is past the eol of the smart line, lets find the
* next fixed tab.
*/
if ((unsigned)window->rcol >= linelen( s ))
spaces = mode.tab_size - (window->rcol % mode.tab_size);
else {
spaces = 0;
s = cpf( s ) + window->rcol;
/*
* if we are on a word, find the end of it.
*/
while (*s != ' ' && *s != '\n') {
++s;
++spaces;
}
/*
* now find the start of the next word.
*/
if (*s != '\n')
while (*s == ' ') {
++s;
++spaces;
}
}
} else
spaces = mode.tab_size - (window->rcol % mode.tab_size);
return( spaces );
}
/*
* Name: prev_smart_tab
* Purpose: To find previous smart tab
* Date: June 5, 1992
* Passed: window: pointer to the current window
* Notes: To find a smart tab 1) find the first non-blank line above the
* current line, 2) find the first non-blank character before
* column of the cursor.
* there are several cases to consider: 1) the cursor is past the
* the end of the smart line, 2) the smart pointer is in the
* middle of a word, 3) there are no more words between the
* smart pointer and the beginning of the line.
*/
int prev_smart_tab( WINDOW *window )
{
register int spaces; /* the spaces to move to the next tab stop */
text_ptr s; /* pointer to text */
unsigned int len;
int i;
/*
* find first previous non-blank line above the cursor, if it exists.
*/
s = find_prev( cpb( window->cursor ) );
while (s != NULL && is_line_blank( s ) )
s = find_prev( s );
if (s != NULL) {
/*
* if there are no words between the cursor and column 1 of the
* smart tab line, find previous fixed tab.
*/
if (window->rcol < first_non_blank( s ))
spaces = window->rcol % mode.tab_size;
else {
s = cpf( s );
len = linelen( s );
/*
* now, we need to figure the initial pointer and space.
* if the cursor is past the eol of the smart line, then
* set the smart pointer "s" to the end of line and "spaces" to
* the number of characters between the cursor and the eol
* of the smart line. otherwise, set the smart pointer "s" to
* the column of the cursor and "spaces" to 0.
*/
if (len < (unsigned)window->rcol) {
s += len;
spaces = window->rcol - len;
} else {
s += window->rcol;
spaces = 0;
}
s = cpb( s );
/*
* if the cursor is past the eol of the smart line, find the start
* of the word that is at the eol.
*/
if ((unsigned)window->rcol >= len) {
/*
* skip any space that has not been trimmed from the eol.
*/
while (*(s-1) == ' ') {
--s;
++spaces;
}
/*
* now find the beginning of the first word at eol.
*/
while (*(s-1) != ' ') {
--s;
++spaces;
}
} else {
/*
* initially, lets keep a count of the number of non-spaces
* that we skip. if the pointer happens to be in the middle
* of a word, then lets find the begining of the word.
*/
i = 0;
while (*s != ' ' && *s != '\n' && *s != CONTROL_Z) {
--s;
++spaces;
i++;
}
if (i > 1)
/*
* in the above while loop, we went one character too far when
* we found the beginning of the word under the pointer.
* if "i" is greater than 1, then the smart pointer "s"
* was somewhere in the middle of a word. so, lets return
* the number of spaces needed to reach the beginning of
* the word from the current cursor position.
*/
--spaces;
else {
/*
* if we get this far, we have just found the first character
* of the first word of the smart pointer "s". now, lets
* skip the spaces between the word we just found and the
* next previous word.
* we may run into '\n' or ^Z if there are no previous words
* on the line.
*/
while (*s == ' ') {
--s;
++spaces;
}
/*
* now, lets find the start of the word we just found.
*/
while (*s != ' ' && *(s-1) != ' ' && *s != '\n' &&
*s != CONTROL_Z) {
--s;
++spaces;
}
/*
* if *s == '\n' or *s == CONTROL_Z, then there were no
* previous words. lets find the previous fixed tab.
*/
if (*s == '\n' || *s == CONTROL_Z)
spaces = window->rcol % mode.tab_size;
}
}
if (spaces > window->rcol)
spaces = window->rcol;
}
} else
spaces = window->rcol % mode.tab_size;
/*
* spaces cannot be negative.
*/
if (spaces < 0)
spaces = 0;
return( spaces );
}
/*
* Name: insert_newline
* Purpose: insert a newline
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: There a several ways to insert a line into a file: 1) pressing
* a key, 2) word wrap, 3) any others?
* When doing word wrap or format paragraph, don't show any changes.
* Wait until the function finishes then show all changes at once.
*/
int insert_newline( WINDOW *window )
{
char *source; /* source for block move to make room for c */
char *dest; /* destination for block move */
int len; /* length of current line */
int add; /* characters to be added (usually 1 in insert mode) */
int rcol;
text_ptr prev; /* previous lines scanned for autoindent */
long length;
int carriage_return;
int split_line;
int wordwrap;
int dirty;
int old_bcol;
register WINDOW *win; /* put window pointer in a register */
win = window;
length = win->file_info->length;
if (win->rline > length && *win->cursor != CONTROL_Z)
return( OK );
wordwrap = mode.word_wrap;
switch (g_status.command) {
case WordWrap:
wordwrap = mode.word_wrap;
carriage_return = TRUE;
split_line = FALSE;
break;
case Rturn :
show_ruler_char( win );
carriage_return = TRUE;
split_line = FALSE;
break;
case AddLine :
split_line = carriage_return = FALSE;
break;
case SplitLine :
split_line = carriage_return = TRUE;
break;
}
/*
* make window temporarily invisible to the un_copy_line function
*/
win->visible = FALSE;
win->cursor = cpf( win->cursor );
copy_line( win->cursor, win->bottom_line );
len = linelen( g_status.line_buff );
source = g_status.line_buff + len;
if (carriage_return || split_line) {
if (win->rcol < len)
source = g_status.line_buff + win->rcol;
}
/*
* make room for '\n' just after source (source+1)
*/
memmove( source+1, source, linelen( source )+2 );
*source = '\n';
un_copy_line( win->cursor, win, TRUE );
adjust_windows_cursor( win, 1 );
++win->file_info->length;
win->file_info->dirty = NOT_LOCAL;
if (length == 0l || wordwrap || win->cline == win->bottom_line)
win->file_info->dirty = GLOBAL;
else if (!split_line)
update_line( win );
/*
* If the cursor is to move down to the next line, then update
* the line and column appropriately.
*/
if (carriage_return || split_line) {
dirty = win->file_info->dirty;
prev = win->cursor;
win->cursor = find_next( win->cursor );
if (win->cline < win->bottom_line)
win->cline++;
win->rline++;
rcol = win->rcol;
old_bcol = win->bcol;
/*
* indentation is only required if we are in the right mode,
* the user typed <CR>, and if there is not space followed
* by something after the cursor.
*/
if (mode.indent || wordwrap) {
/*
* autoindentation is required. Match the indentation of
* the first line above that is not blank.
*/
add = find_left_margin( prev, wordwrap );
copy_line( win->cursor, win->bottom_line );
len = linelen( g_status.line_buff );
source = g_status.line_buff;
if (len + add > MAX_LINE_LENGTH)
add = MAX_LINE_LENGTH - len;
dest = source + add;
memmove( dest, source, len+2 );
/*
* now put in the autoindent characters
*/
memset( source, ' ', add );
win->rcol = add;
un_copy_line( win->cursor, win, TRUE );
} else
win->rcol = 0;
if (split_line) {
win->cursor = cpb( win->cursor );
win->cursor = find_prev( win->cursor );
if (win->cline > win->top_line + window->ruler)
win->cline--;
win->rline--;
win->rcol = rcol;
}
check_virtual_col( win, win->rcol, win->ccol );
if (dirty == GLOBAL || win->file_info->dirty == LOCAL || wordwrap)
win->file_info->dirty = GLOBAL;
else
win->file_info->dirty = dirty;
}
/*
* record that file has been modified
*/
if (win->file_info->dirty != GLOBAL)
my_scroll_down( win );
restore_marked_block( win, 1 );
show_size( win );
win->visible = TRUE;
if (old_bcol != win->bcol) {
make_ruler( win );
show_ruler( win );
}
return( OK );
}
/*
* Name: insert_overwrite
* Purpose: To make the necessary changes after the user has typed a normal
* printable character
* Date: June 5, 1991
* Passed: window: pointer to current window
*/
int insert_overwrite( WINDOW *window )
{
char *source; /* source for block move to make room for c */
char *dest; /* destination for block move */
int len; /* length of current line */
int pad; /* padding to add if cursor beyond end of line */
int add; /* characters to be added (usually 1 in insert mode) */
register int rcol;
register WINDOW *win; /* put window pointer in a register */
int rc;
win = window;
if (*win->cursor == CONTROL_Z || g_status.key_pressed >= 256)
rc = OK;
else {
rcol = win->rcol;
/*
* first check we have room - the editor can not
* cope with lines wider than g_display.line_length
*/
if (rcol >= g_display.line_length) {
/*
* cannot insert more characters
*/
error( WARNING, win->bottom_line, ed2 );
rc = ERROR;
} else {
copy_line( win->cursor, win->bottom_line );
/*
* work out how many characters need to be inserted
*/
len = linelen( g_status.line_buff );
pad = rcol > len ? rcol - len : 0;
/*
* if this is the last line in a file, the last character in the
* line buffer will be CONTROL_Z. increment pad and insert a \n.
*/
if (g_status.line_buff[len] == CONTROL_Z)
++pad;
if (mode.insert || rcol >= len)
/*
* inserted characters, or overwritten characters at the end of
* the line, are inserted.
*/
add = 1;
else
/*
* and no extra space is required to overwrite existing characters
*/
add = 0;
/*
* check that current line would not get too long.
*/
if (len + pad + add >= g_display.line_length) {
/*
* no more room to add
*/
error( WARNING, win->bottom_line, ed3 );
rc = ERROR;
} else {
/*
* make room for whatever needs to be inserted
*/
if (pad > 0 || add > 0) {
source = g_status.line_buff + len;
if (*source == CONTROL_Z) {
if (rcol > len)
source = g_status.line_buff + rcol + 1;
*source++ = '\n';
*source = CONTROL_Z;
++win->file_info->length;
show_size( win );
--pad;
++len;
}
source = g_status.line_buff + rcol - pad;
dest = source + pad + add;
memmove( dest, source, len + pad - rcol + 2 );
/*
* put in the required padding
*/
memset( source, ' ', pad );
}
g_status.line_buff[rcol] = (char)g_status.key_pressed;
/*
* always increment the real column (rcol) then adjust the
* logical and base column as needed. show the changed line
* in all but the LOCAL window. In the LOCAL window, there are
* two cases: 1) update the line, or 2) redraw the window if
* cursor goes too far right.
*/
win->file_info->dirty = NOT_LOCAL;
show_changed_line( win );
if (win->ccol < win->end_col) {
show_curl_line( win );
show_ruler_char( win );
win->ccol++;
} else {
win->bcol++;
win->file_info->dirty = LOCAL;
make_ruler( win );
show_ruler( win );
}
rcol++;
}
/*
* record that file has been modified and adjust cursors,
* file start and end pointers as needed.
*/
check_virtual_col( win, rcol, win->ccol );
win->file_info->modified = TRUE;
if (mode.word_wrap) {
g_status.command = WordWrap;
word_wrap( win );
}
rc = OK;
}
}
return( rc );
}
/*
* Name: join_line
* Purpose: To join current line and line below at cursor
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: trunc the line then join with line below if it exists
*/
int join_line( WINDOW *window )
{
register int len; /* length of current line */
char *source; /* source for block move to delete word */
char *dest; /* destination for block move */
text_ptr p; /* next line in file */
int pad, i; /* padding spaces required */
int cr; /* does current line end with carriage return? */
register WINDOW *win; /* put window pointer in a register */
int rc;
win = window;
if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
return( OK );
win->cursor = cpf( win->cursor );
load_undo_buffer( win->cursor );
copy_line( win->cursor, win->bottom_line );
if (win->rcol < (len = linelen( g_status.line_buff ))) {
/*
* delete rest of line
*/
dest = g_status.line_buff + win->rcol;
if (g_status.line_buff[len] == '\n')
*dest++ = '\n';
*dest = CONTROL_Z;
un_copy_line( win->cursor, win, FALSE );
win->file_info->dirty = GLOBAL;
}
rc = OK;
/*
* we need to combine with the next line, if any
*/
if ((p = find_next( win->cursor )) != NULL && *p != CONTROL_Z) {
/*
* add padding if required
*/
len = linelen( g_status.line_buff );
cr = g_status.line_buff[len] == '\n' ? 1 : 0;
pad = win->rcol > len ? win->rcol - len : 0;
/*
* check room to combine lines
*/
if (len + pad + cr + linelen( p ) >= g_display.line_length) {
/*
* cannot combine lines.
*/
error( WARNING, win->bottom_line, ed4 );
rc = ERROR;
} else {
/*
* do the move
*/
source = g_status.line_buff + win->rcol - pad;
dest = source + pad;
memmove( dest, source, len + pad - win->rcol + 1 + cr );
/*
* insert the padding
*/
while (pad--)
*source++ = ' ';
/*
* remove the \n separating the two lines.
*/
i = 0;
if (*source == '\n') {
*source = CONTROL_Z;
i = -1;
}
g_status.copied = TRUE;
un_copy_line( win->cursor, win, FALSE );
adjust_windows_cursor( win, i );
--win->file_info->length;
restore_marked_block( win, -1 );
show_size( win );
win->file_info->dirty = GLOBAL;
}
}
return( rc );
}
/*
* Name: word_delete
* Purpose: To delete from the cursor to the start of the next word.
* Date: September 1, 1991
* Passed: window: pointer to current window
* Notes: If the cursor is at the right of the line, then combine the
* current line with the next one, leaving the cursor where it
* is.
* If the cursor is on an alphanumeric character, then all
* subsequent alphanumeric characters are deleted.
* If the cursor is on a space, then all subsequent spaces
* are deleted.
* If the cursor is on a punctuation character, then all
* subsequent punctuation characters are deleted.
*/
int word_delete( WINDOW *window )
{
int len; /* length of current line */
register int start; /* column that next word starts in */
char *source; /* source for block move to delete word */
char *dest; /* destination for block move */
int alpha; /* is the cursor char alphanumeric? */
text_ptr p;
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
return( OK );
win->cursor = cpf( win->cursor );
copy_line( win->cursor, win->bottom_line );
if (win->rcol >= (len = linelen( g_status.line_buff ))) {
join_line( win );
p = win->cursor + win->rcol;
if (*p != CONTROL_Z)
load_undo_buffer( p );
} else {
/*
* normal word delete
*
* find the start of the next word
*/
start = win->rcol;
if (g_status.line_buff[start] == ' ') {
/*
* the cursor was on a space, so eat all consecutive spaces
* from the cursor onwards.
*/
while (g_status.line_buff[start] == ' ')
++start;
} else {
/*
* eat all consecutive characters in the same class (spaces
* are considered to be in the same class as the cursor
* character)
*/
alpha = myisalnum( g_status.line_buff[start++] );
while (start < len) {
if (g_status.line_buff[start] == ' ')
/*
* the next character that is not a space will
* end the delete
*/
alpha = -1;
else if (alpha != myisalnum( g_status.line_buff[start] )) {
if (g_status.line_buff[start] != ' ')
break;
}
++start;
}
}
/*
* move text to delete word
*/
source = g_status.line_buff + start;
dest = g_status.line_buff + win->rcol;
memmove( dest, source, len-start+2 );
win->file_info->modified = TRUE;
win->file_info->dirty = GLOBAL;
if (g_status.command == WordDelete)
show_changed_line( win );
}
return( OK );
}
/*
* Name: dup_line
* Purpose: Duplicate current line
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: cursor stays on current line
*/
int dup_line( WINDOW *window )
{
register int len; /* length of current line */
long number;
text_ptr d, s;
register WINDOW *win; /* put window pointer in a register */
int rc;
win = window;
if (win->rline > win->file_info->length)
return( ERROR );
win->cursor = cpf( win->cursor );
un_copy_line( win->cursor, win, TRUE );
/*
* don't dup the ^Z or a NULL line
*/
if (*win->cursor != CONTROL_Z && (d=find_next( win->cursor )) != NULL) {
/*
* don't use buffers to dup the line. use hw_move to make space and
* copy current line at same time. d is set to beginning of next line.
*/
s = win->cursor;
len = linelen( s );
if (s[len] == '\n')
++len;
number = ptoul( g_status.end_mem ) - ptoul( s );
hw_move( d, s, number );
++win->file_info->length;
g_status.end_mem = addltop( len, g_status.end_mem );
adjust_start_end( win->file_info, len );
addorsub_all_cursors( win, len );
adjust_windows_cursor( win, 1 );
/*
* if current line is the bottom line, we can't see the dup line because
* cursor doesn't move and dup line is added after current line.
*/
if (win->cline != win->bottom_line)
my_scroll_down( win );
win->file_info->dirty = NOT_LOCAL;
/*
* record that file has been modified
*/
win->file_info->modified = TRUE;
restore_marked_block( win, 1 );
show_size( win );
show_avail_mem( );
rc = OK;
} else {
/*
* cannot duplicate line
*/
error( WARNING, win->bottom_line, ed5 );
rc = ERROR;
}
return( rc );
}
/*
* Name: back_space
* Purpose: To delete the character to the left of the cursor.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If the cursor is at the left of the line, then combine the
* current line with the previous one.
* If in indent mode, and the cursor is on the first non-blank
* character of the line, then match the indentation of an
* earlier line.
*/
int back_space( WINDOW *window )
{
int len; /* length of the current line */
char *source; /* source of block move to delete character */
char *dest; /* destination of block move */
text_ptr p; /* previous line in file */
int plen; /* length of previous line */
int del_count; /* number of characters to delete */
int pos; /* the position of the first non-blank char */
register int rcol;
int ccol;
int old_bcol;
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
return( OK );
win->cursor = cpf( win->cursor );
copy_line( win->cursor, win->bottom_line );
len = linelen( g_status.line_buff );
rcol = win->rcol;
ccol = win->ccol;
old_bcol = win->bcol;
if (rcol == 0) {
/*
* combine this line with the previous, if any
*/
win->cursor = cpb( win->cursor );
if ((p = find_prev( win->cursor )) != NULL) {
if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
/*
* cannot combine lines
*/
error( WARNING, win->bottom_line, ed4 );
return( ERROR );
}
un_copy_line( win->cursor, win, TRUE );
copy_line( p, win->bottom_line );
load_undo_buffer( p );
g_status.line_buff[plen] = CONTROL_Z;
win->cursor = p;
un_copy_line( win->cursor, win, FALSE );
/*
* make sure cursor stays on the screen, at the end of the
* previous line
*/
if (win->cline > win->top_line + win->ruler)
--win->cline;
--win->rline;
rcol = plen;
ccol = rcol - win->bcol;
--win->file_info->length;
restore_marked_block( win, -1 );
adjust_windows_cursor( win, -1 );
show_size( win );
check_virtual_col( win, rcol, ccol );
win->file_info->dirty = GLOBAL;
make_ruler( win );
show_ruler( win );
}
} else {
/*
* normal delete
*
* find out how much to delete (depends on indent mode)
*/
del_count = 1; /* the default */
if (mode.indent) {
/*
* indent only happens if the cursor is on the first
* non-blank character of the line
*/
if ((pos = first_non_blank( g_status.line_buff )) == rcol
|| g_status.line_buff[pos] == '\n'
|| g_status.line_buff[pos] == CONTROL_Z) {
/*
* now work out how much to indent
*/
p = cpb( win->cursor );
for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
if ((plen=first_non_blank( p )) < rcol && *(p+plen)!='\n') {
/*
* found the line to match
*/
del_count = rcol - plen;
break;
}
}
}
}
/*
* move text to delete char(s), unless no chars actually there
*/
if (rcol - del_count < len) {
dest = g_status.line_buff + rcol - del_count;
if (rcol > len) {
source = g_status.line_buff + len;
len = 2;
} else {
source = g_status.line_buff + rcol;
len = len - rcol + 2;
}
memmove( dest, source, len );
}
rcol -= del_count;
ccol -= del_count;
win->file_info->dirty = NOT_LOCAL;
show_ruler_char( win );
show_changed_line( win );
check_virtual_col( win, rcol, ccol );
if (!win->file_info->dirty)
show_curl_line( win );
if (old_bcol != win->bcol) {
make_ruler( win );
show_ruler( win );
}
}
win->file_info->modified = TRUE;
return( OK );
}
/*
* Name: line_kill
* Purpose: To delete the line the cursor is on.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If *window->cursor is pointing to CONTROL_Z then do not do a
* line kill (can't kill a NULL line).
*/
int line_kill( WINDOW *window )
{
int i = 0;
text_ptr s; /* next line in file */
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->file_info->length > 0 && *win->cursor != CONTROL_Z) {
s = win->cursor = cpf( win->cursor );
load_undo_buffer( g_status.copied ? g_status.line_buff : win->cursor );
g_status.copied = TRUE;
g_status.line_buff[0] = CONTROL_Z;
/*
* if line to delete has \n at end of line then decrement file length.
*/
if (*(s + linelen( s )) == '\n') {
--win->file_info->length;
--i;
}
un_copy_line( s, win, FALSE );
win->file_info->dirty = NOT_LOCAL;
/*
* move all cursors one according to i, restore begin and end block
*/
adjust_windows_cursor( win, i );
restore_marked_block( win, i );
/*
* we are not doing a GLOBAL update, so update current window here
*/
if (win->file_info->dirty == NOT_LOCAL)
my_scroll_down( win );
show_size( win );
return( OK );
} else
return( ERROR );
}
/*
* Name: char_del_under
* Purpose: To delete the character under the cursor.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If the cursor is beyond the end of the line, then this
* command is ignored.
* DeleteChar and StreamDeleteChar use this function.
*/
int char_del_under( WINDOW *window )
{
char *source; /* source of block move to delete character */
register int len;
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
return( OK );
copy_line( win->cursor, win->bottom_line );
if (win->rcol < (len = linelen( g_status.line_buff ))) {
/*
* move text to delete using buffer
*/
source = g_status.line_buff + win->rcol + 1;
memmove( source-1, source, len - win->rcol + 2 );
win->file_info->dirty = GLOBAL;
win->file_info->modified = TRUE;
show_changed_line( win );
} else if (g_status.command == StreamDeleteChar)
join_line( win );
return( OK );
}
/*
* Name: eol_kill
* Purpose: To delete everything from the cursor to the end of the line.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If the cursor is beyond the end of the line, then this
* command is ignored.
*/
int eol_kill( WINDOW *window )
{
register char *dest; /* the start of the delete area */
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
return( OK );
copy_line( win->cursor, win->bottom_line );
load_undo_buffer( g_status.line_buff );
if ((unsigned)win->rcol < linelen( g_status.line_buff )) {
/*
* truncate to delete rest of line
*/
dest = g_status.line_buff + win->rcol;
*dest++ = '\n';
*dest = CONTROL_Z;
win->file_info->dirty = GLOBAL;
show_changed_line( win );
}
return( OK );
}
/*
* Name: undo_line
* Purpose: To retrieve unaltered line if possible.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: Changes are made to the line buffer so the underlying text has
* not changed. Put the unchanged line from the file into the
* line buffer and display it.
*/
int undo_line( WINDOW *window )
{
register WINDOW *win; /* put window pointer in a register */
win = window;
if (win->rline <= win->file_info->length && g_status.copied) {
g_status.copied = FALSE;
copy_line( win->cursor, win->bottom_line );
win->file_info->dirty = GLOBAL;
show_changed_line( win );
}
return( OK );
}
/*
* Name: undo
* Purpose: To retrieve (pop) a line from the undo stack
* Date: September 26, 1991
* Passed: window: pointer to current window
* Notes: Insert an empty line into the file then pop the line in the undo
* stack. When we pop line 0, there are no more lines on the stack.
* Set the stack pointer to -1 to indicate an empty stack.
*/
int undo( WINDOW *window )
{
char *source; /* pointer to line_buff */
unsigned int len;
unsigned int number;
register WINDOW *win; /* put window pointer in a register */
if (g_status.bot_stack != NULL) {
win = window;
win->cursor = cpf( win->cursor );
un_copy_line( win->cursor, win, TRUE );
copy_line( win->cursor, win->bottom_line );
/*
* make room for '\n'. then, after we un_copy the g_status.line_buff, we
* have added a line to the file.
*/
source = g_status.line_buff;
number = linelen( source ) + 2;
len = linelen( g_status.top_stack ) + 1;
memmove( source+len, source, number );
_fmemcpy( source, g_status.top_stack, len );
un_copy_line( win->cursor, win, TRUE );
/*
* ajust cursors in other windows opened to the same file.
*/
adjust_windows_cursor( win, 1 );
/*
* we have now undeleted a line. increment the file length and display
* it.
*/
win->file_info->length++;
win->file_info->dirty = GLOBAL;
show_size( win );
/*
* now "pop" the line off the stack.
*/
number = (unsigned)(ptoul( g_status.bot_stack ) -
ptoul( g_status.top_stack )) - len;
hw_move( g_status.top_stack, g_status.top_stack+len, number );
/*
* bottom of stack now moves up. it the bottom of the stack
* is equal to top of stack, then the stack is empty.
*/
g_status.bot_stack = cpb( g_status.bot_stack ) - len;
if (ptoul( g_status.bot_stack ) == ptoul( g_status.top_stack ))
g_status.bot_stack = NULL;
}
return( OK );
}
/*
* Name: beg_next_line
* Purpose: To move the cursor to the beginning of the next line.
* Date: October 4, 1991
* Passed: window: pointer to current window
*/
int beg_next_line( WINDOW *window )
{
int rc;
window->rcol = 0;
check_virtual_col( window, window->rcol, window->ccol );
rc = prepare_move_down( window );
sync( window );
make_ruler( window );
show_ruler( window );
return( rc );
}
/*
* Name: next_line
* Purpose: To move the cursor to the first character of the next line.
* Date: October 4, 1991
* Passed: window: pointer to current window
*/
int next_line( WINDOW *window )
{
register int rcol;
register WINDOW *win; /* put window pointer in a register */
int rc;
win = window;
rc = prepare_move_down( win );
rcol = first_non_blank( win->cursor );
if (win->cursor[rcol] == '\n')
rcol = 0;
check_virtual_col( win, rcol, win->ccol );
sync( win );
make_ruler( win );
show_ruler( win );
return( rc );
}
/*
* Name: home
* Purpose: To move the cursor to the left of the current line.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: this routine is made a little more complicated with cursor sync.
* if the g_status.copied flag is set we need to see from what file
* the line_buff was copied.
*/
int home( WINDOW *window )
{
register int rcol;
register WINDOW *win; /* put window pointer in a register */
win = window;
if (g_status.copied && win->file_info == g_status.current_window->file_info){
rcol = first_non_blank( g_status.line_buff );
if (g_status.line_buff[rcol] == '\n')
rcol = 0;
} else {
win->cursor = cpf( win->cursor );
rcol = first_non_blank( win->cursor );
if (win->cursor[rcol] == '\n')
rcol = 0;
}
if (win->rcol == rcol)
rcol = 0;
check_virtual_col( win, rcol, win->ccol );
sync( win );
make_ruler( win );
show_ruler( win );
return( OK );
}
/*
* Name: goto_eol
* Purpose: To move the cursor to the eol character of the current line.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: this routine is made a little more complicated with cursor sync.
* if the g_status.copied flag is set we need to see from what file
* the line_buff was copied.
*/
int goto_eol( WINDOW *window )
{
register int rcol;
register WINDOW *win; /* put window pointer in a register */
win = window;
if (g_status.copied) {
if (win->file_info == g_status.current_window->file_info)
rcol = linelen( g_status.line_buff );
else
rcol = linelen( win->cursor );
} else
rcol = linelen( win->cursor );
win->ccol = win->start_col + rcol - win->bcol;
check_virtual_col( win, rcol, win->ccol );
sync( win );
make_ruler( win );
show_ruler( win );
return( OK );
}
/*
* Name: goto_top
* Purpose: To move the cursor to the top of the current window.
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: If the start of the file occurs before the top of the window,
* then the start of the file is moved to the top of the window.
*/
int goto_top( WINDOW *window )
{
text_ptr cursor; /* anticipated cursor line */
register WINDOW *win; /* put window pointer in a register */
win = window;
un_copy_line( win->cursor, win, TRUE );
update_line( win );
win->cursor = cpb( win->cursor );
for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
if ((cursor = find_prev( win->cursor )) == NULL)
break;
win->cursor = cursor;
}
show_curl_line( win );
sync( win );
return( OK );
}
/*
* Name: goto_bottom
* Purpose: To move the cursor to the bottom of the current window.
* Date: June 5, 1991
* Passed: window: pointer to current window
*/
int goto_bottom( WINDOW *window )
{
text_ptr cursor;
register WINDOW *win; /* put window pointer in a register */
win = window;
un_copy_line( win->cursor, win, TRUE );
update_line( win );
win->cursor = cpf( win->cursor );
for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
if ((cursor = find_next( win->cursor )) == NULL ||
find_next( cursor ) == NULL)
break;
win->cursor = cursor;
}
show_curl_line( win );
sync( win );
return( OK );
}
/*
* Name: set_tabstop
* Purpose: To set the current interval between tab stops
* Date: October 1, 1989
* Notes: Tab interval must be reasonable, and this function will
* not allow tabs more than MAX_COLS / 2.
*/
int set_tabstop( WINDOW *window )
{
char num_str[MAX_COLS]; /* tab interval as a character string */
int tab; /* new tab interval */
register int rc;
itoa( mode.tab_size, num_str, 10 );
/*
* tab interval:
*/
rc = get_name( ed7, window->bottom_line, num_str, g_display.message_color );
if (rc == OK) {
tab = atoi( num_str );
if (tab < MAX_COLS/2)
mode.tab_size = tab;
else {
/*
* tab size too long
*/
error( WARNING, window->bottom_line, ed8 );
rc = ERROR;
}
}
return( rc );
}
/*
* Name: show_line_col
* Purpose: show current real line and column of current cursor position
* Date: June 5, 1991
* Passed: window: pointer to current window
* Notes: Blank old position and display new position. current line and
* column may take up to 11 columns, which allows the display of
* 999 columns and 9,999,999 lines.
*/
void show_line_col( WINDOW *window )
{
int i;
register int k;
char line_col[20], num[10];
/*
* blank out current line:column position.
*/
memset( line_col, ' ', 12 );
line_col[12] = '\0';
/*
* convert column to ascii and store in display buffer.
*/
itoa( window->rcol+1, num, 10 );
i = strlen( num ) - 1;
for (k=11; i>=0; i--, k--)
line_col[k] = num[i];
/*
* put in colon to separate line and column
*/
line_col[k--] = ':';
/*
* convert line to ascii and store in display buffer.
*/
ltoa( window->rline, num, 10 );
i = strlen( num ) - 1;
for (; i>=0; i--, k--)
line_col[k] = num[i];
/*
* find line to start line:column display then output
*/
s_output( line_col, window->top_line-1, window->end_col-11,
g_display.head_color );
show_asterisk( window );
}
/*
* Name: show_asterisk
* Purpose: give user an indication if file is dirty
* Date: September 16, 1991
* Passed: window: pointer to current window
*/
void show_asterisk( WINDOW *window )
{
c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
window->top_line-1, g_display.head_color );
}
/*
* Name: toggle_overwrite
* Purpose: toggle overwrite-insert mode
* Date: September 16, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_overwrite( WINDOW *arg_filler )
{
mode.insert = !mode.insert;
show_insert_mode( );
set_cursor_size( mode.insert ? g_display.insert_cursor :
g_display.overw_cursor );
return( OK );
}
/*
* Name: toggle_smart_tabs
* Purpose: toggle smart tab mode
* Date: June 5, 1992
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_smart_tabs( WINDOW *arg_filler )
{
mode.smart_tab = !mode.smart_tab;
show_smarttab_mode( );
return( OK );
}
/*
* Name: toggle_indent
* Purpose: toggle indent mode
* Date: September 16, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_indent( WINDOW *arg_filler )
{
mode.indent = !mode.indent;
show_indent_mode( );
return( OK );
}
/*
* Name: set_left_margin
* Purpose: set left margin for word wrap
* Date: November 27, 1991
* Passed: window
*/
int set_left_margin( WINDOW *window )
{
register int rc;
char temp[80];
itoa( mode.left_margin + 1, temp, 10 );
/*
* enter left margin
*/
rc = get_name( ed9, window->bottom_line, temp, g_display.message_color );
if (rc == OK) {
rc = atoi( temp ) - 1;
if (rc < 0 || rc >= mode.right_margin) {
/*
* left margin out of range
*/
error( WARNING, window->bottom_line, ed10 );
rc = ERROR;
} else {
mode.left_margin = rc;
show_all_rulers( );
}
}
return( rc );
}
/*
* Name: set_right_margin
* Purpose: set right margin for word wrap
* Date: November 27, 1991
* Passed: window
*/
int set_right_margin( WINDOW *window )
{
register int rc;
char temp[80];
itoa( mode.right_margin + 1, temp, 10 );
/*
* enter right margin
*/
rc = get_name( ed11, window->bottom_line, temp, g_display.message_color );
if (rc == OK) {
rc = atoi( temp ) - 1;
if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH) {
/*
* right margin out of range
*/
error( WARNING, window->bottom_line, ed12 );
rc = ERROR;
} else {
mode.right_margin = rc;
show_all_rulers( );
}
}
return( rc );
}
/*
* Name: set_paragraph_margin
* Purpose: set column to begin paragraph
* Date: November 27, 1991
* Passed: window
* Notes: paragraph may be indented, flush, or offset.
*/
int set_paragraph_margin( WINDOW *window )
{
register int rc;
char temp[80];
itoa( mode.parg_margin + 1, temp, 10 );
/*
* enter paragraph margin
*/
rc = get_name( ed13, window->bottom_line, temp, g_display.message_color );
if (rc == OK) {
rc = atoi( temp ) - 1;
if (rc < 0 || rc >= mode.right_margin) {
/*
* paragraph margin out of range
*/
error( WARNING, window->bottom_line, ed14 );
rc = ERROR;
} else {
mode.parg_margin = rc;
show_all_rulers( );
}
}
return( rc );
}
/*
* Name: toggle_crlf
* Purpose: toggle crlf mode
* Date: November 27, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_crlf( WINDOW *arg_filler )
{
mode.crlf = (mode.crlf == CRLF) ? LF : CRLF;
show_crlf_mode( );
return( OK );
}
/*
* Name: toggle_ww
* Purpose: toggle word wrap mode
* Date: November 27, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_ww( WINDOW *arg_filler )
{
++mode.word_wrap;
if (mode.word_wrap > DYNAMIC_WRAP)
mode.word_wrap = NO_WRAP;
show_wordwrap_mode( );
return( OK );
}
/*
* Name: toggle_trailing
* Purpose: toggle eleminating trainling space at eol
* Date: November 25, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_trailing( WINDOW *arg_filler )
{
mode.trailing = !mode.trailing;
show_trailing( );
return( OK );
}
/*
* Name: toggle_z
* Purpose: toggle writing control z at eof
* Date: November 25, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_z( WINDOW *arg_filler )
{
mode.control_z = !mode.control_z;
show_control_z( );
return( OK );
}
/*
* Name: toggle_eol
* Purpose: toggle writing eol character at eol
* Date: November 25, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_eol( WINDOW *arg_filler )
{
register file_infos *file;
mode.show_eol = !mode.show_eol;
for (file=g_status.file_list; file != NULL; file=file->next)
file->dirty = GLOBAL;
return( OK );
}
/*
* Name: toggle_search_case
* Purpose: toggle search case
* Date: September 16, 1991
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_search_case( WINDOW *arg_filler )
{
bm.search_case = (bm.search_case == IGNORE) ? MATCH : IGNORE;
show_search_case( );
if (bm.search_defined == OK)
build_boyer_array( );
return( OK );
}
/*
* Name: toggle_sync
* Purpose: toggle sync mode
* Date: January 15, 1992
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_sync( WINDOW *arg_filler )
{
mode.sync = !mode.sync;
show_sync_mode( );
return( OK );
}
/*
* Name: toggle_ruler
* Purpose: toggle ruler
* Date: March 5, 1992
* Passed: arg_filler: argument to satify function prototype
*/
int toggle_ruler( WINDOW *arg_filler )
{
register WINDOW *wp;
mode.ruler = !mode.ruler;
wp = g_status.window_list;
while (wp != NULL) {
if (mode.ruler) {
/*
* there has to be more than one line in a window to display a ruler.
* even if the ruler mode is on, we need to check the num of lines.
*/
if (wp->bottom_line - wp->top_line >0) {
if (wp->cline == wp->top_line)
++wp->cline;
if (wp->cline > wp->bottom_line)
wp->cline = wp->bottom_line;
wp->ruler = TRUE;
} else
wp->ruler = FALSE;
} else {
/*
* if this is the first page in a file, then we may need to "pull"
* the file up before displaying the first page.
*/
if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
--wp->cline;
if (wp->cline < wp->top_line)
wp->cline = wp->top_line;
wp->ruler = FALSE;
}
make_ruler( wp );
setup_window( wp );
if (wp->visible)
redraw_current_window( wp );
wp = wp->next;
}
return( OK );
}
/*
* Name: sync
* Purpose: carry out cursor movements in all visible windows
* Date: January 15, 1992
* Passed: window
* Notes: switch sync semaphore when we do this so we don't get into a
* recursive loop. All cursor movement commands un_copy_line before
* moving the cursor off the current line. You MUST make certain
* that the current line is uncopied in the task routines before
* calling sync.
*/
void sync( WINDOW *window )
{
register WINDOW *wp;
register file_infos *fp;
if (mode.sync && mode.sync_sem) {
mode.sync_sem = FALSE;
wp = g_status.window_list;
while (wp != NULL) {
if (wp->visible && wp != window) {
(*do_it[g_status.command])( wp );
show_line_col( wp );
show_ruler_pointer( wp );
}
wp = wp->next;
}
mode.sync_sem = TRUE;
fp = g_status.file_list;
while (fp != NULL) {
if (fp->dirty != FALSE)
fp->dirty = GLOBAL;
fp = fp->next;
}
}
}
/*
* Name: editor
* Purpose: Set up the editor structures and display changes as needed.
* Date: June 5, 1991
* Notes: Master editor routine.
*/
void editor( )
{
char *name; /* name of file to start editing */
register WINDOW *window; /* current active window */
int c;
/*
* Check that user specified file to edit, if not offer help
*/
if (g_status.argc > 1)
c = edit_next_file( g_status.current_window );
else {
name = g_status.rw_name;
*name = '\0';
/*
* file name to edit
*/
c = get_name( ed15, g_display.nlines, name, g_display.text_color );
if (c == ERROR || *name == '\0')
return;
if (c == OK)
c = attempt_edit_display( name, GLOBAL );
}
g_status.stop = c == OK ? FALSE : TRUE;
if (c == OK)
set_cursor_size( mode.insert ? g_display.insert_cursor :
g_display.overw_cursor );
/*
* main loop - keep updating the display and processing any commands
* while user has not pressed the stop key
*/
for (; g_status.stop != TRUE;) {
window = g_status.current_window;
display_dirty_windows( window );
/*
* set the critical error handler flag to a known state before we
* do each editor command.
*/
ceh.flag = OK;
/*
* Get a key from the user. Look up the function assigned to that key.
* All regular text keys are assigned to function 0. Text characters
* are less than 0x100, decimal 256, which includes the ASCII and
* extended ASCII character set.
*/
g_status.key_pressed = getkey( );
g_status.command = getfunc( g_status.key_pressed );
if (g_status.wrapped) {
g_status.wrapped = FALSE;
show_search_message( CLR_SEARCH, g_display.mode_color );
}
g_status.control_break = FALSE;
if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
record_keys( window->bottom_line );
(*do_it[g_status.command])( window );
}
}
cls( );
xygoto( 0, 0 );
}
/*
* Name: display_dirty_windows
* Purpose: Set up the editor structures and display changes as needed.
* Date: June 5, 1991
* Notes: Display all windows with dirty files.
*/
void display_dirty_windows( WINDOW *window )
{
register WINDOW *below; /* window below current */
register WINDOW *above; /* window above current */
file_infos *file; /* temporary file structure */
/*
* update all windows that point to any file that has been changed
*/
above = below = window;
while (above->prev || below->next) {
if (above->prev) {
above = above->prev;
show_dirty_window( above );
}
if (below->next) {
below = below->next;
show_dirty_window( below );
}
}
file = window->file_info;
if (file->dirty == LOCAL || file->dirty == GLOBAL)
display_current_window( window );
for (file=g_status.file_list; file != NULL; file=file->next)
file->dirty = FALSE;
/*
* Set the cursor position at window->ccol, window->cline. Show the
* user where in the file the cursor is positioned.
*/
xygoto( window->ccol, window->cline );
show_line_col( window );
show_ruler_pointer( window );
}
/*
* Name: show_dirty_window
* Purpose: show changes in non-current window
* Date: June 5, 1991
* Passed: window: pointer to current window
*/
void show_dirty_window( WINDOW *window )
{
register WINDOW *win; /* register window pointer */
int dirty;
win = window;
if (win->visible) {
dirty = win->file_info->dirty;
if (dirty == GLOBAL || dirty == NOT_LOCAL) {
display_current_window( win );
show_size( win );
}
show_asterisk( win );
}
}
/*
* Name: play_back
* Purpose: play back a series of keystrokes assigned to key
* Date: April 1, 1992
* Notes: go thru the macro key list playing back the recorded keystrokes.
*/
int play_back( WINDOW *window )
{
int key;
int rc = OK;
/*
* if we are recording a macro, let's just return if we do a recursive
* definition. Otherwise, we end up executing our recursive macro
* while we are defining it.
*/
if (mode.record == TRUE && g_status.key_pressed == g_status.recording_key)
rc = ERROR;
else {
/*
* set the global macro flags, so other routines will know
* if a macro is executing.
*/
g_status.macro_executing = TRUE;
do {
/*
* find the first keystroke in the macro.
*/
g_status.macro_next = macro.first_stroke[g_status.key_pressed-256];
key = macro.strokes[g_status.macro_next].key;
if (key != MAX_KEYS+1 && key != -1) {
do {
/*
* set up all editor variables as if we were entering
* keys from the keyboard.
*/
window = g_status.current_window;
display_dirty_windows( window );
ceh.flag = OK;
g_status.key_pressed = macro.strokes[g_status.macro_next].key;
g_status.command = getfunc( g_status.key_pressed );
if (g_status.wrapped) {
g_status.wrapped = FALSE;
show_search_message( CLR_SEARCH, g_display.mode_color );
}
/*
* while there are no errors or Control-Breaks, let's keep on
* executing a macro. g_status.control_break is a global
* editor flag that is set in our Control-Break interrupt
* handler routine.
*/
if (g_status.control_break == TRUE) {
rc = ERROR;
break;
}
/*
* we haven't called any editor function yet. we need
* to look at the editor command that is to be executed.
* if the command is PlayBack, we need to break out of
* this inner do loop and start executing the macro
* from the beginning (the outer do loop).
*
* if we don't break out now from a recursive macro, we will
* recursively call PlayBack and we might overflow
* the stack.
*/
if (g_status.command == PlayBack)
break;
if (g_status.command >= 0 && g_status.command < NUM_FUNCS)
rc = (*do_it[g_status.command])( window );
} while (rc == OK && (g_status.macro_next =
macro.strokes[g_status.macro_next].next) != -1);
if (g_status.macro_next == -1)
rc = ERROR;
}
} while (rc == OK);
g_status.macro_executing = FALSE;
}
return( OK );
}
/*
* Name: Pause
* Purpose: Enter pause state for macros
* Date: June 5, 1992
* Passed: arg_filler: argument to satify function prototype
* Returns: ERROR if the ESC key was pressed, OK otherwise.
* Notes: this little function is quite useful in macro definitions. if
* it is called near the beginning of a macro, the user may decide
* whether or not to stop the macro.
*/
int pause( WINDOW *arg_filler )
{
int c;
/*
* tell user we are paused.
*/
s_output( paused1, g_display.mode_line, 23, g_display.mode_color | 0x80 );
s_output( paused2, g_display.mode_line, 23+strlen( paused1 ),
g_display.mode_color );
/*
* get the user's response and restore the mode line.
*/
c = getkey( );
show_modes( );
if (mode.record == TRUE) {
/*
* if recording a macro, show recording message
*/
s_output( main15, g_display.mode_line, 23, g_display.mode_color | 0x80 );
show_avail_strokes( );
}
return( c == ESC ? ERROR : OK );
}