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