xref: /illumos-gate/usr/src/cmd/vi/port/ex_v.c (revision 942c5e3c2dd127463517e5cc1694ee94ca45e021)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1981 Regents of the University of California */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "ex.h"
36 #include "ex_re.h"
37 #include "ex_tty.h"
38 #include "ex_vis.h"
39 
40 /*
41  * Entry points to open and visual from command mode processor.
42  * The open/visual code breaks down roughly as follows:
43  *
44  * ex_v.c	entry points, checking of terminal characteristics
45  *
46  * ex_vadj.c	logical screen control, use of intelligent operations
47  *		insert/delete line and coordination with screen image;
48  *		updating of screen after changes.
49  *
50  * ex_vget.c	input of single keys and reading of input lines
51  *		from the echo area, handling of \ escapes on input for
52  *		uppercase only terminals, handling of memory for repeated
53  *		commands and small saved texts from inserts and partline
54  *		deletes, notification of multi line changes in the echo
55  *		area.
56  *
57  * ex_vmain.c	main command decoding, some command processing.
58  *
59  * ex_voperate.c   decoding of operator/operand sequences and
60  *		contextual scans, implementation of word motions.
61  *
62  * ex_vops.c	major operator interfaces, undos, motions, deletes,
63  *		changes, opening new lines, shifts, replacements and yanks
64  *		coordinating logical and physical changes.
65  *
66  * ex_vops2.c	subroutines for operator interfaces in ex_vops.c,
67  *		insert mode, read input line processing at lowest level.
68  *
69  * ex_vops3.c	structured motion definitions of ( ) { } and [ ] operators,
70  *		indent for lisp routines, () and {} balancing.
71  *
72  * ex_vput.c	output routines, clearing, physical mapping of logical cursor
73  *		positioning, cursor motions, handling of insert character
74  *		and delete character functions of intelligent and unintelligent
75  *		terminals, visual mode tracing routines (for debugging),
76  *		control of screen image and its updating.
77  *
78  * ex_vwind.c	window level control of display, forward and backward rolls,
79  *		absolute motions, contextual displays, line depth determination
80  */
81 
82 void setsize();
83 void winch();
84 void vintr();
85 void ovend(ttymode);
86 
87 wchar_t	atube[TUBESIZE];
88 jmp_buf	venv;
89 int windowchg;
90 int sigok;
91 
92 /* reinitialize window size after SIGWINCH */
93 void windowinit()
94 {
95 	windowchg = 0;
96 	setsize();
97 	if(value(vi_WINDOW) >= lines || options[vi_WINDOW].odefault == value(vi_WINDOW))
98 		value(vi_WINDOW) = lines -1;
99 	options[vi_WINDOW].odefault = lines - 1;
100 	if(options[vi_SCROLL].odefault == value(vi_SCROLL))
101 		value(vi_SCROLL) = value(vi_WINDOW)/2;
102 	options[vi_SCROLL].odefault = (lines - 1)/2;
103 	vsetsiz(value(vi_WINDOW));
104 	setwind();
105 	vok(atube, 1);
106 }
107 
108 void redraw()
109 {
110 	vsave();
111 	windowinit();
112 	vclear();
113 	vdirty(0, lines);
114 	if(state != VISUAL) {
115 		vclean();
116 		vmoveto(dot, cursor, 0);
117 	} else {
118 		vredraw(WTOP);
119 		vrepaint(cursor);
120 		vfixcurs();
121 	}
122 }
123 
124 /*ARGSUSED*/
125 void
126 #ifdef __STDC__
127 winch(int sig)
128 #else
129 winch(sig)
130 int sig;
131 #endif
132 {
133 	struct winsize jwin;
134 	int l;
135 
136 	if(ioctl(0, TIOCGWINSZ, &jwin) != -1) {
137 #ifdef XPG4
138 		oldlines = jwin.ws_row;
139 		oldcolumns = jwin.ws_col;
140 #endif /* XPG4 */
141 		if (sigok) {
142 			if (columns != jwin.ws_col || lines != jwin.ws_row)
143 			    redraw();
144 		}
145 	}
146 	else
147 		windowchg++;
148 	(void)signal(SIGWINCH, winch);
149 }
150 
151 void
152 setsize()
153 {
154 	struct winsize jwin;
155 	int l;
156 
157 	if(ioctl(0, TIOCGWINSZ, &jwin) != -1) {
158 		if (jwin.ws_col > 0)
159 			columns = jwin.ws_col;
160 		if (jwin.ws_row > 0)
161 			lines = jwin.ws_row;
162 	}
163 
164 #ifdef XPG4
165 	if (envlines != -1) {
166 		lines = envlines;
167 	}
168 
169 	if (envcolumns != -1) {
170 		columns = envcolumns;
171 	}
172 
173 	if (envlines != -1 || envcolumns != -1) {
174 		jwin.ws_row = lines;
175 		jwin.ws_col = columns;
176 
177 		if (ioctl(0, TIOCSWINSZ, &jwin) == -1) {
178 			jwin.ws_row = oldlines;
179 			jwin.ws_col = oldcolumns;
180 
181 			ioctl(0, TIOCSWINSZ, &jwin);
182 		}
183 	}
184 #endif /* XPG4 */
185 
186 	if (lines <= 1)
187 		lines = 24;
188 	l = lines;
189 	if (columns <= 4)
190 		columns = 1000;
191 	value(vi_WINDOW) = options[vi_WINDOW].odefault = l - 1;
192 }
193 
194 /*
195  * Enter open mode
196  */
197 void
198 oop(void)
199 {
200 	unsigned char *ic;
201 	ttymode f;	/* was register */
202 	int resize;
203 
204 	windowchg = 0;
205 	(void)signal(SIGWINCH, winch);
206 	ovbeg();
207 	if (peekchar() == '/') {
208 		(void)vi_compile(getchar(), 1);
209 		savere(&scanre);
210 		if (execute(0, dot) == 0)
211 			error(value(vi_TERSE) ? gettext("Fail") :
212 gettext("Pattern not found on addressed line"));
213 		ic = (unsigned char *)loc1;
214 		if (ic > linebuf && *ic == 0)
215 			ic--;
216 	} else {
217 		getDOT();
218 		ic = vskipwh(linebuf);
219 	}
220 	donewline();
221 
222 	/*
223 	 * If overstrike then have to HARDOPEN
224 	 * else if can move cursor up off current line can use CRTOPEN (~~vi1)
225 	 * otherwise have to use ONEOPEN (like adm3)
226 	 */
227 	if (over_strike && !erase_overstrike)
228 		bastate = HARDOPEN;
229 	else if (cursor_address || cursor_up)
230 		bastate = CRTOPEN;
231 	else
232 		bastate = ONEOPEN;
233 	setwind();
234 
235 	/*
236 	 * To avoid bombing on glass-crt's when the line is too long
237 	 * pretend that such terminals are 160 columns wide.
238 	 * If a line is too wide for display, we will dynamically
239 	 * switch to hardcopy open mode.
240 	 */
241 	if (state != CRTOPEN)
242 		WCOLS = TUBECOLS;
243 	if (!inglobal)
244 		savevis();
245 	vok(atube, 0);
246 	if (state != CRTOPEN)
247 		columns = WCOLS;
248 	Outchar = vputchar;
249 	f = ostart();
250 	if (state == CRTOPEN) {
251 		if (outcol == UKCOL)
252 			outcol = 0;
253 		vmoveitup(1, 1);
254 	} else
255 		outline = destline = WBOT;
256 	vshow(dot, NOLINE);
257 	vnline(ic);
258 	vmain();
259 	if (state != CRTOPEN)
260 		vclean();
261 	Command = (unsigned char *)"open";
262 	ovend(f);
263 	(void)signal(SIGWINCH, SIG_DFL);
264 }
265 
266 void
267 ovbeg(void)
268 {
269 
270 	if (inopen)
271 		error(gettext("Recursive open/visual not allowed"));
272 	Vlines = lineDOL();
273 	fixzero();
274 	setdot();
275 	pastwh();
276 	dot = addr2;
277 }
278 
279 void
280 ovend(ttymode f)
281 {
282 
283 	splitw++;
284 	vgoto(WECHO, 0);
285 	vclreol();
286 	vgoto(WECHO, 0);
287 	holdcm = 0;
288 	splitw = 0;
289 	ostop(f);
290 	setoutt();
291 	undvis();
292 	columns = OCOLUMNS;
293 	inopen = 0;
294 	flusho();
295 	netchHAD(Vlines);
296 }
297 
298 /*
299  * Enter visual mode
300  */
301 void
302 vop(void)
303 {
304 	int c;
305 	ttymode f;	/* was register */
306 	extern unsigned char termtype[];
307 
308 	if (!cursor_address && !cursor_up) {
309 		if (initev) {
310 toopen:
311 			if (generic_type)
312 				merror(gettext("I don't know what kind of terminal you are on - all I have is '%s'."), termtype);
313 			putNFL();
314 			merror(gettext("[Using open mode]"));
315 			putNFL();
316 			oop();
317 			return;
318 		}
319 		error(gettext("Visual needs addressable cursor or upline capability"));
320 	}
321 	if (over_strike && !erase_overstrike) {
322 		if (initev)
323 			goto toopen;
324 		error(gettext("Can't use visual on a terminal which overstrikes"));
325 	}
326 	if (!clear_screen) {
327 		if (initev)
328 			goto toopen;
329 		error(gettext("Visual requires clear screen capability"));
330 	}
331 	if (!scroll_forward) {
332 		if (initev)
333 			goto toopen;
334 		error(gettext("Visual requires scrolling"));
335 	}
336 	windowchg = 0;
337 	(void)signal(SIGWINCH, winch);
338 	ovbeg();
339 	bastate = VISUAL;
340 	c = 0;
341 	if (any(peekchar(), "+-^."))
342 		c = getchar();
343 	pastwh();
344 	vsetsiz(isdigit(peekchar()) ? getnum() : value(vi_WINDOW));
345 	setwind();
346 	donewline();
347 	vok(atube, 0);
348 	if (!inglobal)
349 		savevis();
350 	Outchar = vputchar;
351 	vmoving = 0;
352 	f = ostart();
353 	if (initev == 0) {
354 		vcontext(dot, c);
355 		vnline((unsigned char *)NOSTR);
356 	}
357 	vmain();
358 	Command = (unsigned char *)"visual";
359 	ovend(f);
360 	(void)signal(SIGWINCH, SIG_DFL);
361 }
362 
363 /*
364  * Hack to allow entry to visual with
365  * empty buffer since routines internally
366  * demand at least one line.
367  */
368 void
369 fixzero(void)
370 {
371 
372 	if (dol == zero) {
373 		bool ochng = chng;
374 
375 		vdoappend((unsigned char *)"");
376 		if (!ochng)
377 			sync();
378 		addr1 = addr2 = one;
379 	} else if (addr2 == zero)
380 		addr2 = one;
381 }
382 
383 /*
384  * Save lines before visual between unddol and truedol.
385  * Accomplish this by throwing away current [unddol,truedol]
386  * and then saving all the lines in the buffer and moving
387  * unddol back to dol.  Don't do this if in a global.
388  *
389  * If you do
390  *	g/xxx/vi.
391  * and then do a
392  *	:e xxxx
393  * at some point, and then quit from the visual and undo
394  * you get the old file back.  Somewhat weird.
395  */
396 void
397 savevis(void)
398 {
399 
400 	if (inglobal)
401 		return;
402 	truedol = unddol;
403 	saveall();
404 	unddol = dol;
405 	undkind = UNDNONE;
406 }
407 
408 /*
409  * Restore a sensible state after a visual/open, moving the saved
410  * stuff back to [unddol,dol], and killing the partial line kill indicators.
411  */
412 void
413 undvis(void)
414 {
415 
416 	if (ruptible)
417 		signal(SIGINT, onintr);
418 	squish();
419 	pkill[0] = pkill[1] = 0;
420 	unddol = truedol;
421 	unddel = zero;
422 	undap1 = one;
423 	undap2 = dol + 1;
424 	undkind = UNDALL;
425 	if (undadot <= zero || undadot > dol)
426 		undadot = zero+1;
427 }
428 
429 /*
430  * Set the window parameters based on the base state bastate
431  * and the available buffer space.
432  */
433 void
434 setwind(void)
435 {
436 
437 	WCOLS = columns;
438 	switch (bastate) {
439 
440 	case ONEOPEN:
441 		if (auto_right_margin)
442 			WCOLS--;
443 		/* fall into ... */
444 
445 	case HARDOPEN:
446 		basWTOP = WTOP = WBOT = WECHO = 0;
447 		ZERO = 0;
448 		holdcm++;
449 		break;
450 
451 	case CRTOPEN:
452 		basWTOP = lines - 2;
453 		/* fall into */
454 
455 	case VISUAL:
456 		ZERO = lines - TUBESIZE / WCOLS;
457 		if (ZERO < 0)
458 			ZERO = 0;
459 		if (ZERO > basWTOP)
460 			error(gettext("Screen too large for internal buffer"));
461 		WTOP = basWTOP; WBOT = lines - 2; WECHO = lines - 1;
462 		break;
463 	}
464 	state = bastate;
465 	basWLINES = WLINES = WBOT - WTOP + 1;
466 }
467 
468 /*
469  * Can we hack an open/visual on this terminal?
470  * If so, then divide the screen buffer up into lines,
471  * and initialize a bunch of state variables before we start.
472  */
473 static unsigned char vlinebuf[LBSIZE];
474 
475 void
476 vok(wchar_t *atube, int undo)
477 {
478 	int i;
479 	static int beenhere;
480 
481 	if (WCOLS == 1000)
482 		serror((unsigned char *)
483 		    gettext("Don't know enough about your terminal to use %s"),
484 		    Command);
485 	if (WCOLS > TUBECOLS)
486 		error(gettext("Terminal too wide"));
487 	if (WLINES >= TUBELINES || WCOLS * (WECHO - ZERO + 1) > TUBESIZE)
488 		error(gettext("Screen too large"));
489 
490 	vtube0 = atube;
491 	if(beenhere)
492 		vclrbyte(atube, WCOLS * (WECHO - ZERO + 1));
493 	for (i = 0; i < ZERO; i++)
494 		vtube[i] = (wchar_t *) 0;
495 	for (; i <= WECHO; i++)
496 		vtube[i] = atube, atube += WCOLS;
497 	if(beenhere++) {
498 		for (; i < TUBELINES; i++)
499 			vtube[i] = (wchar_t *) 0;
500 	}
501 	vutmp = vlinebuf;
502 	if(!undo) {
503 		vundkind = VNONE;
504 		vUNDdot = 0;
505 	}
506 	OCOLUMNS = columns;
507 	inopen = 1;
508 #ifdef CBREAK
509 	signal(SIGINT, vintr);
510 #endif
511 	vmoving = 0;
512 	splitw = 0;
513 	doomed = 0;
514 	holdupd = 0;
515 	if(!undo)
516 		Peekkey = 0;
517 	vcnt = vcline = 0;
518 	if (vSCROLL == 0)
519 		vSCROLL = value(vi_SCROLL);
520 }
521 
522 #ifdef CBREAK
523 /*ARGSUSED*/
524 void
525 #ifdef __STDC__
526 vintr(int sig)
527 #else
528 vintr(sig)
529 int sig;
530 #endif
531 {
532 
533 	signal(SIGINT, vintr);
534 	if (vcatch)
535 		onintr(0);
536 	ungetkey(ATTN);
537 	draino();
538 }
539 #endif
540 
541 /*
542  * Set the size of the screen to size lines, to take effect the
543  * next time the screen is redrawn.
544  */
545 void
546 vsetsiz(int size)
547 {
548 	int b;
549 
550 	if (bastate != VISUAL)
551 		return;
552 	b = lines - 1 - size;
553 	if (b >= lines - 1)
554 		b = lines - 2;
555 	if (b < 0)
556 		b = 0;
557 	basWTOP = b;
558 	basWLINES = WBOT - b + 1;
559 }
560