cl_funcs.c revision 1.16
1/*	$OpenBSD: cl_funcs.c,v 1.16 2014/11/12 04:28:41 bentley Exp $	*/
2
3/*-
4 * Copyright (c) 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 * Copyright (c) 1993, 1994, 1995, 1996
7 *	Keith Bostic.  All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12#include "config.h"
13
14#include <sys/types.h>
15#include <sys/queue.h>
16#include <sys/time.h>
17
18#include <bitstring.h>
19#include <ctype.h>
20#include <curses.h>
21#include <signal.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <term.h>
26#include <termios.h>
27#include <unistd.h>
28
29#include "../common/common.h"
30#include "../vi/vi.h"
31#include "cl.h"
32
33/*
34 * cl_addstr --
35 *	Add len bytes from the string at the cursor, advancing the cursor.
36 *
37 * PUBLIC: int cl_addstr(SCR *, const char *, size_t);
38 */
39int
40cl_addstr(SCR *sp, const char *str, size_t len)
41{
42	size_t oldy, oldx;
43	int iv;
44
45	/*
46	 * If ex isn't in control, it's the last line of the screen and
47	 * it's a split screen, use inverse video.
48	 */
49	iv = 0;
50	getyx(stdscr, oldy, oldx);
51	if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
52	    oldy == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) {
53		iv = 1;
54		(void)standout();
55	}
56
57	if (addnstr(str, len) == ERR)
58		return (1);
59
60	if (iv)
61		(void)standend();
62	return (0);
63}
64
65/*
66 * cl_attr --
67 *	Toggle a screen attribute on/off.
68 *
69 * PUBLIC: int cl_attr(SCR *, scr_attr_t, int);
70 */
71int
72cl_attr(SCR *sp, scr_attr_t attribute, int on)
73{
74	CL_PRIVATE *clp;
75
76	clp = CLP(sp);
77
78	switch (attribute) {
79	case SA_ALTERNATE:
80	/*
81	 * !!!
82	 * There's a major layering violation here.  The problem is that the
83	 * X11 xterm screen has what's known as an "alternate" screen.  Some
84	 * xterm termcap/terminfo entries include sequences to switch to/from
85	 * that alternate screen as part of the ti/te (smcup/rmcup) strings.
86	 * Vi runs in the alternate screen, so that you are returned to the
87	 * same screen contents on exit from vi that you had when you entered
88	 * vi.  Further, when you run :shell, or :!date or similar ex commands,
89	 * you also see the original screen contents.  This wasn't deliberate
90	 * on vi's part, it's just that it historically sent terminal init/end
91	 * sequences at those times, and the addition of the alternate screen
92	 * sequences to the strings changed the behavior of vi.  The problem
93	 * caused by this is that we don't want to switch back to the alternate
94	 * screen while getting a new command from the user, when the user is
95	 * continuing to enter ex commands, e.g.:
96	 *
97	 *	:!date				<<< switch to original screen
98	 *	[Hit return to continue]	<<< prompt user to continue
99	 *	:command			<<< get command from user
100	 *
101	 * Note that the :command input is a true vi input mode, e.g., input
102	 * maps and abbreviations are being done.  So, we need to be able to
103	 * switch back into the vi screen mode, without flashing the screen.
104	 *
105	 * To make matters worse, the curses initscr() and endwin() calls will
106	 * do this automatically -- so, this attribute isn't as controlled by
107	 * the higher level screen as closely as one might like.
108	 */
109	if (on) {
110		if (clp->ti_te != TI_SENT) {
111			clp->ti_te = TI_SENT;
112			if (clp->smcup == NULL)
113				(void)cl_getcap(sp, "smcup", &clp->smcup);
114			if (clp->smcup != NULL)
115				(void)tputs(clp->smcup, 1, cl_putchar);
116		}
117	} else
118		if (clp->ti_te != TE_SENT) {
119			clp->ti_te = TE_SENT;
120			if (clp->rmcup == NULL)
121				(void)cl_getcap(sp, "rmcup", &clp->rmcup);
122			if (clp->rmcup != NULL)
123				(void)tputs(clp->rmcup, 1, cl_putchar);
124			(void)fflush(stdout);
125		}
126		(void)fflush(stdout);
127		break;
128	case SA_INVERSE:
129		if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
130			if (clp->smso == NULL)
131				return (1);
132			if (on)
133				(void)tputs(clp->smso, 1, cl_putchar);
134			else
135				(void)tputs(clp->rmso, 1, cl_putchar);
136			(void)fflush(stdout);
137		} else {
138			if (on)
139				(void)standout();
140			else
141				(void)standend();
142		}
143		break;
144	default:
145		abort();
146	}
147	return (0);
148}
149
150/*
151 * cl_baud --
152 *	Return the baud rate.
153 *
154 * PUBLIC: int cl_baud(SCR *, u_long *);
155 */
156int
157cl_baud(SCR *sp, u_long *ratep)
158{
159	CL_PRIVATE *clp;
160
161	/*
162	 * XXX
163	 * There's no portable way to get a "baud rate" -- cfgetospeed(3)
164	 * returns the value associated with some #define, which we may
165	 * never have heard of, or which may be a purely local speed.  Vi
166	 * only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
167	 * Try and detect the slow ones, and default to fast.
168	 */
169	clp = CLP(sp);
170	switch (cfgetospeed(&clp->orig)) {
171	case B50:
172	case B75:
173	case B110:
174	case B134:
175	case B150:
176	case B200:
177	case B300:
178	case B600:
179		*ratep = 600;
180		break;
181	case B1200:
182		*ratep = 1200;
183		break;
184	default:
185		*ratep = 9600;
186		break;
187	}
188	return (0);
189}
190
191/*
192 * cl_bell --
193 *	Ring the bell/flash the screen.
194 *
195 * PUBLIC: int cl_bell(SCR *);
196 */
197int
198cl_bell(SCR *sp)
199{
200	if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
201		(void)write(STDOUT_FILENO, "\07", 1);		/* \a */
202	else {
203		/*
204		 * If the screen has not been setup we cannot call
205		 * curses routines yet.
206		 */
207		if (F_ISSET(sp, SC_SCR_VI)) {
208			/*
209			 * Vi has an edit option which determines if the
210			 * terminal should be beeped or the screen flashed.
211			 */
212			if (O_ISSET(sp, O_FLASH))
213				(void)flash();
214			else
215				(void)beep();
216		} else if (!O_ISSET(sp, O_FLASH))
217			(void)write(STDOUT_FILENO, "\07", 1);
218	}
219	return (0);
220}
221
222/*
223 * cl_clrtoeol --
224 *	Clear from the current cursor to the end of the line.
225 *
226 * PUBLIC: int cl_clrtoeol(SCR *);
227 */
228int
229cl_clrtoeol(SCR *sp)
230{
231	return (clrtoeol() == ERR);
232}
233
234/*
235 * cl_cursor --
236 *	Return the current cursor position.
237 *
238 * PUBLIC: int cl_cursor(SCR *, size_t *, size_t *);
239 */
240int
241cl_cursor(SCR *sp, size_t *yp, size_t *xp)
242{
243	/*
244	 * The curses screen support splits a single underlying curses screen
245	 * into multiple screens to support split screen semantics.  For this
246	 * reason the returned value must be adjusted to be relative to the
247	 * current screen, and not absolute.  Screens that implement the split
248	 * using physically distinct screens won't need this hack.
249	 */
250	getyx(stdscr, *yp, *xp);
251	*yp -= sp->woff;
252	return (0);
253}
254
255/*
256 * cl_deleteln --
257 *	Delete the current line, scrolling all lines below it.
258 *
259 * PUBLIC: int cl_deleteln(SCR *);
260 */
261int
262cl_deleteln(SCR *sp)
263{
264#ifndef mvchgat
265	CHAR_T ch;
266	size_t col, lno, spcnt;
267#endif
268	size_t oldy, oldx;
269
270	/*
271	 * This clause is required because the curses screen uses reverse
272	 * video to delimit split screens.  If the screen does not do this,
273	 * this code won't be necessary.
274	 *
275	 * If the bottom line was in reverse video, rewrite it in normal
276	 * video before it's scrolled.
277	 *
278	 * Check for the existence of a chgat function; XSI requires it, but
279	 * historic implementations of System V curses don't.   If it's not
280	 * a #define, we'll fall back to doing it by hand, which is slow but
281	 * acceptable.
282	 *
283	 * By hand means walking through the line, retrieving and rewriting
284	 * each character.  Curses has no EOL marker, so track strings of
285	 * spaces, and copy the trailing spaces only if there's a non-space
286	 * character following.
287	 */
288	if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
289		getyx(stdscr, oldy, oldx);
290#ifdef mvchgat
291		mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL);
292#else
293		for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) {
294			(void)move(lno, col);
295			ch = winch(stdscr);
296			if (isblank(ch))
297				++spcnt;
298			else {
299				(void)move(lno, col - spcnt);
300				for (; spcnt > 0; --spcnt)
301					(void)addch(' ');
302				(void)addch(ch);
303			}
304			if (++col >= sp->cols)
305				break;
306		}
307#endif
308		(void)move(oldy, oldx);
309	}
310
311	/*
312	 * The bottom line is expected to be blank after this operation,
313	 * and other screens must support that semantic.
314	 */
315	return (deleteln() == ERR);
316}
317
318/*
319 * cl_ex_adjust --
320 *	Adjust the screen for ex.  This routine is purely for standalone
321 *	ex programs.  All special purpose, all special case.
322 *
323 * PUBLIC: int cl_ex_adjust(SCR *, exadj_t);
324 */
325int
326cl_ex_adjust(SCR *sp, exadj_t action)
327{
328	CL_PRIVATE *clp;
329	int cnt;
330
331	clp = CLP(sp);
332	switch (action) {
333	case EX_TERM_SCROLL:
334		/* Move the cursor up one line if that's possible. */
335		if (clp->cuu1 != NULL)
336			(void)tputs(clp->cuu1, 1, cl_putchar);
337		else if (clp->cup != NULL)
338			(void)tputs(tgoto(clp->cup,
339			    0, LINES - 2), 1, cl_putchar);
340		else
341			return (0);
342		/* FALLTHROUGH */
343	case EX_TERM_CE:
344		/* Clear the line. */
345		if (clp->el != NULL) {
346			(void)putchar('\r');
347			(void)tputs(clp->el, 1, cl_putchar);
348		} else {
349			/*
350			 * Historically, ex didn't erase the line, so, if the
351			 * displayed line was only a single glyph, and <eof>
352			 * was more than one glyph, the output would not fully
353			 * overwrite the user's input.  To fix this, output
354			 * the maxiumum character number of spaces.  Note,
355			 * this won't help if the user entered extra prompt
356			 * or <blank> characters before the command character.
357			 * We'd have to do a lot of work to make that work, and
358			 * it's almost certainly not worth the effort.
359			 */
360			for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
361				(void)putchar('\b');
362			for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
363				(void)putchar(' ');
364			(void)putchar('\r');
365			(void)fflush(stdout);
366		}
367		break;
368	default:
369		abort();
370	}
371	return (0);
372}
373
374/*
375 * cl_insertln --
376 *	Push down the current line, discarding the bottom line.
377 *
378 * PUBLIC: int cl_insertln(SCR *);
379 */
380int
381cl_insertln(SCR *sp)
382{
383	/*
384	 * The current line is expected to be blank after this operation,
385	 * and the screen must support that semantic.
386	 */
387	return (insertln() == ERR);
388}
389
390/*
391 * cl_keyval --
392 *	Return the value for a special key.
393 *
394 * PUBLIC: int cl_keyval(SCR *, scr_keyval_t, CHAR_T *, int *);
395 */
396int
397cl_keyval(SCR *sp, scr_keyval_t val, CHAR_T *chp, int *dnep)
398{
399	CL_PRIVATE *clp;
400
401	/*
402	 * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
403	 * VWERASE is a 4BSD extension.
404	 */
405	clp = CLP(sp);
406	switch (val) {
407	case KEY_VEOF:
408		*dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
409		break;
410	case KEY_VERASE:
411		*dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
412		break;
413	case KEY_VKILL:
414		*dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
415		break;
416#ifdef VWERASE
417	case KEY_VWERASE:
418		*dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
419		break;
420#endif
421	default:
422		*dnep = 1;
423		break;
424	}
425	return (0);
426}
427
428/*
429 * cl_move --
430 *	Move the cursor.
431 *
432 * PUBLIC: int cl_move(SCR *, size_t, size_t);
433 */
434int
435cl_move(SCR *sp, size_t lno, size_t cno)
436{
437	/* See the comment in cl_cursor. */
438	if (move(RLNO(sp, lno), cno) == ERR) {
439		msgq(sp, M_ERR,
440		    "Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff);
441		return (1);
442	}
443	return (0);
444}
445
446/*
447 * cl_refresh --
448 *	Refresh the screen.
449 *
450 * PUBLIC: int cl_refresh(SCR *, int);
451 */
452int
453cl_refresh(SCR *sp, int repaint)
454{
455	CL_PRIVATE *clp;
456
457	clp = CLP(sp);
458
459	/*
460	 * If we received a killer signal, we're done, there's no point
461	 * in refreshing the screen.
462	 */
463	if (clp->killersig)
464		return (0);
465
466	/*
467	 * If repaint is set, the editor is telling us that we don't know
468	 * what's on the screen, so we have to repaint from scratch.
469	 *
470	 * In the curses library, doing wrefresh(curscr) is okay, but the
471	 * screen flashes when we then apply the refresh() to bring it up
472	 * to date.  So, use clearok().
473	 */
474	if (repaint)
475		clearok(curscr, 1);
476	return (refresh() == ERR);
477}
478
479/*
480 * cl_rename --
481 *	Rename the file.
482 *
483 * PUBLIC: int cl_rename(SCR *, char *, int);
484 */
485int
486cl_rename(SCR *sp, char *name, int on)
487{
488	GS *gp;
489	CL_PRIVATE *clp;
490	char *ttype;
491
492	gp = sp->gp;
493	clp = CLP(sp);
494
495	ttype = OG_STR(gp, GO_TERM);
496
497	/*
498	 * XXX
499	 * We can only rename windows for xterm.
500	 */
501	if (on) {
502		if (F_ISSET(clp, CL_RENAME_OK) &&
503		    !strncmp(ttype, "xterm", sizeof("xterm") - 1)) {
504			F_SET(clp, CL_RENAME);
505			(void)printf(XTERM_RENAME, name);
506			(void)fflush(stdout);
507		}
508	} else
509		if (F_ISSET(clp, CL_RENAME)) {
510			F_CLR(clp, CL_RENAME);
511			(void)printf(XTERM_RENAME, ttype);
512			(void)fflush(stdout);
513		}
514	return (0);
515}
516
517/*
518 * cl_suspend --
519 *	Suspend a screen.
520 *
521 * PUBLIC: int cl_suspend(SCR *, int *);
522 */
523int
524cl_suspend(SCR *sp, int *allowedp)
525{
526	struct termios t;
527	CL_PRIVATE *clp;
528	size_t oldy, oldx;
529	int changed;
530
531	clp = CLP(sp);
532	*allowedp = 1;
533
534	/*
535	 * The ex implementation of this function isn't needed by screens not
536	 * supporting ex commands that require full terminal canonical mode
537	 * (e.g. :suspend).
538	 *
539	 * The vi implementation of this function isn't needed by screens not
540	 * supporting vi process suspension, i.e. any screen that isn't backed
541	 * by a UNIX shell.
542	 *
543	 * Setting allowedp to 0 will cause the editor to reject the command.
544	 */
545	if (F_ISSET(sp, SC_EX)) {
546		/* Save the terminal settings, and restore the original ones. */
547		if (F_ISSET(clp, CL_STDIN_TTY)) {
548			(void)tcgetattr(STDIN_FILENO, &t);
549			(void)tcsetattr(STDIN_FILENO,
550			    TCSASOFT | TCSADRAIN, &clp->orig);
551		}
552
553		/* Stop the process group. */
554		(void)kill(0, SIGTSTP);
555
556		/* Time passes ... */
557
558		/* Restore terminal settings. */
559		if (F_ISSET(clp, CL_STDIN_TTY))
560			(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
561		return (0);
562	}
563
564	/*
565	 * Move to the lower left-hand corner of the screen.
566	 *
567	 * XXX
568	 * Not sure this is necessary in System V implementations, but it
569	 * shouldn't hurt.
570	 */
571	getyx(stdscr, oldy, oldx);
572	(void)move(LINES - 1, 0);
573	(void)refresh();
574
575	/*
576	 * Temporarily end the screen.  System V introduced a semantic where
577	 * endwin() could be restarted.  We use it because restarting curses
578	 * from scratch often fails in System V.
579	 */
580
581	/* Restore the cursor keys to normal mode. */
582	(void)keypad(stdscr, FALSE);
583
584	/* Restore the window name. */
585	(void)cl_rename(sp, NULL, 0);
586
587	(void)endwin();
588
589	/*
590	 * XXX
591	 * Restore the original terminal settings.  This is bad -- the
592	 * reset can cause character loss from the tty queue.  However,
593	 * we can't call endwin() in BSD curses implementations, and too
594	 * many System V curses implementations don't get it right.
595	 */
596	(void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
597
598	/* Stop the process group. */
599	(void)kill(0, SIGTSTP);
600
601	/* Time passes ... */
602
603	/*
604	 * If we received a killer signal, we're done.  Leave everything
605	 * unchanged.  In addition, the terminal has already been reset
606	 * correctly, so leave it alone.
607	 */
608	if (clp->killersig) {
609		F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
610		return (0);
611	}
612
613	/* Set the window name. */
614	(void)cl_rename(sp, sp->frp->name, 1);
615
616	/* Put the cursor keys into application mode. */
617	(void)keypad(stdscr, TRUE);
618
619	/* Refresh and repaint the screen. */
620	(void)move(oldy, oldx);
621	(void)cl_refresh(sp, 1);
622
623	/* If the screen changed size, set the SIGWINCH bit. */
624	if (cl_ssize(sp, 1, NULL, NULL, &changed))
625		return (1);
626	if (changed)
627		F_SET(CLP(sp), CL_SIGWINCH);
628
629	return (0);
630}
631
632/*
633 * cl_usage --
634 *	Print out the curses usage messages.
635 *
636 * PUBLIC: void cl_usage(void);
637 */
638void
639cl_usage()
640{
641	switch (pmode) {
642	case MODE_EX:
643		(void)fprintf(stderr, "usage: "
644		    "ex [-FRrSsv] [-c cmd] [-t tag] [-w size] [file ...]\n");
645		break;
646	case MODE_VI:
647		(void)fprintf(stderr, "usage: "
648		    "vi [-eFRrS] [-c cmd] [-t tag] [-w size] [file ...]\n");
649		break;
650	case MODE_VIEW:
651		(void)fprintf(stderr, "usage: "
652		    "view [-eFrS] [-c cmd] [-t tag] [-w size] [file ...]\n");
653		break;
654	}
655}
656
657#ifdef DEBUG
658/*
659 * gdbrefresh --
660 *	Stub routine so can flush out curses screen changes using gdb.
661 */
662int
663gdbrefresh()
664{
665	refresh();
666	return (0);		/* XXX Convince gdb to run it. */
667}
668#endif
669