home *** CD-ROM | disk | FTP | other *** search
- /* vcmd.c */
-
- /* Author:
- * Steve Kirkendall
- * 14407 SW Teal Blvd. #C
- * Beaverton, OR 97005
- * kirkenda@cs.pdx.edu
- */
-
-
- /* This file contains the functions that handle VI commands */
-
-
- #include "config.h"
- #include "ctype.h"
- #include "vi.h"
- #if MSDOS
- # include <process.h>
- # include <string.h>
- #endif
- #if TOS
- # include <osbind.h>
- # include <string.h>
- #endif
- #if OSK
- # include <stdio.h>
- #endif
-
-
- /* This function puts the editor in EX mode */
- MARK v_quit()
- {
- move(LINES - 1, 0);
- mode = MODE_EX;
- return cursor;
- }
-
- /* This function causes the screen to be redrawn */
- MARK v_redraw()
- {
- redraw(MARK_UNSET, FALSE);
- return cursor;
- }
-
- /* This function executes a string of EX commands, and waits for a user keystroke
- * before returning to the VI screen. If that keystroke is another ':', then
- * another EX command is read and executed.
- */
- /*ARGSUSED*/
- MARK v_1ex(m, text)
- MARK m; /* the current line */
- char *text; /* the first command to execute */
- {
- /* run the command. be careful about modes & output */
- exwrote = (mode == MODE_COLON);
- doexcmd(text);
- exrefresh();
-
- /* if mode is no longer MODE_VI, then we should quit right away! */
- if (mode != MODE_VI && mode != MODE_COLON)
- return cursor;
-
- /* The command did some output. Wait for a keystoke. */
- if (exwrote)
- {
- mode = MODE_VI;
- msg("[Hit <RETURN> to continue]");
- if (getkey(0) == ':')
- { mode = MODE_COLON;
- addch('\n');
- }
- else
- redraw(MARK_UNSET, FALSE);
- }
-
- return cursor;
- }
-
- /* This function undoes the last change */
- /*ARGSUSED*/
- MARK v_undo(m)
- MARK m; /* (ignored) */
- {
- if (undo())
- {
- redraw(MARK_UNSET, FALSE);
- }
- return cursor;
- }
-
- /* This function deletes the character(s) that the cursor is on */
- MARK v_xchar(m, cnt, cmd)
- MARK m; /* where to start deletions */
- long cnt; /* number of chars to delete */
- int cmd; /* either 'x' or 'X' */
- {
- DEFAULT(1);
-
- /* for 'X', adjust so chars are deleted *BEFORE* cursor */
- if (cmd == 'X')
- {
- if (markidx(m) < cnt)
- return MARK_UNSET;
- m -= cnt;
- }
-
- /* make sure we don't try to delete more thars than there are */
- pfetch(markline(m));
- if (markidx(m + cnt) > plen)
- {
- cnt = plen - markidx(m);
- }
- if (cnt == 0L)
- {
- return MARK_UNSET;
- }
-
- /* do it */
- ChangeText
- {
- cut(m, m + cnt);
- delete(m, m + cnt);
- }
- return m;
- }
-
- /* This function defines a mark */
- /*ARGSUSED*/
- MARK v_mark(m, count, key)
- MARK m; /* where the mark will be */
- long count; /* (ignored) */
- int key; /* the ASCII label of the mark */
- {
- if (key < 'a' || key > 'z')
- {
- msg("Marks must be from a to z");
- }
- else
- {
- mark[key - 'a'] = m;
- }
- return m;
- }
-
- /* This function toggles upper & lower case letters */
- MARK v_ulcase(m, cnt)
- MARK m; /* where to make the change */
- long cnt; /* number of chars to flip */
- {
- REG char *pos;
- REG int j;
-
- DEFAULT(1);
-
- /* fetch the current version of the line */
- pfetch(markline(m));
-
- /* for each position in the line */
- for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++)
- {
- if (isupper(*pos))
- {
- tmpblk.c[j] = tolower(*pos);
- }
- else
- {
- tmpblk.c[j] = toupper(*pos);
- }
- }
-
- /* if the new text is different from the old, then change it */
- if (strncmp(tmpblk.c, &ptext[markidx(m)], j))
- {
- ChangeText
- {
- tmpblk.c[j] = '\0';
- change(m, m + j, tmpblk.c);
- }
- }
-
- return m + j;
- }
-
-
- MARK v_replace(m, cnt, key)
- MARK m; /* first char to be replaced */
- long cnt; /* number of chars to replace */
- int key; /* what to replace them with */
- {
- REG char *text;
- REG int i;
-
- DEFAULT(1);
-
- /* map ^M to '\n' */
- if (key == '\r')
- {
- key = '\n';
- }
-
- /* make sure the resulting line isn't too long */
- if (cnt > BLKSIZE - 2 - markidx(m))
- {
- cnt = BLKSIZE - 2 - markidx(m);
- }
-
- /* build a string of the desired character with the desired length */
- for (text = tmpblk.c, i = cnt; i > 0; i--)
- {
- *text++ = key;
- }
- *text = '\0';
-
- /* make sure cnt doesn't extend past EOL */
- pfetch(markline(m));
- key = markidx(m);
- if (key + cnt > plen)
- {
- cnt = plen - key;
- }
-
- /* do the replacement */
- ChangeText
- {
- change(m, m + cnt, tmpblk.c);
- }
-
- if (*tmpblk.c == '\n')
- {
- return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE;
- }
- else
- {
- return m + cnt - 1;
- }
- }
-
- MARK v_overtype(m)
- MARK m; /* where to start overtyping */
- {
- MARK end; /* end of a substitution */
- static long width; /* width of a single-line replace */
-
- /* the "doingdot" version of replace is really a substitution */
- if (doingdot)
- {
- /* was the last one really repeatable? */
- if (width < 0)
- {
- msg("Can't repeat a multi-line overtype command");
- return MARK_UNSET;
- }
-
- /* replacing nothing by nothing? Don't bother */
- if (width == 0)
- {
- return m;
- }
-
- /* replace some chars by repeated text */
- return v_subst(m, width);
- }
-
- /* Normally, we input starting here, in replace mode */
- ChangeText
- {
- end = input(m, m, WHEN_VIREP, FALSE);
- }
-
- /* if we ended on the same line we started on, then this
- * overtype is repeatable via the dot key.
- */
- if (markline(end) == markline(m) && end >= m - 1L)
- {
- width = end - m + 1L;
- }
- else /* it isn't repeatable */
- {
- width = -1L;
- }
-
- return end;
- }
-
-
- /* This function selects which cut buffer to use */
- /*ARGSUSED*/
- MARK v_selcut(m, cnt, key)
- MARK m;
- long cnt;
- int key;
- {
- cutname(key);
- return m;
- }
-
- /* This function pastes text from a cut buffer */
- /*ARGSUSED*/
- MARK v_paste(m, cnt, cmd)
- MARK m; /* where to paste the text */
- long cnt; /* (ignored) */
- int cmd; /* either 'p' or 'P' */
- {
- MARK dest;
-
- ChangeText
- {
- /* paste the text, and find out where it ends */
- dest = paste(m, cmd == 'p', TRUE);
-
- /* was that a line-mode paste? */
- if (dest && markline(dest) != markline(m))
- {
- /* line-mode pastes leave the cursor at the front
- * of the first pasted line.
- */
- dest = m;
- if (cmd == 'p')
- {
- dest += BLKSIZE;
- }
- force_flags |= FRNT;
- }
- }
- return dest;
- }
-
- /* This function yanks text into a cut buffer */
- MARK v_yank(m, n)
- MARK m, n; /* range of text to yank */
- {
- cut(m, n);
- return m;
- }
-
- /* This function deletes a range of text */
- MARK v_delete(m, n)
- MARK m, n; /* range of text to delete */
- {
- /* illegal to try and delete nothing */
- if (n <= m)
- {
- return MARK_UNSET;
- }
-
- /* Do it */
- ChangeText
- {
- cut(m, n);
- delete(m, n);
- }
- return m;
- }
-
-
- /* This starts input mode without deleting anything */
- MARK v_insert(m, cnt, key)
- MARK m; /* where to start (sort of) */
- long cnt; /* repeat how many times? */
- int key; /* what command is this for? {a,A,i,I,o,O} */
- {
- int wasdot;
- long reps;
- int above; /* boolean: new line going above old line? */
-
- DEFAULT(1);
-
- ChangeText
- {
- /* tweak the insertion point, based on command key */
- above = FALSE;
- switch (key)
- {
- case 'i':
- break;
-
- case 'a':
- pfetch(markline(m));
- if (plen > 0)
- {
- m++;
- }
- break;
-
- case 'I':
- m = m_front(m, 1L);
- break;
-
- case 'A':
- pfetch(markline(m));
- m = (m & ~(BLKSIZE - 1)) + plen;
- break;
-
- case 'O':
- m &= ~(BLKSIZE - 1);
- add(m, "\n");
- above = TRUE;
- break;
-
- case 'o':
- m = (m & ~(BLKSIZE - 1)) + BLKSIZE;
- add(m, "\n");
- break;
- }
-
- /* insert the same text once or more */
- for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE)
- {
- m = input(m, m, WHEN_VIINP, above) + 1;
- }
- if (markidx(m) > 0)
- {
- m--;
- }
-
- doingdot = wasdot;
- }
-
- #ifndef CRUNCH
- # ifndef NO_EXTENSIONS
- if (key == 'i' && *o_inputmode && mode == MODE_VI)
- {
- msg("Now in command mode! To return to input mode, hit <i>");
- }
- # endif
- #endif
-
- return m;
- }
-
- /* This starts input mode with some text deleted */
- MARK v_change(m, n)
- MARK m, n; /* the range of text to change */
- {
- int lnmode; /* is this a line-mode change? */
-
- /* swap them if they're in reverse order */
- if (m > n)
- {
- MARK tmp;
- tmp = m;
- m = n;
- n = tmp;
- }
-
- /* for line mode, retain the last newline char */
- lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n);
- if (lnmode)
- {
- n -= BLKSIZE;
- pfetch(markline(n));
- n = (n & ~(BLKSIZE - 1)) + plen;
- }
-
- ChangeText
- {
- cut(m, n);
- m = input(m, n, WHEN_VIINP, FALSE);
- }
-
- return m;
- }
-
- /* This function replaces a given number of characters with input */
- MARK v_subst(m, cnt)
- MARK m; /* where substitutions start */
- long cnt; /* number of chars to replace */
- {
- DEFAULT(1);
-
- /* make sure we don't try replacing past EOL */
- pfetch(markline(m));
- if (markidx(m) + cnt > plen)
- {
- cnt = plen - markidx(m);
- }
-
- /* Go for it! */
- ChangeText
- {
- cut(m, m + cnt);
- m = input(m, m + cnt, WHEN_VIINP, FALSE);
- }
- return m;
- }
-
- /* This calls the ex "join" command to join some lines together */
- MARK v_join(m, cnt)
- MARK m; /* the first line to be joined */
- long cnt; /* number of other lines to join */
- {
- MARK joint; /* where the lines were joined */
-
- DEFAULT(1);
-
- /* figure out where the joint will be */
- pfetch(markline(m));
- joint = (m & ~(BLKSIZE - 1)) + plen;
-
- /* join the lines */
- cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, "");
-
- /* the cursor should be left at the joint */
- return joint;
- }
-
-
- /* This calls the ex "<" command to shift some lines left */
- MARK v_lshift(m, n)
- MARK m, n; /* range of lines to shift */
- {
- /* adjust for inclusive endmarks in ex */
- n -= BLKSIZE;
-
- cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0);
-
- return m;
- }
-
- /* This calls the ex ">" command to shift some lines right */
- MARK v_rshift(m, n)
- MARK m, n; /* range of lines to shift */
- {
- /* adjust for inclusive endmarks in ex */
- n -= BLKSIZE;
-
- cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0);
-
- return m;
- }
-
- /* This filters some lines through a preset program, to reformat them */
- MARK v_reformat(m, n)
- MARK m, n; /* range of lines to shift */
- {
- /* adjust for inclusive endmarks in ex */
- n -= BLKSIZE;
-
- /* run the filter command */
- filter(m, n, o_equalprg, TRUE);
-
- redraw(MARK_UNSET, FALSE);
- return m;
- }
-
-
- /* This runs some lines through a filter program */
- MARK v_filter(m, n)
- MARK m, n; /* range of lines to shift */
- {
- char cmdln[150]; /* a shell command line */
-
- /* adjust for inclusive endmarks in ex */
- n -= BLKSIZE;
-
- if (vgets('!', cmdln, sizeof(cmdln)) > 0)
- {
- filter(m, n, cmdln, TRUE);
- }
-
- redraw(MARK_UNSET, FALSE);
- return m;
- }
-
-
- /* This function runs the ex "file" command to show the file's status */
- MARK v_status()
- {
- cmd_file(cursor, cursor, CMD_FILE, 0, "");
- return cursor;
- }
-
-
- /* This function runs the ":&" command to repeat the previous :s// */
- MARK v_again(m, n)
- MARK m, n;
- {
- cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, "");
- return cursor;
- }
-
-
-
- /* This function switches to the previous file, if possible */
- MARK v_switch()
- {
- if (!*prevorig)
- msg("No previous file");
- else
- { strcpy(tmpblk.c, prevorig);
- cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c);
- }
- return cursor;
- }
-
- /* This function does a tag search on a keyword */
- /*ARGSUSED*/
- MARK v_tag(keyword, m, cnt)
- char *keyword;
- MARK m;
- long cnt;
- {
- /* move the cursor to the start of the tag name, where m is */
- cursor = m;
-
- /* perform the tag search */
- cmd_tag(cursor, cursor, CMD_TAG, 0, keyword);
-
- return cursor;
- }
-
- #ifndef NO_EXTENSIONS
- /* This function looks up a keyword by calling the helpprog program */
- /*ARGSUSED*/
- MARK v_keyword(keyword, m, cnt)
- char *keyword;
- MARK m;
- long cnt;
- {
- int waswarn;
- char cmdline[130];
-
- move(LINES - 1, 0);
- addstr("---------------------------------------------------------\n");
- clrtoeol();
- refresh();
- sprintf(cmdline, "%s %s", o_keywordprg, keyword);
- waswarn = *o_warn;
- *o_warn = FALSE;
- suspend_curses();
- if (system(cmdline))
- {
- addstr("<<< failed >>>\n");
- }
- resume_curses(FALSE);
- mode = MODE_VI;
- redraw(MARK_UNSET, FALSE);
- *o_warn = waswarn;
-
- return m;
- }
-
-
-
- MARK v_increment(keyword, m, cnt)
- char *keyword;
- MARK m;
- long cnt;
- {
- static sign;
- char newval[12];
- long atol();
-
- DEFAULT(1);
-
- /* get one more keystroke, unless doingdot */
- if (!doingdot)
- {
- sign = getkey(0);
- }
-
- /* adjust the number, based on that second keystroke */
- switch (sign)
- {
- case '+':
- case '#':
- cnt = atol(keyword) + cnt;
- break;
-
- case '-':
- cnt = atol(keyword) - cnt;
- break;
-
- case '=':
- break;
-
- default:
- return MARK_UNSET;
- }
- sprintf(newval, "%ld", cnt);
-
- ChangeText
- {
- change(m, m + strlen(keyword), newval);
- }
-
- return m;
- }
- #endif
-
-
- /* This function acts like the EX command "xit" */
- /*ARGSUSED*/
- MARK v_xit(m, cnt, key)
- MARK m; /* ignored */
- long cnt; /* ignored */
- int key; /* must be a second 'Z' */
- {
- /* if second char wasn't 'Z', fail */
- if (key != 'Z')
- {
- return MARK_UNSET;
- }
-
- /* move the cursor to the bottom of the screen */
- move(LINES - 1, 0);
- clrtoeol();
-
- /* do the xit command */
- cmd_xit(m, m, CMD_XIT, FALSE, "");
-
- /* return the cursor */
- return m;
- }
-
-
- /* This function undoes changes to a single line, if possible */
- MARK v_undoline(m)
- MARK m; /* where we hope to undo the change */
- {
- /* make sure we have the right line in the buffer */
- if (markline(m) != U_line)
- {
- return MARK_UNSET;
- }
-
- /* fix it */
- ChangeText
- {
- strcat(U_text, "\n");
- change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text);
- }
-
- /* nothing in the buffer anymore */
- U_line = -1L;
-
- /* return, with the cursor at the front of the line */
- return m & ~(BLKSIZE - 1);
- }
-
-
- #ifndef NO_ERRLIST
- MARK v_errlist(m)
- MARK m;
- {
- cmd_errlist(m, m, CMD_ERRLIST, FALSE, "");
- return cursor;
- }
- #endif
-
-
- #ifndef NO_AT
- /*ARGSUSED*/
- MARK v_at(m, cnt, key)
- MARK m;
- long cnt;
- int key;
- {
- int size;
-
- size = cb2str(key, tmpblk.c, BLKSIZE);
- if (size <= 0 || size == BLKSIZE)
- {
- return MARK_UNSET;
- }
-
- execmap(0, tmpblk.c, FALSE);
- return cursor;
- }
- #endif
-
-
- #ifdef SIGTSTP
- MARK v_suspend()
- {
- cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, "");
- return MARK_UNSET;
- }
- #endif
-
-
- #ifndef NO_VISIBLE
- /*ARGSUSED*/
- MARK v_start(m, cnt, cmd)
- MARK m; /* starting point for a v or V command */
- long cnt; /* ignored */
- int cmd; /* either 'v' or 'V' */
- {
- if (V_from)
- {
- V_from = MARK_UNSET;
- }
- else
- {
- V_from = m;
- V_linemd = isupper(cmd);
- }
- return m;
- }
- #endif
-
- #ifndef NO_POPUP
- # define MENU_HEIGHT 11
- # define MENU_WIDTH 23
- MARK v_popup(m, n)
- MARK m, n; /* the range of text to change */
- {
- int i;
- int y, x; /* position where the window will pop up at */
- int key; /* keystroke from the user */
- int sel; /* index of the selected operation */
- static int dfltsel;/* default value of sel */
- static char *labels[11] =
- {
- "ESC cancel! ",
- " d delete (cut) ",
- " y yank (copy) ",
- " p paste after ",
- " P paste before ",
- " > more indented ",
- " < less indented ",
- " = reformat ",
- " ! external filter ",
- " ZZ save & exit ",
- " u undo previous "
- };
-
- /* try to position the menu near the cursor */
- x = physcol - (MENU_WIDTH / 2);
- if (x < 0)
- x = 0;
- else if (x + MENU_WIDTH + 2 > COLS)
- x = COLS - MENU_WIDTH - 2;
- if (markline(cursor) < topline || markline(cursor) > botline)
- y = 0;
- else if (markline(cursor) + 1L + MENU_HEIGHT > botline)
- y = (int)(markline(cursor) - topline) - MENU_HEIGHT;
- else
- y = (int)(markline(cursor) - topline) + 1L;
-
- /* draw the menu */
- for (sel = 0; sel < MENU_HEIGHT; sel++)
- {
- move(y + sel, x);
- do_POPUP();
- if (sel == dfltsel)
- qaddstr("-->");
- else
- qaddstr(" ");
- qaddstr(labels[sel]);
- do_SE();
- }
-
- /* get a selection */
- move(y + dfltsel, x + 4);
- for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; )
- {
- /* erase the selection arrow */
- move(y + sel, x);
- do_POPUP();
- qaddstr(" ");
- qaddstr(labels[sel]);
- do_SE();
-
- /* process the user's keystroke */
- if (key == 'j' && sel < MENU_HEIGHT - 1)
- {
- sel++;
- }
- else if (key == 'k' && sel > 0)
- {
- sel--;
- }
- else if (key == '\033')
- {
- sel = 0;
- break;
- }
- else
- {
- for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++)
- {
- }
- if (i < MENU_HEIGHT)
- {
- sel = i;
- break;
- }
- }
-
- /* redraw the arrow, possibly in a new place */
- move(y + sel, x);
- do_POPUP();
- qaddstr("-->");
- qaddstr(labels[sel]);
- do_SE();
- move(y + sel, x + 4);
- }
-
- /* in most cases, the default selection is "paste after" */
- dfltsel = 3;
-
- /* perform the appropriate action */
- switch (sel)
- {
- case 0:
- m = cursor;
- dfltsel = 0;
- break;
-
- case 1: /* delete (cut) */
- m = v_delete(m, n);
- break;
-
- case 2: /* yank (copy) */
- m = v_yank(m, n);
- break;
-
- case 3: /* paste after */
- m = v_paste(n, 1L, 'P');
- break;
-
- case 4: /* paste before */
- m = v_paste(m, 1L, 'P');
- dfltsel = 4;
- break;
-
- case 5: /* more indented */
- m = v_rshift(m, n);
- dfltsel = 5;
- break;
-
- case 6: /* less indented */
- m = v_lshift(m, n);
- dfltsel = 6;
- break;
-
- case 7: /* reformat */
- m = v_reformat(m, n);
- dfltsel = 7;
- break;
-
- case 8: /* external filter */
- m = v_filter(m, n);
- dfltsel = 0;
- break;
-
- case 9: /* save & exit */
- /* get confirmation first */
- do
- {
- key = getkey(0);
- } while (key != '\\' && key != 'Z' && key != '\r' /* good */
- && key != '\033'); /* bad */
- if (key != '\033')
- {
- m = v_xit(m, 0L, 'Z');
- }
- break;
-
- case 10: /* undo previous */
- m = v_undo(m);
- dfltsel = 9;
- break;
- }
-
- /* arrange for the menu to be erased (except that "chg from kbd"
- * already erased it, and "save & exit" doesn't care)
- */
- if (sel != 5 && sel != 9)
- redraw(MARK_UNSET, FALSE);
-
- return m;
- }
- #endif /* undef NO_POPUP */
-