609 lines
13 KiB
C
609 lines
13 KiB
C
/*-
|
|
* Copyright (c) 1993, 1994
|
|
* The Regents of the University of California. All rights reserved.
|
|
* Copyright (c) 1993, 1994, 1995, 1996
|
|
* Keith Bostic. All rights reserved.
|
|
*
|
|
* See the LICENSE file for redistribution information.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifndef lint
|
|
static const char sccsid[] = "@(#)cl_funcs.c 10.40 (Berkeley) 5/16/96";
|
|
#endif /* not lint */
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <bitstring.h>
|
|
#include <ctype.h>
|
|
#include <curses.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../common/common.h"
|
|
#include "../vi/vi.h"
|
|
#include "cl.h"
|
|
|
|
/*
|
|
* cl_addstr --
|
|
* Add len bytes from the string at the cursor, advancing the cursor.
|
|
*
|
|
* PUBLIC: int cl_addstr __P((SCR *, const char *, size_t));
|
|
*/
|
|
int
|
|
cl_addstr(sp, str, len)
|
|
SCR *sp;
|
|
const char *str;
|
|
size_t len;
|
|
{
|
|
CL_PRIVATE *clp;
|
|
size_t oldy, oldx;
|
|
int iv;
|
|
|
|
clp = CLP(sp);
|
|
|
|
/*
|
|
* If ex isn't in control, it's the last line of the screen and
|
|
* it's a split screen, use inverse video.
|
|
*/
|
|
iv = 0;
|
|
getyx(stdscr, oldy, oldx);
|
|
if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
|
|
oldy == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) {
|
|
iv = 1;
|
|
(void)standout();
|
|
}
|
|
|
|
if (addnstr(str, len) == ERR)
|
|
return (1);
|
|
|
|
if (iv)
|
|
(void)standend();
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_attr --
|
|
* Toggle a screen attribute on/off.
|
|
*
|
|
* PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int));
|
|
*/
|
|
int
|
|
cl_attr(sp, attribute, on)
|
|
SCR *sp;
|
|
scr_attr_t attribute;
|
|
int on;
|
|
{
|
|
CL_PRIVATE *clp;
|
|
|
|
switch (attribute) {
|
|
case SA_INVERSE:
|
|
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
|
|
clp = CLP(sp);
|
|
if (clp->smso == NULL)
|
|
return (1);
|
|
if (on)
|
|
(void)tputs(clp->smso, 1, cl_putchar);
|
|
else
|
|
(void)tputs(clp->rmso, 1, cl_putchar);
|
|
(void)fflush(stdout);
|
|
} else {
|
|
if (on)
|
|
(void)standout();
|
|
else
|
|
(void)standend();
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_baud --
|
|
* Return the baud rate.
|
|
*
|
|
* PUBLIC: int cl_baud __P((SCR *, u_long *));
|
|
*/
|
|
int
|
|
cl_baud(sp, ratep)
|
|
SCR *sp;
|
|
u_long *ratep;
|
|
{
|
|
CL_PRIVATE *clp;
|
|
|
|
/*
|
|
* XXX
|
|
* There's no portable way to get a "baud rate" -- cfgetospeed(3)
|
|
* returns the value associated with some #define, which we may
|
|
* never have heard of, or which may be a purely local speed. Vi
|
|
* only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
|
|
* Try and detect the slow ones, and default to fast.
|
|
*/
|
|
clp = CLP(sp);
|
|
switch (cfgetospeed(&clp->orig)) {
|
|
case B50:
|
|
case B75:
|
|
case B110:
|
|
case B134:
|
|
case B150:
|
|
case B200:
|
|
case B300:
|
|
case B600:
|
|
*ratep = 600;
|
|
break;
|
|
case B1200:
|
|
*ratep = 1200;
|
|
break;
|
|
default:
|
|
*ratep = 9600;
|
|
break;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_bell --
|
|
* Ring the bell/flash the screen.
|
|
*
|
|
* PUBLIC: int cl_bell __P((SCR *));
|
|
*/
|
|
int
|
|
cl_bell(sp)
|
|
SCR *sp;
|
|
{
|
|
if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
|
|
(void)write(STDOUT_FILENO, "\07", 1); /* \a */
|
|
else {
|
|
/*
|
|
* Vi has an edit option which determines if the terminal
|
|
* should be beeped or the screen flashed.
|
|
*/
|
|
if (O_ISSET(sp, O_FLASH))
|
|
(void)flash();
|
|
else
|
|
(void)beep();
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_clrtoeol --
|
|
* Clear from the current cursor to the end of the line.
|
|
*
|
|
* PUBLIC: int cl_clrtoeol __P((SCR *));
|
|
*/
|
|
int
|
|
cl_clrtoeol(sp)
|
|
SCR *sp;
|
|
{
|
|
return (clrtoeol() == ERR);
|
|
}
|
|
|
|
/*
|
|
* cl_cursor --
|
|
* Return the current cursor position.
|
|
*
|
|
* PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *));
|
|
*/
|
|
int
|
|
cl_cursor(sp, yp, xp)
|
|
SCR *sp;
|
|
size_t *yp, *xp;
|
|
{
|
|
/*
|
|
* The curses screen support splits a single underlying curses screen
|
|
* into multiple screens to support split screen semantics. For this
|
|
* reason the returned value must be adjusted to be relative to the
|
|
* current screen, and not absolute. Screens that implement the split
|
|
* using physically distinct screens won't need this hack.
|
|
*/
|
|
getyx(stdscr, *yp, *xp);
|
|
*yp -= sp->woff;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_deleteln --
|
|
* Delete the current line, scrolling all lines below it.
|
|
*
|
|
* PUBLIC: int cl_deleteln __P((SCR *));
|
|
*/
|
|
int
|
|
cl_deleteln(sp)
|
|
SCR *sp;
|
|
{
|
|
CHAR_T ch;
|
|
CL_PRIVATE *clp;
|
|
size_t col, lno, spcnt, oldy, oldx;
|
|
|
|
clp = CLP(sp);
|
|
|
|
/*
|
|
* This clause is required because the curses screen uses reverse
|
|
* video to delimit split screens. If the screen does not do this,
|
|
* this code won't be necessary.
|
|
*
|
|
* If the bottom line was in reverse video, rewrite it in normal
|
|
* video before it's scrolled.
|
|
*
|
|
* Check for the existence of a chgat function; XSI requires it, but
|
|
* historic implementations of System V curses don't. If it's not
|
|
* a #define, we'll fall back to doing it by hand, which is slow but
|
|
* acceptable.
|
|
*
|
|
* By hand means walking through the line, retrieving and rewriting
|
|
* each character. Curses has no EOL marker, so track strings of
|
|
* spaces, and copy the trailing spaces only if there's a non-space
|
|
* character following.
|
|
*/
|
|
if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
|
|
getyx(stdscr, oldy, oldx);
|
|
#ifdef mvchgat
|
|
mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL);
|
|
#else
|
|
for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) {
|
|
(void)move(lno, col);
|
|
ch = winch(stdscr);
|
|
if (isblank(ch))
|
|
++spcnt;
|
|
else {
|
|
(void)move(lno, col - spcnt);
|
|
for (; spcnt > 0; --spcnt)
|
|
(void)addch(' ');
|
|
(void)addch(ch);
|
|
}
|
|
if (++col >= sp->cols)
|
|
break;
|
|
}
|
|
#endif
|
|
(void)move(oldy, oldx);
|
|
}
|
|
|
|
/*
|
|
* The bottom line is expected to be blank after this operation,
|
|
* and other screens must support that semantic.
|
|
*/
|
|
return (deleteln() == ERR);
|
|
}
|
|
|
|
/*
|
|
* cl_ex_adjust --
|
|
* Adjust the screen for ex. This routine is purely for standalone
|
|
* ex programs. All special purpose, all special case.
|
|
*
|
|
* PUBLIC: int cl_ex_adjust __P((SCR *, exadj_t));
|
|
*/
|
|
int
|
|
cl_ex_adjust(sp, action)
|
|
SCR *sp;
|
|
exadj_t action;
|
|
{
|
|
CL_PRIVATE *clp;
|
|
int cnt;
|
|
|
|
clp = CLP(sp);
|
|
switch (action) {
|
|
case EX_TERM_SCROLL:
|
|
/* Move the cursor up one line if that's possible. */
|
|
if (clp->cuu1 != NULL)
|
|
(void)tputs(clp->cuu1, 1, cl_putchar);
|
|
else if (clp->cup != NULL)
|
|
(void)tputs(tgoto(clp->cup,
|
|
0, LINES - 2), 1, cl_putchar);
|
|
else
|
|
return (0);
|
|
/* FALLTHROUGH */
|
|
case EX_TERM_CE:
|
|
/* Clear the line. */
|
|
if (clp->el != NULL) {
|
|
(void)putchar('\r');
|
|
(void)tputs(clp->el, 1, cl_putchar);
|
|
} else {
|
|
/*
|
|
* Historically, ex didn't erase the line, so, if the
|
|
* displayed line was only a single glyph, and <eof>
|
|
* was more than one glyph, the output would not fully
|
|
* overwrite the user's input. To fix this, output
|
|
* the maxiumum character number of spaces. Note,
|
|
* this won't help if the user entered extra prompt
|
|
* or <blank> characters before the command character.
|
|
* We'd have to do a lot of work to make that work, and
|
|
* it's almost certainly not worth the effort.
|
|
*/
|
|
for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
|
|
(void)putchar('\b');
|
|
for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
|
|
(void)putchar(' ');
|
|
(void)putchar('\r');
|
|
(void)fflush(stdout);
|
|
}
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_insertln --
|
|
* Push down the current line, discarding the bottom line.
|
|
*
|
|
* PUBLIC: int cl_insertln __P((SCR *));
|
|
*/
|
|
int
|
|
cl_insertln(sp)
|
|
SCR *sp;
|
|
{
|
|
/*
|
|
* The current line is expected to be blank after this operation,
|
|
* and the screen must support that semantic.
|
|
*/
|
|
return (insertln() == ERR);
|
|
}
|
|
|
|
/*
|
|
* cl_keyval --
|
|
* Return the value for a special key.
|
|
*
|
|
* PUBLIC: int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
|
|
*/
|
|
int
|
|
cl_keyval(sp, val, chp, dnep)
|
|
SCR *sp;
|
|
scr_keyval_t val;
|
|
CHAR_T *chp;
|
|
int *dnep;
|
|
{
|
|
CL_PRIVATE *clp;
|
|
|
|
/*
|
|
* VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
|
|
* VWERASE is a 4BSD extension.
|
|
*/
|
|
clp = CLP(sp);
|
|
switch (val) {
|
|
case KEY_VEOF:
|
|
*dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
|
|
break;
|
|
case KEY_VERASE:
|
|
*dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
|
|
break;
|
|
case KEY_VKILL:
|
|
*dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
|
|
break;
|
|
#ifdef VWERASE
|
|
case KEY_VWERASE:
|
|
*dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
|
|
break;
|
|
#endif
|
|
default:
|
|
*dnep = 1;
|
|
break;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_move --
|
|
* Move the cursor.
|
|
*
|
|
* PUBLIC: int cl_move __P((SCR *, size_t, size_t));
|
|
*/
|
|
int
|
|
cl_move(sp, lno, cno)
|
|
SCR *sp;
|
|
size_t lno, cno;
|
|
{
|
|
/* See the comment in cl_cursor. */
|
|
if (move(RLNO(sp, lno), cno) == ERR) {
|
|
msgq(sp, M_ERR,
|
|
"Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_refresh --
|
|
* Refresh the screen.
|
|
*
|
|
* PUBLIC: int cl_refresh __P((SCR *, int));
|
|
*/
|
|
int
|
|
cl_refresh(sp, repaint)
|
|
SCR *sp;
|
|
int repaint;
|
|
{
|
|
/*
|
|
* If repaint is set, the editor is telling us that we don't know
|
|
* what's on the screen, so we have to repaint from scratch.
|
|
*
|
|
* In the curses library, doing wrefresh(curscr) is okay, but the
|
|
* screen flashes when we then apply the refresh() to bring it up
|
|
* to date. So, use clearok().
|
|
*/
|
|
if (repaint)
|
|
clearok(curscr, 1);
|
|
return (refresh() == ERR);
|
|
}
|
|
|
|
/*
|
|
* cl_rename --
|
|
* Rename the file.
|
|
*
|
|
* PUBLIC: int cl_rename __P((SCR *));
|
|
*/
|
|
int
|
|
cl_rename(sp)
|
|
SCR *sp;
|
|
{
|
|
return (0); /* Curses doesn't care. */
|
|
}
|
|
|
|
/*
|
|
* cl_suspend --
|
|
* Suspend a screen.
|
|
*
|
|
* PUBLIC: int cl_suspend __P((SCR *, int *));
|
|
*/
|
|
int
|
|
cl_suspend(sp, allowedp)
|
|
SCR *sp;
|
|
int *allowedp;
|
|
{
|
|
struct termios t;
|
|
CL_PRIVATE *clp;
|
|
GS *gp;
|
|
size_t oldy, oldx;
|
|
int changed;
|
|
|
|
gp = sp->gp;
|
|
clp = CLP(sp);
|
|
*allowedp = 1;
|
|
|
|
/*
|
|
* The ex implementation of this function isn't needed by screens not
|
|
* supporting ex commands that require full terminal canonical mode
|
|
* (e.g. :suspend).
|
|
*
|
|
* The vi implementation of this function isn't needed by screens not
|
|
* supporting vi process suspension, i.e. any screen that isn't backed
|
|
* by a UNIX shell.
|
|
*
|
|
* Setting allowedp to 0 will cause the editor to reject the command.
|
|
*/
|
|
if (F_ISSET(sp, SC_EX)) {
|
|
/* Save the terminal settings, and restore the original ones. */
|
|
if (F_ISSET(gp, G_STDIN_TTY)) {
|
|
(void)tcgetattr(STDIN_FILENO, &t);
|
|
(void)tcsetattr(STDIN_FILENO,
|
|
TCSASOFT | TCSADRAIN, &clp->orig);
|
|
}
|
|
|
|
/* Stop the process group. */
|
|
(void)kill(0, SIGTSTP);
|
|
|
|
/* Time passes ... */
|
|
|
|
/* Restore terminal settings. */
|
|
if (F_ISSET(gp, G_STDIN_TTY))
|
|
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Move to the lower left-hand corner of the screen.
|
|
*
|
|
* XXX
|
|
* Not sure this is necessary in System V implementations, but it
|
|
* shouldn't hurt.
|
|
*/
|
|
getyx(stdscr, oldy, oldx);
|
|
(void)move(LINES - 1, 0);
|
|
(void)refresh();
|
|
|
|
/*
|
|
* Temporarily end the screen. System V introduced a semantic where
|
|
* endwin() could be restarted. We use it because restarting curses
|
|
* from scratch often fails in System V. 4BSD curses didn't support
|
|
* restarting after endwin(), so we have to do what clean up we can
|
|
* without calling it.
|
|
*/
|
|
#ifdef HAVE_BSD_CURSES
|
|
/* Save the terminal settings. */
|
|
(void)tcgetattr(STDIN_FILENO, &t);
|
|
#endif
|
|
|
|
/* Restore the cursor keys to normal mode. */
|
|
(void)keypad(stdscr, FALSE);
|
|
|
|
#ifdef HAVE_BSD_CURSES
|
|
/* Send the terminal end sequence. */
|
|
if (clp->rmcup == NULL)
|
|
(void)cl_getcap(sp, "rmcup", &clp->rmcup);
|
|
if (clp->rmcup != NULL)
|
|
(void)tputs(clp->rmcup, 1, cl_putchar);
|
|
(void)fflush(stdout);
|
|
#else
|
|
(void)endwin();
|
|
#endif
|
|
/*
|
|
* XXX
|
|
* Restore the original terminal settings. This is bad -- the
|
|
* reset can cause character loss from the tty queue. However,
|
|
* we can't call endwin() in BSD curses implementations, and too
|
|
* many System V curses implementations don't get it right.
|
|
*/
|
|
(void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
|
|
|
|
/* Stop the process group. */
|
|
(void)kill(0, SIGTSTP);
|
|
|
|
/* Time passes ... */
|
|
|
|
#ifdef HAVE_BSD_CURSES
|
|
/* Restore terminal settings. */
|
|
if (F_ISSET(gp, G_STDIN_TTY))
|
|
(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
|
|
|
|
/* Send the terminal initialization sequence. */
|
|
if (clp->smcup == NULL)
|
|
(void)cl_getcap(sp, "smcup", &clp->smcup);
|
|
if (clp->smcup != NULL)
|
|
(void)tputs(clp->smcup, 1, cl_putchar);
|
|
(void)fflush(stdout);
|
|
#endif
|
|
/* Put the cursor keys into application mode. */
|
|
(void)keypad(stdscr, TRUE);
|
|
|
|
/* Refresh and repaint the screen. */
|
|
(void)move(oldy, oldx);
|
|
(void)cl_refresh(sp, 1);
|
|
|
|
/* If the screen changed size, set the SIGWINCH bit. */
|
|
if (cl_ssize(sp, 1, NULL, NULL, &changed))
|
|
return (1);
|
|
if (changed)
|
|
F_SET(CLP(sp), CL_SIGWINCH);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* cl_usage --
|
|
* Print out the curses usage messages.
|
|
*
|
|
* PUBLIC: void cl_usage __P((void));
|
|
*/
|
|
void
|
|
cl_usage()
|
|
{
|
|
#define USAGE "\
|
|
usage: ex [-eFRrsv] [-c command] [-t tag] [-w size] [file ...]\n\
|
|
usage: vi [-eFlRrv] [-c command] [-t tag] [-w size] [file ...]\n"
|
|
(void)fprintf(stderr, "%s", USAGE);
|
|
#undef USAGE
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* gdbrefresh --
|
|
* Stub routine so can flush out curses screen changes using gdb.
|
|
*/
|
|
int
|
|
gdbrefresh()
|
|
{
|
|
refresh();
|
|
return (0); /* XXX Convince gdb to run it. */
|
|
}
|
|
#endif
|