/* $NetBSD: cl_funcs.c,v 1.2 1998/01/09 08:06:20 perry Exp $ */ /*- * 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 #include #include #include #include #include #include #include #include #include #include #include #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 * 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 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