/******************* 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 , 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 ); }