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