summaryrefslogtreecommitdiffstats
path: root/contrib/nvi/cl/cl_funcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/cl/cl_funcs.c')
-rw-r--r--contrib/nvi/cl/cl_funcs.c704
1 files changed, 704 insertions, 0 deletions
diff --git a/contrib/nvi/cl/cl_funcs.c b/contrib/nvi/cl/cl_funcs.c
new file mode 100644
index 0000000..40315ee
--- /dev/null
+++ b/contrib/nvi/cl/cl_funcs.c
@@ -0,0 +1,704 @@
+/*-
+ * 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.50 (Berkeley) 9/24/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;
+
+ clp = CLP(sp);
+
+ switch (attribute) {
+ case SA_ALTERNATE:
+ /*
+ * !!!
+ * There's a major layering violation here. The problem is that the
+ * X11 xterm screen has what's known as an "alternate" screen. Some
+ * xterm termcap/terminfo entries include sequences to switch to/from
+ * that alternate screen as part of the ti/te (smcup/rmcup) strings.
+ * Vi runs in the alternate screen, so that you are returned to the
+ * same screen contents on exit from vi that you had when you entered
+ * vi. Further, when you run :shell, or :!date or similar ex commands,
+ * you also see the original screen contents. This wasn't deliberate
+ * on vi's part, it's just that it historically sent terminal init/end
+ * sequences at those times, and the addition of the alternate screen
+ * sequences to the strings changed the behavior of vi. The problem
+ * caused by this is that we don't want to switch back to the alternate
+ * screen while getting a new command from the user, when the user is
+ * continuing to enter ex commands, e.g.:
+ *
+ * :!date <<< switch to original screen
+ * [Hit return to continue] <<< prompt user to continue
+ * :command <<< get command from user
+ *
+ * Note that the :command input is a true vi input mode, e.g., input
+ * maps and abbreviations are being done. So, we need to be able to
+ * switch back into the vi screen mode, without flashing the screen.
+ *
+ * To make matters worse, the curses initscr() and endwin() calls will
+ * do this automatically -- so, this attribute isn't as controlled by
+ * the higher level screen as closely as one might like.
+ */
+ if (on) {
+ if (clp->ti_te != TI_SENT) {
+ clp->ti_te = TI_SENT;
+ if (clp->smcup == NULL)
+ (void)cl_getcap(sp, "smcup", &clp->smcup);
+ if (clp->smcup != NULL)
+ (void)tputs(clp->smcup, 1, cl_putchar);
+ }
+ } else
+ if (clp->ti_te != TE_SENT) {
+ clp->ti_te = TE_SENT;
+ 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);
+ }
+ (void)fflush(stdout);
+ break;
+ case SA_INVERSE:
+ if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
+ 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;
+{
+ CL_PRIVATE *clp;
+
+ clp = CLP(sp);
+
+ /*
+ * If we received a killer signal, we're done, there's no point
+ * in refreshing the screen.
+ */
+ if (clp->killersig)
+ return (0);
+
+ /*
+ * 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 *, char *, int));
+ */
+int
+cl_rename(sp, name, on)
+ SCR *sp;
+ char *name;
+ int on;
+{
+ GS *gp;
+ CL_PRIVATE *clp;
+ char *ttype;
+
+ gp = sp->gp;
+ clp = CLP(sp);
+
+ ttype = OG_STR(gp, GO_TERM);
+
+ /*
+ * XXX
+ * We can only rename windows for xterm.
+ */
+ if (on) {
+ if (F_ISSET(clp, CL_RENAME_OK) &&
+ !strncmp(ttype, "xterm", sizeof("xterm") - 1)) {
+ F_SET(clp, CL_RENAME);
+ (void)printf(XTERM_RENAME, name);
+ (void)fflush(stdout);
+ }
+ } else
+ if (F_ISSET(clp, CL_RENAME)) {
+ F_CLR(clp, CL_RENAME);
+ (void)printf(XTERM_RENAME, ttype);
+ (void)fflush(stdout);
+ }
+ return (0);
+}
+
+/*
+ * 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(clp, CL_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(clp, CL_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);
+
+ /* Restore the window name. */
+ (void)cl_rename(sp, NULL, 0);
+
+#ifdef HAVE_BSD_CURSES
+ (void)cl_attr(sp, SA_ALTERNATE, 0);
+#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 ... */
+
+ /*
+ * If we received a killer signal, we're done. Leave everything
+ * unchanged. In addition, the terminal has already been reset
+ * correctly, so leave it alone.
+ */
+ if (clp->killersig) {
+ F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
+ return (0);
+ }
+
+#ifdef HAVE_BSD_CURSES
+ /* Restore terminal settings. */
+ if (F_ISSET(clp, CL_STDIN_TTY))
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+
+ (void)cl_attr(sp, SA_ALTERNATE, 1);
+#endif
+
+ /* Set the window name. */
+ (void)cl_rename(sp, sp->frp->name, 1);
+
+ /* 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 [-eFRrSsv] [-c command] [-t tag] [-w size] [file ...]\n\
+usage: vi [-eFlRrSv] [-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
OpenPOWER on IntegriCloud