Logo Search packages:      
Sourcecode: sc version File versions  Download package

vi.c

/*    SC    A Spreadsheet Calculator
 *
 *    One line vi emulation
 *    $Revision: 7.16 $
 */

#include <sys/types.h>
#ifdef BSD42
#include <strings.h>
#else
#ifndef SYSIII
#include <string.h>
#endif
#endif

#include <signal.h>
#include <curses.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include "sc.h"

#if defined(REGCOMP)
#include <regex.h>
#endif
#if defined(RE_COMP)
extern char *re_comp(char *s);
extern char *re_exec(char *s);
#endif
#if defined(REGCMP)
char *regcmp();
char *regex();
#endif

void doshell();
void gohome();
void leftlimit();
void rightlimit();
void gototop();
void gotobottom();

#define istext(a) (isalnum(a) || ((a) == '_'))

/*#define bool    int*/
#define true      1
#define false     0

static void append_line();
static void back_hist();
static int  back_line(int arg);
static int  back_word(int arg, int big_word);
static void back_space();
static void change_case(int arg);
static void col_0();
static void cr_line();
static void del_in_line(int arg, int back_null);
static void del_to_end();
static void doabbrev();
static void dogoto();
static void dotab();
static void dotcmd();
static int  find_char(int arg, int n);
static void for_hist();
static int  for_line(int arg, int stop_null);
static int  for_word(int arg, int end_word, int big_word, int stop_null);
static int  istart;
static void last_col();
static void match_paren();
static void readhistfile(FILE *fp);
static void rep_char();
static void replace_in_line(int c);
static void replace_mode();
static void restore_it();
static void savedot(int c);
static void save_hist();
static void search_again(bool reverse);
static void search_hist();
static void search_mode(char sind);
static void stop_edit();
static int  to_char(int arg, int n);
static void u_save(int c);
static void yank_cmd(int delete, int change);
static void yank_chars(register int first, register int last, int delete);

extern int framerows;         /* Rows in current frame */
extern char mode_ind;         /* Mode indicator */
extern char search_ind;       /* Search indicator */
extern int lcols;       /* Spreadsheet Column the cursor was in last */
char  *completethis = NULL;
int   search_dir;       /* Search direction:  forward = 0; back = 1 */

/* values for mode below */

#define INSERT_MODE     0     /* Insert mode */
#define EDIT_MODE       1     /* Edit mode */
#define REP_MODE        2     /* Replace mode */
#define SEARCH_MODE     3     /* Get arguments for '/' command */
#define NAVIGATE_MODE   4     /* Navigate the spreadsheet while editing
                              a line */

#define     DOTLEN            200

static int mode = INSERT_MODE;
struct      hist {
      unsigned int      len;
      char        *histline;
} history[HISTLEN + 1];

static int  histp = 0;
static int  lasthist = 0;
static int  endhist = -1;
static int  histsessionstart = 0;
static int  histsessionnew = 0;

#ifdef REGCOMP
static regex_t    preg;
static regex_t    *last_search = NULL;
static int  errcode;
#else
#ifndef RE_COMP
static char *last_search = NULL;
#endif
#endif
static char *undo_line = NULL;
static int  undo_lim;
static char dotb[DOTLEN];
static int  doti = 0;
static int  do_dot = 0;
static int  nosavedot = 1;
static int  dotarg = 1;
static char putbuf[FBUFLEN];
static int  findfunc = '\0';
static int  findchar = 1;
static int  finddir = 0;

void
write_line(int c)
{
    struct frange *fr;
    struct crange *cr;

    error("");
    if (c != ctl('i')) completethis = NULL;
    if (mode == EDIT_MODE) {
      nosavedot = 0;
      switch (c) {
      case KEY_BACKSPACE:
      case (ctl('h')):  linelim = back_line(arg);           break;
      case (ctl('i')):  dotab();                      break;
      case (ctl('m')):  if (search_ind != ' ')
                            search_hist();
                        else
                            cr_line();                      break;
      case 'v':
      case (ctl('v')):  toggle_navigate_mode();             break;
      case ESC:   stop_edit();                              break;
      case '+':   for_hist();                         break;
      case '-':   back_hist();                              break;
      case KEY_END:
      case (ctl('e')):
      case '$':   last_col();                         break;
      case '.':   dotcmd();                           break;
      case '!':   doshell();                          break;
      case ';':   if (findfunc)
                      ungetch(findchar);
                  else
                      break;
                  findchar = 0;
                  if (findfunc == 'f')
                      linelim = find_char(arg, finddir);
                  else
                      linelim = to_char(arg, finddir);
                                                      break;
      case ',':   if (findfunc)
                      ungetch(findchar);
                  else
                      break;
                  findchar = 0;
                  if (findfunc == 'f')
                      linelim = find_char(arg, -(finddir));
                  else
                      linelim = to_char(arg, -(finddir));
                                                      break;
      case '~':   u_save(c); change_case(arg);              break;
      case '%':   match_paren();                            break;
#ifdef KEY_FIND
      case KEY_FIND:
#endif
      case '?':
      case '/':   search_mode(c);                           break;
      case KEY_HOME:
      case (ctl('a')):
      case '0':   col_0();                            break;
      case 'A':   u_save(c); last_col(); append_line();           break;
      case 'B':   linelim = back_word(arg, 1);              break;
      case 'C':   u_save(c); del_to_end(); append_line();         break;
      case 'D':   u_save(c); del_to_end();                  break;
      case 'E':   linelim = for_word(arg, 1, 1, 0);         break;
      case 'F':   linelim = find_char(arg, -1);             break;
      case 'G':   if (histp > 0) histp = lasthist; for_hist();    break;
      case 'I':   u_save(c); col_0(); insert_mode();        break;
      case 'N':   search_again(true);                       break;
      case 'P':   u_save(c);
                  ins_string(putbuf);
                  linelim = back_line(1);                   break;
      case 'R':   u_save(c); replace_mode();                break;
      case 'T':   linelim = to_char(arg, -1);               break;
      case 'W':   linelim = for_word(arg, 0, 1, 0);         break;
      case 'X':   u_save(c); back_space();                  break;
      case 'Y':   yank_chars(linelim, strlen(line), 0);           break;
      case 'a':   u_save(c); append_line();                 break;
      case 'b':   linelim = back_word(arg, 0);              break;
      case 'c':   u_save(c); yank_cmd(1, 1); insert_mode(); break;
      case 'd':   u_save(c); yank_cmd(1, 0);                break;
      case 'e':   linelim = for_word(arg, 1, 0, 0);         break;
      case 'f':   linelim = find_char(arg, 1);              break;
      case KEY_LEFT:
      case (ctl('b')):
      case 'h':   linelim = back_line(arg);                 break;
      case KEY_IC:
      case 'i':   u_save(c); insert_mode();                 break;
      case KEY_DOWN:
      case 'j':   for_hist();                         break;
      case KEY_UP:
      case 'k':   back_hist();                              break;
      case KEY_RIGHT:
      case (ctl('f')):
      case ' ':
      case 'l':   linelim = for_line(arg, 0);               break;
      case 'n':   search_again(false);                      break;
      case 'p':   u_save(c);
                  linelim = for_line(1, 1);
                  ins_string(putbuf);
                  linelim = back_line(1);                   break;
      case 'q':   stop_edit();                              break;
      case 'r':   u_save(c); rep_char();                    break;
      case 's':   u_save(c); del_in_line(arg, 0); insert_mode();  break;
      case 't':   linelim = to_char(arg, 1);                break;
      case 'u':   restore_it();                             break;
      case 'w':   linelim = for_word(arg, 0, 0, 0);         break;
      case KEY_DC:
      case 'x':   u_save(c); del_in_line(arg, 1);                 break;
      case 'y':   yank_cmd(0, 0);                           break;
      default:                                        break;
      }
    } else if (mode == INSERT_MODE) { 
      if (c == (ctl('m')))
          savedot(ESC);
      else
          savedot(c);
      switch (c) {
      case KEY_BACKSPACE:
      case (ctl('h')):  back_space();                       break;
      case (ctl('i')):  dotab();                      break;
      case (ctl('m')):  cr_line();                    break;
      case (ctl('v')):  toggle_navigate_mode();             break;
      case KEY_LEFT:
      case (ctl('b')):  if (numeric_field) {
                            if (linelim == strlen(line) &&
                                  (line[linelim - 1] == '+' ||
                                   line[linelim - 1] == '-')) {
                              toggle_navigate_mode();
                              backcol(1);
                            } else {
                              c = craction;
                              craction = 0;
                              cr_line();
                              craction = c;
                              backcol(1);
                            }
                        } else {
                            linelim = back_line(arg);
                            istart = linelim;
                        }   break;
      case KEY_RIGHT:
      case (ctl('f')):  if (numeric_field) {
                            if (linelim == strlen(line) &&
                                  (line[linelim - 1] == '+' ||
                                   line[linelim - 1] == '-')) {
                              toggle_navigate_mode();
                              forwcol(1);
                            } else {
                              c = craction;
                              craction = 0;
                              cr_line();
                              craction = c;
                              forwcol(1);
                            }
                        } else {
                            linelim = for_line(arg, 1);
                            istart = linelim;
                        }   break;
      case KEY_DOWN:
      case (ctl('n')):  if (numeric_field) {
                            if (linelim == strlen(line) &&
                                  (line[linelim - 1] == '+' ||
                                   line[linelim - 1] == '-')) {
                              toggle_navigate_mode();
                              forwrow(1);
                            } else {
                              c = craction;
                              craction = 0;
                              cr_line();
                              craction = c;
                              forwrow(1);
                            }
                        } else {
                            for_hist();
                            istart = linelim;
                        }   break;
      case KEY_UP:
      case (ctl('p')):  if (numeric_field) {
                            if (linelim == strlen(line) &&
                                  (line[linelim - 1] == '+' ||
                                   line[linelim - 1] == '-')) {
                              toggle_navigate_mode();
                              backrow(1);
                            } else {
                              c = craction;
                              craction = 0;
                              cr_line();
                              craction = c;
                              backrow(1);
                            }
                        } else {
                            back_hist();
                            istart = linelim;
                        }   break;
      case KEY_HOME:
      case (ctl('a')):  col_0();                      break;
      case KEY_END:
      case (ctl('e')):  last_col();                   break;
      case ESC:         ins_in_line(0);
                        edit_mode();                        break;
      /* '\035' is ^], which expands abbreviations without inserting another
       * character in the line
       */
      case '\035':            if (linelim > 0) doabbrev();        break;
      default:          ins_in_line(c);                     break;
      }
    } else if (mode == SEARCH_MODE) {
      switch (c) {
      case KEY_BACKSPACE:
      case (ctl('h')):  back_space();                       break;
      case (ctl('m')):  search_hist();                      break;
      case ESC:         ins_in_line(0);
                        edit_mode();                        break;
      /* '\035' is ^], which expands abbreviations without inserting another
       * character in the line
       */
      case '\035':            if (linelim > 0) doabbrev();        break;
      default:          ins_in_line(c);                     break;
      }
    } else if (mode == REP_MODE) {
      savedot(c);
      switch (c) {
      case KEY_BACKSPACE:
      case (ctl('h')):  if (linelim > strlen(undo_line))
                            back_space();
                        else {
                            linelim = back_line(1);
                            *(line+linelim) = *(undo_line+linelim);
                        }                             break;
      case (ctl('m')):  cr_line();                    break;
      case ESC:         edit_mode();                        break;
      default:          replace_in_line(c);                 break;
      }
    } else if (mode == NAVIGATE_MODE) {
      switch (c) {
      case '.':
      case ':':
      case (ctl('i')):  if (!showrange) {
                            toggle_navigate_mode();
                            startshow();
                        } else if (linelim == strlen(line) &&
                              (line[linelim - 1] == '+' ||
                              line[linelim - 1] == '-' ||
                              (line[linelim - 1] == ' ' &&
                               line[linelim - 2] == '='))) {
                            ins_string("@sum(");
                            showdr();
                            ins_in_line(')');
                        } else {
                            showdr();
                            ins_in_line(' ');
                        }                             break;
      case ' ':         if (showrange) {
                            showdr();
                            ins_in_line(' ');
                            toggle_navigate_mode();
                        } else {
                            forwcol(arg);
                        }                             break;
      case '+':
      case '-':         if (!showrange) {
                            ins_string(v_name(currow, curcol));
                            ins_in_line(c);
                        } else if (linelim == strlen(line) &&
                              (line[linelim - 1] == '+' ||
                              line[linelim - 1] == '-' ||
                              (line[linelim - 1] == ' ' &&
                               line[linelim - 2] == '='))) {
                            ins_string("@sum(");
                            showdr();
                            ins_in_line(')');
                            ins_in_line(c);
                            toggle_navigate_mode();
                        } else {
                            showdr();
                            ins_in_line(')');
                            ins_in_line(c);
                        }                             break;
      case (ctl('m')):  if (!showrange) {
                            ins_string(v_name(currow, curcol));
                            toggle_navigate_mode();
                        } else {
                            toggle_navigate_mode();
                            cr_line();
                        }                             break;
      case 'v':   {   /* insert variable value */
                            struct ent *p = *ATBL(tbl, currow, curcol);
                            char temp[100];

                            if (p && p->flags & is_valid) {
                              (void) sprintf(temp, "%.*f",
                                    precision[curcol], p->v);
                              ins_string(temp);
                              toggle_navigate_mode();
                            }
                        }                             break;
      case 'c':         if ((cr = find_crange(currow, curcol))) {
                            ins_string(r_name(cr->r_left->row,
                                  cr->r_left->col,
                                  cr->r_right->row,
                                  cr->r_right->col));
                            toggle_navigate_mode();
                            ins_in_line(' ');
                            showrange = 0;
                        }                             break;
      case 'f':         if ((fr = find_frange(currow, curcol))) {
                            ins_string(r_name(fr->or_left->row,
                                  fr->or_left->col,
                                  fr->or_right->row,
                                  fr->or_right->col));
                            toggle_navigate_mode();
                            ins_in_line(' ');
                            showrange = 0;
                        }                             break;
      case 'r':         if ((fr = find_frange(currow, curcol))) {
                            ins_string(r_name(fr->ir_left->row,
                                  fr->ir_left->col,
                                  fr->ir_right->row,
                                  fr->ir_right->col));
                            toggle_navigate_mode();
                            ins_in_line(' ');
                            showrange = 0;
                        }                             break;
      case KEY_LEFT:
      case 'h':         backcol(arg);                       break;
      case KEY_RIGHT:
      case 'l':         forwcol(arg);                       break;
      case KEY_DOWN:
      case (ctl('n')):
      case 'j':         forwrow(arg);                       break;
      case KEY_UP:
      case (ctl('p')):
      case 'k':         backrow(arg);                       break;
      case 'q':
      case ctl('g'):
      case (ctl('v')):
      case ESC:         toggle_navigate_mode();
                        showrange = 0;                      break;
      case 'H':         backcol(curcol - stcol + 2);
                                                      break;
      case KEY_NPAGE:               /* next page */
      case (ctl('f')):
      case 'J':
                        {
                        int ps;

                        ps = pagesize ? pagesize :
                            (LINES - RESROW - framerows)/2;
                        forwrow(arg * ps);
                        strow = strow + (arg * ps);
                        FullUpdate++;
                        }
                                                      break;
      case KEY_PPAGE:               /* previous page */
      case (ctl('b')):
      case 'K':
                        {
                        int ps;

                        ps = pagesize ? pagesize :
                            (LINES - RESROW - framerows)/2;
                        backrow(arg * ps);
                        strow = strow - (arg * ps);
                        if (strow < 0) strow = 0;
                        FullUpdate++;
                        }
                                                      break;
      case 'L':         forwcol(lcols - (curcol - stcol) + 1);    break;
      case (ctl('a')):
      case KEY_HOME:          gohome();                     break;
      case '0':         leftlimit();                        break;
      case '$':         rightlimit();                       break;
      case '^':         gototop();                    break;
      case '#':         gotobottom();                       break;
      case 'o':         if (showrange) {
                            int r = currow;
                            int c = curcol;

                            currow = showsr;
                            showsr = r;
                            curcol = showsc;
                            showsc = c;
                            rowsinrange = 1;
                            colsinrange = fwidth[curcol];
                        }                             break;
      case 'm':         markcell();                   break;
      case '`': case '\'':    dotick(c);                    break;
      case '*':         if (nmgetch() == '*') gotonote();   break;
      case 'g':         dogoto();                     break;
      case 'n':         go_last();                    break;
      case 'w':         {
                        register struct ent *p;

                        while (--arg>=0) {
                            do {
                              if (curcol < maxcols - 1)
                                  curcol++;
                              else {
                                  if (currow < maxrows - 1) {
                                    while(++currow < maxrows - 1 &&
                                          row_hidden[currow])
                                        ;
                                    curcol = 0;
                                  } else {
                                    error("At end of table");
                                    break;
                                  }
                              }
                            } while(col_hidden[curcol] ||
                                  !VALID_CELL(p, currow, curcol));
                        }
                        rowsinrange = 1;
                        colsinrange = fwidth[curcol];
                        break;
                        }
      case 'b':         {
                        register struct ent *p;

                        while (--arg>=0) {
                            do {
                              if (curcol) 
                                  curcol--;
                              else {
                                  if (currow) {
                                    while(--currow && row_hidden[currow])
                                        ;
                                    curcol = maxcols - 1;
                                  } else {
                                    error("At start of table");
                                    break;
                                  }
                              }
                            } while (col_hidden[curcol] ||
                                  !VALID_CELL(p, currow, curcol));
                        }
                        rowsinrange = 1;
                        colsinrange = fwidth[curcol];
                        break;
                        }
      case 'C':         if (braille)
                            braillealt ^= 1;
                        break;
      }
    }
}

void
edit_mode()
{
    mode_ind = 'e';
    mode = EDIT_MODE;
    if (linelim < 0)    /* -1 says stop editing, ...so we still aren't */
      return;
    numeric_field = 0;
    linelim = back_line(1);
}

void
insert_mode()
{
    mode_ind = 'i';
    mode = INSERT_MODE;
    istart = linelim;
}

static void
search_mode(char sind)
{
    if (search_ind == ' ') {
      /*
       * This back and forth movement through the history is just a quick
       * way to force the current command to be saved in history[0].histline,
       * allocating space for it if necessary.  The command will be copied
       * back into line by search_hist() before the search is done. - CRM
       */
      back_hist();
      for_hist();
      line[0] = '\0';
      linelim = 0;
      mode_ind = 'i';
      search_ind = sind;
      search_dir = sind == '/' ? 1 : 0;
      mode = SEARCH_MODE;
      istart = linelim;
    }
}

static void
replace_mode()
{
    mode_ind = 'R';
    mode = REP_MODE;
}

void
toggle_navigate_mode()
{
    static char prev_mode = NAVIGATE_MODE;
    int limtmp;

    switch (prev_mode) {
      case INSERT_MODE:
                        if (mode == NAVIGATE_MODE) {
                            prev_mode = NAVIGATE_MODE;
                            insert_mode();
                            break;
                        } else
                            return;
      case EDIT_MODE:
                        if (mode == NAVIGATE_MODE) {
                            prev_mode = NAVIGATE_MODE;
                            limtmp = linelim;
                            edit_mode();
                            linelim = limtmp;
                            break;
                        } else
                            return;
      case NAVIGATE_MODE:
                        prev_mode = mode;
                        mode_ind = 'v';
                        mode = NAVIGATE_MODE;
                        break;
      default:
                        prev_mode = NAVIGATE_MODE;
                        break;
    }
}

void
dotab()
{
    static struct range *firstmatch;
    static struct range *lastmatch;
    static struct range *nextmatch;
    int len;

    if ((linelim > 0 && isalnum(line[linelim-1])) || 
      line[linelim-1] == '_' ||
      (completethis && line[linelim-1] == ' ')) {

      if (!completethis) {
          for (completethis = line + linelim - 1; isalnum(*completethis) ||
                *completethis == '_'; completethis--) /* */;
          completethis++;
          len = line + linelim - completethis;
          if (!find_range(completethis, -len, NULL, NULL, &lastmatch)) {
            firstmatch = lastmatch;
            while (firstmatch->r_next &&
                  !strncmp(completethis, firstmatch->r_next->r_name, len))
                firstmatch = firstmatch->r_next;
            nextmatch = firstmatch;
          } else
            nextmatch = NULL;
      }
      if (nextmatch) {
          len = line + linelim - completethis;
          memmove(completethis, line + linelim, strlen(line) - linelim + 1);
          linelim -= len;
          ins_string(nextmatch->r_name);
          if (*(completethis - 1) == ' ' && *(line + linelim) != ' ')
            ins_in_line(' ');
          if (nextmatch == lastmatch)
            nextmatch = firstmatch;
          else
            nextmatch = nextmatch->r_prev;
      }
    } else
      startshow();
}

/* show the current range (see ^I), we are moving around to define a range */
void
startshow()
{
    showrange = 1;
    showsr = currow;
    showsc = curcol;
    toggle_navigate_mode();
}

/* insert the range we defined by moving around the screen, see startshow() */

void
showdr()
{
    int                 minsr, minsc, maxsr, maxsc;
    /*char        *p;*/
    char          r[12];
    struct frange *fr = find_frange(currow, curcol);

    if (showrange == SHOWROWS) {
      minsr = showsr < currow ? showsr : currow;
      minsc = fr ? fr->or_left->col : 0;
      maxsr = showsr > currow ? showsr : currow;
      maxsc = fr ? fr->or_right->col : maxcols;

      toggle_navigate_mode();
      sprintf(r, "%d:%d", minsr, maxsr);
      ins_string(r);
    } else if (showrange == SHOWCOLS) {
      minsr = 0;
      minsc = showsc < curcol ? showsc : curcol;
      maxsr = maxrows;
      maxsc = showsc > curcol ? showsc : curcol;

      strcpy(r, coltoa(minsc));
      strcat(r, ":");
      strcat(r, coltoa(maxsc));

      toggle_navigate_mode();
      ins_string(r);
    } else {
      minsr = showsr < currow ? showsr : currow;
      minsc = showsc < curcol ? showsc : curcol;
      maxsr = showsr > currow ? showsr : currow;
      maxsc = showsc > curcol ? showsc : curcol;

      toggle_navigate_mode();
      ins_string(r_name(minsr, minsc, maxsr, maxsc));
    }
    showrange = 0;
}

/* dot command functions.  Saves info so we can redo on a '.' command */

static void
savedot(int c)
{
    if (do_dot || nosavedot || (c == '\n'))
      return;

    if (doti == 0) dotarg = arg;
    if (doti < DOTLEN-1) {
      if (c > 255) {
          if (doti < DOTLEN-2) {
            dotb[doti++] = c/256;
            c %= 256;
          } else
            return;
      }
      dotb[doti++] = c;
      dotb[doti] = '\0';
    }
}

static int dotcalled = 0;

static void
dotcmd()
{
    int c;

    if (dotcalled)      /* stop recursive calling of dotcmd() */
      return;
    do_dot = 1;
    doti = 0;
    if (arg == 1)
      arg = dotarg;
    else
      dotarg = arg;
    while (dotb[doti] != '\0') {
      if ((c = dotb[doti++]) < 4)
          c = c * 256 + dotb[doti++];
      dotcalled = 1;
      write_line(c);
    }
    do_dot = 0;
    doti = 0;
    dotcalled = 0;
}

int
vigetch()
{
    int c;

    if (do_dot) {
      if (dotb[doti] != '\0') {
          return (dotb[doti++]);
      } else {
          do_dot = 0;
          doti = 0;
          return (nmgetch());
      }
    }
    update(1);
    c = nmgetch();
    return (c);
}

/* saves the current line for possible use by an undo cmd */
static void
u_save(int c)
{
    static unsigned     undolen = 0;

    if (strlen(line)+1 > undolen) {
      undolen = strlen(line)+40;

      undo_line = scxrealloc(undo_line, undolen);
    }
    (void) strcpy(undo_line, line);

    undo_lim = linelim;

    /* reset dot command if not processing it. */

    if (!do_dot) {
        doti = 0;
      savedot(c);
    }
}

/* Restores the current line saved by u_save() */
static void
restore_it()
{
    static char         *tempc = NULL;
    static unsigned     templen = 0;
    int                 tempi;

    if ((undo_line == NULL) || (*undo_line == '\0')) 
      return;

    if (strlen(line)+1 > templen) {
      templen = strlen(line) + 40;
      tempc = scxrealloc(tempc, templen);
    }

    strcpy(tempc, line);
    tempi = linelim;
    (void) strcpy(line, undo_line);
    linelim = undo_lim;
    strcpy(undo_line, tempc);
    undo_lim = tempi;
}

/* This command stops the editing process. */
static void
stop_edit()
{
    if (search_ind != ' ') {
      search_ind = ' ';
      (void) strcpy(line, history[0].histline);
      write_line('G');
    } else {
      showrange = 0;
      numeric_field = 0;
      linelim = -1;
      (void) move(1, 0);
      (void) clrtoeol();
    }
}

/*
 * Motion commands.  Forward motion commands take an argument
 * which, when set, cause the forward motion to continue onto
 * the null at the end of the line instead of stopping at the
 * the last character of the line.
 */
static int
for_line(int arg, int stop_null)
{
    int cpos = linelim;

    if (linelim < 0)
      return (linelim);
    else if (linelim + arg <= strlen(line))
      cpos += arg;
    else
      cpos = strlen(line);

    if (cpos == strlen(line) && cpos > 0 && !stop_null)
      return (cpos - 1);
    else
      return (cpos);
}

/* If end_word is non-zero, go to next end-of-word.  Otherwise, go to next
 * beginning-of-word.
 */

static int
for_word(int arg, int end_word, int big_word, int stop_null)
{
    register int c;
    register int cpos;

    cpos = linelim;

    while (cpos < strlen(line) && arg--) {
      if (end_word)
          cpos++;

      if (line[cpos] == ' ') {
          while (line[cpos] == ' ')
            cpos++;
          if (cpos > 0 && line[cpos] == '\0')
            --cpos;
          if (!end_word)
            continue;
      }

      if (big_word)
          while ((c = line[cpos]) && c != ' ')
            cpos++;
      else if (istext(line[cpos])) {
          while ((c = line[cpos]) && istext(c)) 
            cpos++;
      } else {
          while ((c = line[cpos]) && !istext(c) && c != ' ')
            cpos++;
      }

      if (end_word)
          cpos--;
      else while (line[cpos] == ' ')
          cpos++;

      if (cpos > 0 && line[cpos] == '\0' && !stop_null) 
          --cpos;
    }

    return (cpos);
}

static int
back_line(int arg)
{
    if (linelim > arg)
        return (linelim - arg);
    else
      return (0);
}

static int
back_word(int arg, int big_word)
{
    register int c;
    register int cpos;

    cpos = linelim;

    while (cpos > 0 && arg--) {
      if (line[cpos] == ' ') {
          /* Skip white space */
          while (cpos > 0 && line[cpos] == ' ')
            --cpos;
      } else if (cpos > 0 && (line[cpos-1] == ' ' 
                  || ( istext(line[cpos]) && !istext(line[cpos-1]))
                  || (!istext(line[cpos]) &&  istext(line[cpos-1])))) {
          /* Started on the first char of a word - back up to prev. word */
          --cpos;
          while (cpos > 0 && line[cpos] == ' ')
            --cpos;
      }

      /* Skip across the word - goes 1 too far */
      if (big_word)
          while (cpos > 0 && (c = line[cpos]) && c != ' ')
            --cpos;
      else if (istext(line[cpos])) {
          while (cpos > 0 && (c = line[cpos]) && istext(c)) 
            --cpos;
      } else {
          while (cpos > 0 && (c = line[cpos]) && !istext(c) && c != ' ')
            --cpos;
      }

      /* We are done - fix up the one too far */
      if (cpos > 0 && line[cpos] && line[cpos+1]) 
          cpos++;
    }

    return(cpos);
}

/* Text manipulation commands */

/* If back_null is set, back up if the deletion leaves you at the null
 * line terminator.  Otherwise, don't.
 */
static void
del_in_line(int arg, int back_null)
{
    register int len, i;

    if (linelim >= 0) {
      len = strlen(line);
      if (arg > len - linelim)
          arg = len - linelim;
      if (linelim == len && linelim > 0)
          linelim--;
      strncpy(putbuf, line + linelim, arg);
      putbuf[arg] = '\0';
      for (i = linelim; i < len; i++)
          line[i] = line[i+arg];
    }
    if (back_null && linelim > 0 && line[linelim] == '\0')
      --linelim;
}

void
ins_in_line(int c)
{
    int i, len;
    static int inabbr;

    if (c < 256) {
      if (linelim < 0 && c > 0) {
          *line = '\0';
          linelim = 0;
      }
      if (!inabbr && linelim > 0 && !(isalpha(c) || isdigit(c) || c == '_')) {
          inabbr++;
          doabbrev();
          inabbr--;
      }
      if (c > 0) {
          len = strlen(line);
          for (i = len; i >= linelim; --i)
            line[i+1] = line[i];
          line[linelim++] = c;
          line[len+1] = '\0';
      }
    }
}

void
ins_string(char *s)
{
    while (*s)
      ins_in_line(*s++);
}

void
doabbrev()
{
    int len, pos;
    struct abbrev *a;
    struct abbrev *prev;
    
    if (!(isalpha(line[linelim-1]) || isdigit(line[linelim-1]) ||
          line[linelim-1] == '_') || !(mode == INSERT_MODE ||
          mode == SEARCH_MODE) || istart >= linelim)
      return;

    pos = linelim - 2;
    if (isalpha(line[pos]) || isdigit(line[pos]) || line[pos] == '_') {
      for (; pos >= istart; pos--)
          if (!(isalpha(line[pos]) || isdigit(line[pos]) || line[pos] == '_'))
            break;
    } else if (line[pos] != ' ')
      for (; pos >= istart; pos--)
          if (isalpha(line[pos]) || isdigit(line[pos]) || line[pos] == '_' ||
                line[pos] == ' ')
            break;
    pos++;

    if (istart && pos == istart) {
      if (isalpha(line[pos]) || isdigit(line[pos]) || line[pos] == '_') {
          if (isalpha(line[--pos]) || isdigit(line[pos]) || line[pos] == '_')
            return;
      } else {
          if (!(isalpha(line[--pos]) || isdigit(line[pos]) ||
                line[pos] == '_' || line[pos] == ' '))
            return;
      }
      pos++;
    }

    len = linelim - pos;
    if (len && (a = find_abbr(line + pos, len, &prev))) {
      if (len > 1 || pos == 0 || line[pos-1] == ' ') {
          linelim = pos;
          del_in_line(len, 0);
          ins_string(a->exp);
      }
    }
}

static void
append_line()
{
    register int i;

    i = linelim;
    if (i >= 0 && line[i])
      linelim++;
    insert_mode();
}

static void
change_case(arg)
{
    if (linelim < 0) {
      linelim = 0;
      *line = '\0';
    }
    while (arg--) {
      if (islower(line[linelim]))
          line[linelim] = toupper(line[linelim]);
      else if (isupper(line[linelim]))
          line[linelim] = tolower(line[linelim]);
      linelim = for_line(1, 0);
    }
}

static void
rep_char()
{
    int c;

    if (linelim < 0) {
      linelim = 0;
      *line = '\0';
    }
    c = vigetch();
    savedot(c);
    if (c < 256 && c!= ESC && c != ctl('g')) {
      if (line[linelim] == '\0')
          line[linelim+1] = '\0';
      line[linelim] = c;
    }
}

static void
replace_in_line(int c)
{
    register int len;

    if (c < 256) {
      if (linelim < 0) {
          linelim = 0;
          *line = '\0';
      }
      len = strlen(line);
      line[linelim++] = c;
      if (linelim > len)
          line[linelim] = '\0';
    }
}

static void
back_space()
{
    if (linelim == 0)
      return;

    if (line[linelim] == '\0') {
      linelim = back_line(1);
      del_in_line(1, 1);
      linelim = strlen(line);
    } else {
      linelim = back_line(1);
      del_in_line(1, 1);
    }
    if (linelim < istart)
      istart = linelim;
}

/* Setting change to 1 makes `w' act like `e' so that `cw' will act like
 * `ce', just as in vi.  Setting change to 0 causes `w' to act as usual.
 */

int
get_motion(int change)
{
    int c;
    int arg2 = 0;

    c = vigetch();
    if (c == '0') {
      savedot(c);
      return (0);
    }
    while (c >= '0' && c <= '9') {
      arg2 = 10 * arg2 + c - '0';
      c = vigetch();
    }
    if (!arg2)
      arg2++;
    arg *= arg2;
    if (!nosavedot) {
      savedot(c);
      dotarg = arg;
    }
    switch (c) {
      case '$':   return (strlen(line));
      case 'b':   return (back_word(arg, 0));
      case 'B':   return (back_word(arg, 1));
      case 'c':   return (change ? -1 : linelim);
      case 'd':   return (!change ? -1 : linelim);
      case 'e':   return (for_word(arg, 1, 0, 1) + 1);
      case 'E':   return (for_word(arg, 1, 1, 1) + 1);
      case 'f':   return ((c = find_char(arg, 1)) == linelim ? c : c + 1);
      case 'F':   return (find_char(arg, -1));
      case 'h':   return (back_line(arg));
      case 'l':   return (for_line(arg, 1));
      case 't':   return ((c = to_char(arg, 1)) == linelim ? c : c + 1);
      case 'T':   return (to_char(arg, -1));
      case 'w':   return (for_word(arg, change, 0, 1) + change);
      case 'W':   return (for_word(arg, change, 1, 1) + change);
      default:    return (linelim);
    }
}

static void
yank_cmd(int delete, int change)
{
    int cpos;

    if ((cpos = get_motion(change)) == -1) {
      cpos++;
      linelim = strlen(line);
    }
    yank_chars(cpos, linelim, delete);
}

static void
yank_chars(register int first, register int last, int delete)
{
    int temp;

    if (first == last)
      return;

    if (last < first) {
      temp = last; last = first; first = temp;
    }

    linelim = first;
    strncpy(putbuf, line + first, last - first);
    putbuf[last - first] = '\0';
    if (delete)
      memmove(line + first, line + last, strlen(line + last) + 1);
}

static void
del_to_end()
{
    if (linelim < 0)
      return;
    strcpy(putbuf, line + linelim);
    line[linelim] = '\0';
    linelim = back_line(1);
}

static void
cr_line()
{
    struct frange *fr;

    ins_in_line(0);
    insert_mode();
    numeric_field = 0;
    if (linelim == -1) {      /* '\n' alone will put you into insert mode */
      *line = '\0';           /* unless numeric and craction are both   set */
      linelim = 0;
      if (numeric && craction)
          cellassign = 1;
      else
          return;
    }
    save_hist();
    nosavedot = 1;
    linelim = 0;
    (void) yyparse();
    showrange = 0;
    linelim = -1;
    if (cellassign) {
      cellassign = 0;
      switch (craction) {
          case CRROWS:
            if ((rowlimit >= 0) && (currow >= rowlimit)) {
                forwcol(1);
                currow = 0;
            } else {
                if ((fr = find_frange(currow, curcol))) {
                  forwrow(1);
                  if (currow > fr->ir_right->row) {
                      backrow(1);
                      if (autowrap) {
                        forwcol(1);
                        currow = fr->ir_left->row;
                        if (row_hidden[currow])
                            forwrow(1);
                        if (curcol > fr->ir_right->col) {
                            backcol(1);
                            if (autoinsert)
                              insertcol(1, 1);
                            else {
                              currow = fr->ir_right->row;
                              if (row_hidden[currow])
                                  backrow(1);
                            }
                        }
                      } else if (autoinsert)
                        insertrow(1, 1);
                  }
                } else
                  forwrow(1);
            }
            break;
          case CRCOLS:
            if ((collimit >= 0) && (curcol >= collimit)) {
                forwrow(1);
                curcol = 0;
            } else {
                if ((fr = find_frange(currow, curcol))) {
                  forwcol(1);
                  if (curcol > fr->ir_right->col) {
                      backcol(1);
                      if (autowrap) {
                        forwrow(1);
                        curcol = fr->ir_left->col;
                        if (col_hidden[curcol])
                            forwcol(1);
                        if (currow > fr->ir_right->row) {
                            backrow(1);
                            if (autoinsert)
                              insertrow(1, 1);
                            else {
                              curcol = fr->ir_right->col;
                              if (col_hidden[curcol])
                                  backcol(1);
                            }
                        }
                      } else if (autoinsert)
                        insertcol(1, 1);
                  }
                } else
                  forwcol(1);
            }
            break;
          default:
            break;
      }
    }
}

void
doshell()
{
    /*
    *  "! command"  executes command
    *  "!"  forks a shell
    *  "!!" repeats last command
    */
#if VMS || MSDOS
    error("Not implemented on VMS or MS-DOS");
#else /* VMS */
    char *shl;
    int pid, temp;
    char cmd[MAXCMD];
    static char lastcmd[MAXCMD];

    if (!(shl = getenv("SHELL")))
      shl = "/bin/sh";

    deraw(1);
    (void) fputs("! ", stdout);
    (void) fflush(stdout);
    (void) fgets(cmd, MAXCMD, stdin);
    cmd[strlen(cmd) - 1] = '\0';    /* clobber \n */
    if (strcmp(cmd,"!") == 0)       /* repeat? */
      (void) strcpy(cmd, lastcmd);
    else
      (void) strcpy(lastcmd, cmd);

    if (modflg) {
      (void) puts("[No write since last change]");
      (void) fflush(stdout);
    }

    if (!(pid = fork())) {
      (void) signal(SIGINT, SIG_DFL);  /* reset */
      if (strlen(cmd))
          (void) execl(shl, shl, "-c", cmd, NULL);
      else
          (void) execl(shl, shl, NULL);
      exit (-127);
    }

    while (pid != wait(&temp));

    (void) printf("Press any key to continue ");
    fflush(stdout);
    cbreak();
    (void)getch();
    goraw();
    clear();
#endif /* VMS */
}

/* History functions */

static void
save_hist()
{
    if (!lasthist || strcmp(history[lasthist].histline, line)) {
      if (lasthist < 0)
          lasthist = 1;
      else
          lasthist = lasthist % HISTLEN + 1;

      if (lasthist > endhist)
          endhist = lasthist;

      if (history[lasthist].len < strlen(line) + 1) {
          history[lasthist].len = strlen(line) + 40;
          history[lasthist].histline = scxrealloc(history[lasthist].histline,
                history[lasthist].len);
      }
      (void) strcpy(history[lasthist].histline, line);
      histsessionnew++;
    }
    if (history[0].histline) {
      scxfree(history[0].histline);
      history[0].histline = NULL;
      history[0].len = 0;
    }
    histp = 0;
}

static void
for_hist()
{
    if (histp == 0) {
      last_col();
      return;
    }

    if (histp == lasthist)
      histp = 0;
    else
      histp = histp % endhist + 1;

    if (lasthist >= 0) {
      (void) strcpy(line, history[histp].histline);
      last_col();
    }
    if (histp)
      error("History line %d", endhist - lasthist + histp);
    else
      error("");
}

static void
back_hist()
{
    if (histp == 0) {
      if (history[0].len < strlen(line) + 1) {
          history[0].len = strlen(line) + 40;
          history[0].histline = scxrealloc(history[0].histline,
                history[0].len);
      }
      (void) strcpy(history[0].histline, line);

      if (lasthist >= 0)
          histp = lasthist;
    } else if (histp == 1) {
      if (endhist != lasthist)
          histp = endhist;
    } else if (histp != ((lasthist + 1) % (endhist + 1)))
      histp--;

    if (lasthist >= 0) {
      (void) strcpy(line, history[histp].histline);
      last_col();
    }
    if (histp)
      error("History line %d", endhist - lasthist + histp);
    else
      error("");
}

static void
search_hist()
{
#ifdef RECOMP
    char    *tmp;
#endif
#if !defined(REGCOMP) && !defined(RE_COMP) && !defined(REGCMP)
    static  unsigned lastsrchlen = 0;
#endif

    if (linelim < 1) {
      linelim = 0;
      edit_mode();
      return;
    }

#ifdef REGCOMP
    if (last_search)
      regfree(last_search);
    else
      last_search = &preg;
    if ((errcode = regcomp(last_search, line, REG_EXTENDED))) {
      char *tmp = scxmalloc((size_t)160);
      regerror(errcode, last_search, tmp, sizeof(tmp));
      error(tmp);
      scxfree(tmp);
      return;
    }
#else
#ifdef RE_COMP
    if ((tmp = re_comp(line)) != NULL) {
      error(tmp);
      return;
    }
#else
#ifdef REGCMP
    free(last_search);
    if ((last_search = regcmp(line, NULL)) == NULL) {
      error("Invalid search string");
      return;
    }
#else
    if (strlen(line) + 1 > lastsrchlen) {
      lastsrchlen = strlen(line) + 40;
      last_search = scxrealloc(last_search, lastsrchlen);
    }
    (void) strcpy(last_search, line);
#endif
#endif
#endif
    (void) strcpy(line, history[0].histline);
    search_again(false);
    if (mode != EDIT_MODE) edit_mode();
    search_ind = ' ';
}

static void
search_again(bool reverse)
{
    int prev_match;
    int found_it=0;
#if !defined(REGCOMP) && !defined(RE_COMP) && !defined(REGCMP)
    char *look_here;
    int do_next;
#endif

#ifdef REGCOMP
    if ((last_search == NULL))
      return;
#else
#ifndef RE_COMP
    if ((last_search == NULL) || (*last_search == '\0'))
      return;
#endif
#endif
    prev_match = histp > 0 ? histp : 0;
    error("");

    do {
      if (lasthist > 0) {
          if (!(search_dir ^ reverse) && histp != lasthist)
            if (histp <= 0) {
                histp = ((lasthist + 1) % endhist);
                (void) strcpy(line, history[histp].histline);
            } else
                for_hist();
          else if ((search_dir ^ reverse) && histp != ((lasthist + 1) % endhist))
            back_hist();
          else {
            histp = 0;
            (void) strcpy(line, history[0].histline);
            last_col();
          }
      } else
          break;
      if (histp == prev_match) {
          if (histp <= 0) {
            error("No matches found");
            break;
          }
      }
      if (histp <= 0) {
          if (search_dir ^ reverse)
            back_hist();
          else
            histp = ((lasthist + 1) % endhist);
            (void) strcpy(line, history[histp].histline);
      }
      found_it = 0;
#ifdef REGCOMP
      if (regexec(last_search, line, 0, NULL, 0) == 0)
          found_it++;
#else
#ifdef RE_COMP
      if (re_exec(line) != 0)
          found_it++;
#else
#ifdef REGCMP
      if (regex(last_search, line) != NULL)
          found_it++;
#else
      look_here = line;
      do_next = 0;
      while ((look_here = (char *)strchr(look_here, *last_search)) != NULL &&
            !found_it && !do_next) {

          if (strncmp(look_here, last_search, strlen(last_search)) == 0)
            found_it++;
          else if (look_here < line + strlen(line) - 1)
              look_here++;
          else
            do_next++;
      }
#endif
#endif
#endif
      if (histp == prev_match)
          break;
    } while (!found_it);
    if (found_it)
      error("History line %d", endhist - lasthist + histp);
    else
      error("No matches found");
    edit_mode();
    linelim = strlen(line) - 1;
}

#if !defined(MSDOS) && defined HISTORY_FILE
void
write_hist()
{
    int i;
    FILE *fp, *tmpfp = NULL;
    char histfile[PATHLEN];

    if (histsessionnew < HISTLEN) {
      /* write the new history for this session to a tmp file */
      tmpfp = tmpfile();
      for (i = 1; i <= histsessionnew; i++) {
          histsessionstart = histsessionstart % endhist + 1;
          if (history[histsessionstart].len > 40)
            fprintf(tmpfp, "%s\n", history[histsessionstart].histline);
      }
      fseek(tmpfp, 0, SEEK_SET);

      /* re-read the main history, then read back in the saved session hist*/
      histp = 0;
      lasthist = 0;
      endhist = -1;
      read_hist();
      readhistfile(tmpfp);
    }

    /* now write to whole lot out to the proper save file */
    strcpy(histfile, HISTORY_FILE);
    if (findhome(histfile) && (fp = fopen(histfile, "w")) != NULL) {
      for (i = 1; i <= endhist; i++) {
          lasthist = lasthist % endhist + 1;
          if (history[lasthist].len > 40)
            fprintf(fp, "%s\n", history[lasthist].histline);
      }
      fclose(fp);
    }
}

static void
readhistfile(FILE *fp)
{
    while (fgets(line, FBUFLEN, fp)) {
      line[strlen(line)-1] = '\0'; /* chop the \n */
      save_hist();
    }
    fclose(fp);
}

void
read_hist()
{
    FILE *fp;
    char histfile[PATHLEN];

    strcpy(histfile, HISTORY_FILE);
    if (findhome(histfile) && (fp = fopen(histfile, "r")) != NULL)
      readhistfile(fp);
    histsessionstart = lasthist;
    histsessionnew = 0;
}
#endif

static void
col_0()
{
    linelim = 0;
}

static void
last_col()
{
    linelim = strlen(line);
    if (linelim > 0 && mode_ind == 'e')
      --linelim;
}

static int
find_char(int arg, int n)
{
    register int i;

    if (findchar)
      finddir = n;
    findchar = vigetch();
    switch (dotb[doti - 1]) {
      case 'f': case 'F': case 't': case 'T':
          savedot(findchar);
      default:
          break;
    }
    i = linelim;
    while (arg--) {
      i += n;
      while (i >= 0 && line[i] && line[i] != findchar)
          i += n;
      if (i < 0 || !line[i]) {
          i = linelim;
          break;
      }
    }
    findfunc = 'f';
    return (i);
}

static int
to_char(int arg, int n)
{
    register int i;
    int tmp = linelim;

    if (linelim + n >= 0 && linelim + n < strlen(line))
      linelim += n;
    i = find_char(arg, n);
    if (i != linelim)
      i -= n;
    linelim = tmp;
    findfunc = 't';

    return (i);
}

static void
match_paren()
{
    /*register int i;*/
    int nest = 1;
    int tmp = linelim;

    if (line[linelim] == '(') {
      while (nest && ++linelim >= 0 && line[linelim]) {
          if (line[linelim] == '(')
            nest++;
          else if (line[linelim] == ')')
            nest--;
      }
      if (line[linelim] != ')')
          linelim = tmp;
    }
    else if (line[linelim] == ')') {
      while (nest && --linelim >= 0 && line[linelim]) {
          if (line[linelim] == ')')
            nest++;
          else if (line[linelim] == '(')
            nest--;
      }
      if (line[linelim] != '(')
          linelim = tmp;
    }
}

/* If save is 0, remember the current position.  Otherwise, if the current
 * cell has changed since the last remember(0), save the remembered location
 * for the `, ', and c comands.
 */
void
remember(int save)
{
    static int remrow, remcol, remstrow, remstcol;

    if (save && (currow != remrow || curcol != remcol ||
          strow != remstrow || stcol != remstcol)) {
      savedrow[0] = remrow;
      savedcol[0] = remcol;
      savedstrow[0] = remstrow;
      savedstcol[0] = remstcol;
    } else {
      remrow = currow;
      remcol = curcol;
      remstrow = strow;
      remstcol = stcol;
    }
}

void
gohome()
{
    struct frange *fr;

    remember(0);
    if ((fr = find_frange(currow, curcol))) {
      if (currow >= fr->ir_left->row &&
            currow <= fr->ir_right->row &&
            curcol >= fr->ir_left->col &&
            curcol <= fr->ir_right->col &&
            (currow > fr->ir_left->row ||
            curcol > fr->ir_left->col)) {
          currow = fr->ir_left->row;
          curcol = fr->ir_left->col;
      } else if (currow > fr->or_left->row ||
            curcol > fr->or_left->col) {
          currow = fr->or_left->row;
          curcol = fr->or_left->col;
      } else {
          currow = 0;
          curcol = 0;
      }
    } else {
      currow = 0;
      curcol = 0;
    }
    rowsinrange = 1;
    colsinrange = fwidth[curcol];
    remember(1);
    FullUpdate++;
}

void
leftlimit()
{
    struct frange *fr;

    remember(0);
    if ((fr = find_frange(currow, curcol))) {
      if (currow >= fr->ir_left->row &&
            currow <= fr->ir_right->row &&
            curcol > fr->ir_left->col &&
            curcol <= fr->ir_right->col)
          curcol = fr->ir_left->col;
      else if (curcol > fr->or_left->col &&
            curcol <= fr->or_right->col)
          curcol = fr->or_left->col;
      else
          curcol = 0;
    } else
      curcol = 0;
    rowsinrange = 1;
    colsinrange = fwidth[curcol];
    remember(1);
}

void
rightlimit()
{
    register struct ent *p;
    struct frange *fr;

    remember(0);
    if ((fr = find_frange(currow, curcol))) {
      if (currow >= fr->ir_left->row &&
            currow <= fr->ir_right->row &&
            curcol >= fr->ir_left->col &&
            curcol < fr->ir_right->col)
          curcol = fr->ir_right->col;
      else if (curcol >= fr->or_left->col &&
            curcol < fr->or_right->col)
          curcol = fr->or_right->col;
      else {
          curcol = maxcols - 1;
          while (!VALID_CELL(p, currow, curcol) &&
                curcol > fr->or_right->col)
            curcol--;
          if ((fr = find_frange(currow, curcol)))
            curcol = fr->or_right->col;
      }
    } else {
      curcol = maxcols - 1;
      while (!VALID_CELL(p, currow, curcol) && curcol > 0)
          curcol--;
      if ((fr = find_frange(currow, curcol)))
          curcol = fr->or_right->col;
    }
    rowsinrange = 1;
    colsinrange = fwidth[curcol];
    remember(1);
}

void
gototop()
{
    struct frange *fr;

    remember(0);
    if ((fr = find_frange(currow, curcol))) {
      if (curcol >= fr->ir_left->col &&
            curcol <= fr->ir_right->col &&
            currow > fr->ir_left->row &&
            currow <= fr->ir_right->row)
          currow = fr->ir_left->row;
      else if (currow > fr->or_left->row &&
            currow <= fr->or_right->row)
          currow = fr->or_left->row;
      else
          currow = 0;
    } else
      currow = 0;
    rowsinrange = 1;
    colsinrange = fwidth[curcol];
    remember(1);
}

void
gotobottom()
{
    register struct ent *p;
    struct frange *fr;

    remember(0);
    if ((fr = find_frange(currow, curcol))) {
      if (curcol >= fr->ir_left->col &&
            curcol <= fr->ir_right->col &&
            currow >= fr->ir_left->row &&
            currow < fr->ir_right->row)
          currow = fr->ir_right->row;
      else if (currow >= fr->or_left->row &&
            currow < fr->or_right->row)
          currow = fr->or_right->row;
      else {
          currow = maxrows - 1;
          while (!VALID_CELL(p, currow, curcol) &&
                currow > fr->or_right->row)
            currow--;
          if ((fr = find_frange(currow, curcol)))
            currow = fr->or_right->row;
      }
    } else {
      currow = maxrows - 1;
      while (!VALID_CELL(p, currow, curcol) && currow > 0)
          currow--;
      if ((fr = find_frange(currow, curcol)))
          currow = fr->or_right->row;
    }
    rowsinrange = 1;
    colsinrange = fwidth[curcol];
    remember(1);
}

static void
dogoto()
{
    static char         *tempc = NULL;
    static unsigned     templen = 0;
    int                 tempi;

    if (strlen(line) + 1 > templen) {
      templen = strlen(line) + 40;
      tempc = scxrealloc(tempc, templen);
    }

    strcpy(tempc, line);
    tempi = linelim;

    /* Can't switch back to navigate mode if insert_mode() is used here
     * instead of toggle_navigate_mode(), which is what we want when doing
     * a goto from within navigate mode.
     */
    insert_mode();
    /* Tempted as I was, I had to resist making this "Where would you like
     * to go today?" - CRM :)
     */
    query("goto where?", NULL);
    if (linelim >= 0) {
      memmove(line + 5, line, strlen(line) + 1);
      strncpy(line, "goto ", 5);
      linelim = 0;
      yyparse();
    }

    strcpy(line, tempc);
    linelim = tempi;
    /* Now we need to change back to navigate mode ourselves so that
     * toggle_navigate_mode() will work properly again.
     */
    mode_ind = 'v';
    mode = NAVIGATE_MODE;
    if (!showrange)
      toggle_navigate_mode();
}

void
query(char *s, char *data)
{
    int c;

    insert_mode();
    if (data != NULL) {
      strcpy(line, data);
      linelim = strlen(line);
    } else {
      *line = '\0';
      linelim = 0;
    }
    if (s != NULL) error(s);

    while (linelim >= 0) {
      update(0);
      switch (c = nmgetch()) {
          case ctl('m'):
            error("");
            return;
          case ctl('g'):
            line[0] = '\0';
            linelim = -1;
            error("");
            update(0);
            return;
          case ctl('l'):
            FullUpdate++;
            clearok(stdscr,1);
            update(1);
            break;
          default:
            write_line(c);
      }
    }
}

Generated by  Doxygen 1.6.0   Back to index