1/*	$NetBSD: vs_split.c,v 1.6 2013/12/01 02:34:54 christos Exp $ */
2/*-
3 * Copyright (c) 1993, 1994
4 *	The Regents of the University of California.  All rights reserved.
5 * Copyright (c) 1993, 1994, 1995, 1996
6 *	Keith Bostic.  All rights reserved.
7 *
8 * See the LICENSE file for redistribution information.
9 */
10
11#include "config.h"
12
13#include <sys/cdefs.h>
14#if 0
15#ifndef lint
16static const char sccsid[] = "Id: vs_split.c,v 10.42 2001/06/25 15:19:38 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:38 ";
17#endif /* not lint */
18#else
19__RCSID("$NetBSD$");
20#endif
21
22#include <sys/types.h>
23#include <sys/queue.h>
24#include <sys/time.h>
25
26#include <bitstring.h>
27#include <errno.h>
28#include <limits.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "../common/common.h"
34#include "vi.h"
35
36typedef enum { HORIZ_FOLLOW, HORIZ_PRECEDE, VERT_FOLLOW, VERT_PRECEDE } jdir_t;
37
38static SCR	*vs_getbg __P((SCR *, const char *));
39static void      vs_insert __P((SCR *sp, WIN *wp));
40static int	 vs_join __P((SCR *, SCR **, jdir_t *));
41
42/*
43 * vs_split --
44 *	Create a new screen, horizontally.
45 *
46 * PUBLIC: int vs_split __P((SCR *, SCR *, int));
47 */
48int
49vs_split(SCR *sp, SCR *new, int ccl)
50
51	        		/* Colon-command line split. */
52{
53	GS *gp;
54	SMAP *smp;
55	size_t half;
56	int issmallscreen, splitup;
57
58	gp = sp->gp;
59
60	/* Check to see if it's possible. */
61	/* XXX: The IS_ONELINE fix will change this, too. */
62	if (sp->rows < 4) {
63		msgq(sp, M_ERR,
64		    "222|Screen must be larger than %d lines to split", 4 - 1);
65		return (1);
66	}
67
68	/* Wait for any messages in the screen. */
69	vs_resolve(sp, NULL, 1);
70
71	/* Get a new screen map. */
72	CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
73	if (_HMAP(new) == NULL)
74		return (1);
75	_HMAP(new)->lno = sp->lno;
76	_HMAP(new)->coff = 0;
77	_HMAP(new)->soff = 1;
78
79	/* Split the screen in half. */
80	half = sp->rows / 2;
81	if (ccl && half > 6)
82		half = 6;
83
84	/*
85	 * Small screens: see vs_refresh.c section 6a.  Set a flag so
86	 * we know to fix the screen up later.
87	 */
88	issmallscreen = IS_SMALL(sp);
89
90	/* The columns in the screen don't change. */
91	new->coff = sp->coff;
92	new->cols = sp->cols;
93
94	/*
95	 * Split the screen, and link the screens together.  If creating a
96	 * screen to edit the colon command line or the cursor is in the top
97	 * half of the current screen, the new screen goes under the current
98	 * screen.  Else, it goes above the current screen.
99	 *
100	 * Recalculate current cursor position based on sp->lno, we're called
101	 * with the cursor on the colon command line.  Then split the screen
102	 * in half and update the shared information.
103	 */
104	splitup =
105	    !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (size_t)(smp - HMAP) + 1) >= half;
106	if (splitup) {				/* Old is bottom half. */
107		new->rows = sp->rows - half;	/* New. */
108		new->roff = sp->roff;
109		sp->rows = half;		/* Old. */
110		sp->roff += new->rows;
111
112		/*
113		 * If the parent is the bottom half of the screen, shift
114		 * the map down to match on-screen text.
115		 */
116		memcpy(_HMAP(sp), _HMAP(sp) + new->rows,
117		    (sp->t_maxrows - new->rows) * sizeof(SMAP));
118	} else {				/* Old is top half. */
119		new->rows = half;		/* New. */
120		sp->rows -= half;		/* Old. */
121		new->roff = sp->roff + sp->rows;
122	}
123
124	/* Adjust maximum text count. */
125	sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
126	new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
127
128	/*
129	 * Small screens: see vs_refresh.c, section 6a.
130	 *
131	 * The child may have different screen options sizes than the parent,
132	 * so use them.  Guarantee that text counts aren't larger than the
133	 * new screen sizes.
134	 */
135	if (issmallscreen) {
136		/* Fix the text line count for the parent. */
137		if (splitup)
138			sp->t_rows -= new->rows;
139
140		/* Fix the parent screen. */
141		if (sp->t_rows > sp->t_maxrows)
142			sp->t_rows = sp->t_maxrows;
143		if (sp->t_minrows > sp->t_maxrows)
144			sp->t_minrows = sp->t_maxrows;
145
146		/* Fix the child screen. */
147		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
148		if (new->t_rows > new->t_maxrows)
149			new->t_rows = new->t_maxrows;
150		if (new->t_minrows > new->t_maxrows)
151			new->t_minrows = new->t_maxrows;
152	} else {
153		sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
154
155		/*
156		 * The new screen may be a small screen, even if the parent
157		 * was not.  Don't complain if O_WINDOW is too large, we're
158		 * splitting the screen so the screen is much smaller than
159		 * normal.
160		 */
161		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
162		if (new->t_rows > new->rows - 1)
163			new->t_minrows = new->t_rows =
164			    IS_ONELINE(new) ? 1 : new->rows - 1;
165	}
166
167	/* Adjust the ends of the new and old maps. */
168	_TMAP(sp) = IS_ONELINE(sp) ?
169	    _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
170	_TMAP(new) = IS_ONELINE(new) ?
171	    _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
172
173	/* Reset the length of the default scroll. */
174	if ((sp->defscroll = sp->t_maxrows / 2) == 0)
175		sp->defscroll = 1;
176	if ((new->defscroll = new->t_maxrows / 2) == 0)
177		new->defscroll = 1;
178
179	/* Fit the screen into the logical chain. */
180	vs_insert(new, sp->wp);
181
182	/* Tell the display that we're splitting. */
183	(void)gp->scr_split(sp, new);
184
185	/*
186	 * Initialize the screen flags:
187	 *
188	 * If we're in vi mode in one screen, we don't have to reinitialize.
189	 * This isn't just a cosmetic fix.  The path goes like this:
190	 *
191	 *	return into vi(), SC_SSWITCH set
192	 *	call vs_refresh() with SC_STATUS set
193	 *	call vs_resolve to display the status message
194	 *	call vs_refresh() because the SC_SCR_VI bit isn't set
195	 *
196	 * Things go downhill at this point.
197	 *
198	 * Draw the new screen from scratch, and add a status line.
199	 */
200	F_SET(new,
201	    SC_SCR_REFORMAT | SC_STATUS |
202	    F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
203	return (0);
204}
205
206/*
207 * vs_vsplit --
208 *	Create a new screen, vertically.
209 *
210 * PUBLIC: int vs_vsplit __P((SCR *, SCR *));
211 */
212int
213vs_vsplit(SCR *sp, SCR *new)
214{
215	GS *gp;
216	size_t cols;
217
218	gp = sp->gp;
219
220	/* Check to see if it's possible. */
221	if (sp->cols / 2 <= MINIMUM_SCREEN_COLS) {
222		msgq(sp, M_ERR,
223		    "288|Screen must be larger than %d columns to split",
224		    MINIMUM_SCREEN_COLS * 2);
225		return (1);
226	}
227
228	/* Wait for any messages in the screen. */
229	vs_resolve(sp, NULL, 1);
230
231	/* Get a new screen map. */
232	CALLOC(sp, _HMAP(new), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
233	if (_HMAP(new) == NULL)
234		return (1);
235	_HMAP(new)->lno = sp->lno;
236	_HMAP(new)->coff = 0;
237	_HMAP(new)->soff = 1;
238
239	/*
240	 * Split the screen in half; we have to sacrifice a column to delimit
241	 * the screens.
242	 *
243	 * XXX
244	 * We always split to the right... that makes more sense to me, and
245	 * I don't want to play the stupid games that I play when splitting
246	 * horizontally.
247	 *
248	 * XXX
249	 * We reserve a column for the screen, "knowing" that curses needs
250	 * one.  This should be worked out with the display interface.
251	 */
252	cols = sp->cols / 2;
253	new->cols = sp->cols - cols - 1;
254	sp->cols = cols;
255	new->coff = sp->coff + cols + 1;
256	sp->cno = 0;
257
258	/* Nothing else changes. */
259	new->rows = sp->rows;
260	new->t_rows = sp->t_rows;
261	new->t_maxrows = sp->t_maxrows;
262	new->t_minrows = sp->t_minrows;
263	new->roff = sp->roff;
264	new->defscroll = sp->defscroll;
265	_TMAP(new) = _HMAP(new) + (new->t_rows - 1);
266
267	/* Fit the screen into the logical chain. */
268	vs_insert(new, sp->wp);
269
270	/* Tell the display that we're splitting. */
271	(void)gp->scr_split(sp, new);
272
273	/* Redraw the old screen from scratch. */
274	F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
275
276	/*
277	 * Initialize the screen flags:
278	 *
279	 * If we're in vi mode in one screen, we don't have to reinitialize.
280	 * This isn't just a cosmetic fix.  The path goes like this:
281	 *
282	 *	return into vi(), SC_SSWITCH set
283	 *	call vs_refresh() with SC_STATUS set
284	 *	call vs_resolve to display the status message
285	 *	call vs_refresh() because the SC_SCR_VI bit isn't set
286	 *
287	 * Things go downhill at this point.
288	 *
289	 * Draw the new screen from scratch, and add a status line.
290	 */
291	F_SET(new,
292	    SC_SCR_REFORMAT | SC_STATUS |
293	    F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
294	return (0);
295}
296
297/*
298 * vs_insert --
299 *	Insert the new screen into the correct place in the logical
300 *	chain.
301 */
302static void
303vs_insert(SCR *sp, WIN *wp)
304{
305	SCR *tsp;
306
307	sp->wp = wp;
308
309	/* Move past all screens with lower row numbers. */
310	TAILQ_FOREACH(tsp, &wp->scrq, q)
311		if (tsp->roff >= sp->roff)
312			break;
313	/*
314	 * Move past all screens with the same row number and lower
315	 * column numbers.
316	 */
317	for (; tsp != NULL; tsp = TAILQ_NEXT(tsp, q))
318		if (tsp->roff != sp->roff || tsp->coff > sp->coff)
319			break;
320
321	/*
322	 * If we reached the end, this screen goes there.  Otherwise,
323	 * put it before or after the screen where we stopped.
324	 */
325	if (tsp == NULL) {
326		TAILQ_INSERT_TAIL(&wp->scrq, sp, q);
327	} else if (tsp->roff < sp->roff ||
328	    (tsp->roff == sp->roff && tsp->coff < sp->coff)) {
329		TAILQ_INSERT_AFTER(&wp->scrq, tsp, sp, q);
330	} else
331		TAILQ_INSERT_BEFORE(tsp, sp, q);
332}
333
334/*
335 * vs_discard --
336 *	Discard the screen, folding the real-estate into a related screen,
337 *	if one exists, and return that screen.
338 *
339 * PUBLIC: int vs_discard __P((SCR *, SCR **));
340 */
341int
342vs_discard(SCR *sp, SCR **spp)
343{
344	GS *gp;
345	SCR *tsp, **lp, *list[100];
346	jdir_t jdir;
347
348	gp = sp->gp;
349
350	/*
351	 * Save the old screen's cursor information.
352	 *
353	 * XXX
354	 * If called after file_end(), and the underlying file was a tmp
355	 * file, it may have gone away.
356	 */
357	if (sp->frp != NULL) {
358		sp->frp->lno = sp->lno;
359		sp->frp->cno = sp->cno;
360		F_SET(sp->frp, FR_CURSORSET);
361	}
362
363	/* If no other screens to join, we're done. */
364	if (!IS_SPLIT(sp)) {
365		(void)gp->scr_discard(sp, NULL);
366
367		if (spp != NULL)
368			*spp = NULL;
369		return (0);
370	}
371
372	/*
373	 * Find a set of screens that cover one of the screen's borders.
374	 * Check the vertical axis first, for no particular reason.
375	 *
376	 * XXX
377	 * It's possible (I think?), to create a screen that shares no full
378	 * border with any other set of screens, so we can't discard it.  We
379	 * just complain at the user until they clean it up.
380	 */
381	if (vs_join(sp, list, &jdir))
382		return (1);
383
384	/*
385	 * Modify the affected screens.  Redraw the modified screen(s) from
386	 * scratch, setting a status line.  If this is ever a performance
387	 * problem we could play games with the map, but I wrote that code
388	 * before and it was never clean or easy.
389	 *
390	 * Don't clean up the discarded screen's information.  If the screen
391	 * isn't exiting, we'll do the work when the user redisplays it.
392	 */
393	switch (jdir) {
394	case HORIZ_FOLLOW:
395	case HORIZ_PRECEDE:
396		for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
397			/*
398			 * Small screens: see vs_refresh.c section 6a.  Adjust
399			 * text line info, unless it's a small screen.
400			 *
401			 * Reset the length of the default scroll.
402			 *
403			 * Reset the map references.
404			 */
405			tsp->rows += sp->rows;
406			if (!IS_SMALL(tsp))
407				tsp->t_rows = tsp->t_minrows = tsp->rows - 1;
408			tsp->t_maxrows = tsp->rows - 1;
409
410			tsp->defscroll = tsp->t_maxrows / 2;
411
412			*(_HMAP(tsp) + (tsp->t_rows - 1)) = *_TMAP(tsp);
413			_TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
414
415			switch (jdir) {
416			case HORIZ_FOLLOW:
417				tsp->roff = sp->roff;
418				vs_sm_fill(tsp, OOBLNO, P_TOP);
419				break;
420			case HORIZ_PRECEDE:
421				vs_sm_fill(tsp, OOBLNO, P_BOTTOM);
422				break;
423			default:
424				abort();
425			}
426			F_SET(tsp, SC_STATUS);
427		}
428		break;
429	case VERT_FOLLOW:
430	case VERT_PRECEDE:
431		for (lp = &list[0]; (tsp = *lp) != NULL; ++lp) {
432			if (jdir == VERT_FOLLOW)
433				tsp->coff = sp->coff;
434			tsp->cols += sp->cols + 1;	/* XXX: DIVIDER */
435			vs_sm_fill(tsp, OOBLNO, P_TOP);
436			F_SET(tsp, SC_STATUS);
437		}
438		break;
439	default:
440		abort();
441	}
442
443	/* Find the closest screen that changed and move to it. */
444	tsp = list[0];
445	if (spp != NULL)
446		*spp = tsp;
447
448	/* Tell the display that we're discarding a screen. */
449	(void)gp->scr_discard(sp, list);
450
451	return (0);
452}
453
454/*
455 * vs_join --
456 *	Find a set of screens that covers a screen's border.
457 */
458static int
459vs_join(SCR *sp, SCR **listp, jdir_t *jdirp)
460{
461	WIN *wp;
462	SCR **lp, *tsp;
463	int first;
464	size_t tlen;
465
466	wp = sp->wp;
467
468	/* Check preceding vertical. */
469	lp = listp;
470	tlen = sp->rows;
471	TAILQ_FOREACH(tsp, &wp->scrq, q) {
472		if (sp == tsp)
473			continue;
474		/* Test if precedes the screen vertically. */
475		if (tsp->coff + tsp->cols + 1 != sp->coff)
476			continue;
477		/*
478		 * Test if a subset on the vertical axis.  If overlaps the
479		 * beginning or end, we can't join on this axis at all.
480		 */
481		if (tsp->roff > sp->roff + sp->rows)
482			continue;
483		if (tsp->roff < sp->roff) {
484			if (tsp->roff + tsp->rows >= sp->roff)
485				break;
486			continue;
487		}
488		if (tsp->roff + tsp->rows > sp->roff + sp->rows)
489			break;
490#ifdef DEBUG
491		if (tlen < tsp->rows)
492			abort();
493#endif
494		tlen -= tsp->rows;
495		*lp++ = tsp;
496	}
497	if (tlen == 0) {
498		*lp = NULL;
499		*jdirp = VERT_PRECEDE;
500		return (0);
501	}
502
503	/* Check following vertical. */
504	lp = listp;
505	tlen = sp->rows;
506	TAILQ_FOREACH(tsp, &wp->scrq, q) {
507		if (sp == tsp)
508			continue;
509		/* Test if follows the screen vertically. */
510		if (tsp->coff != sp->coff + sp->cols + 1)
511			continue;
512		/*
513		 * Test if a subset on the vertical axis.  If overlaps the
514		 * beginning or end, we can't join on this axis at all.
515		 */
516		if (tsp->roff > sp->roff + sp->rows)
517			continue;
518		if (tsp->roff < sp->roff) {
519			if (tsp->roff + tsp->rows >= sp->roff)
520				break;
521			continue;
522		}
523		if (tsp->roff + tsp->rows > sp->roff + sp->rows)
524			break;
525#ifdef DEBUG
526		if (tlen < tsp->rows)
527			abort();
528#endif
529		tlen -= tsp->rows;
530		*lp++ = tsp;
531	}
532	if (tlen == 0) {
533		*lp = NULL;
534		*jdirp = VERT_FOLLOW;
535		return (0);
536	}
537
538	/* Check preceding horizontal. */
539	first = 0;
540	lp = listp;
541	tlen = sp->cols;
542	TAILQ_FOREACH(tsp, &wp->scrq, q) {
543		if (sp == tsp)
544			continue;
545		/* Test if precedes the screen horizontally. */
546		if (tsp->roff + tsp->rows != sp->roff)
547			continue;
548		/*
549		 * Test if a subset on the horizontal axis.  If overlaps the
550		 * beginning or end, we can't join on this axis at all.
551		 */
552		if (tsp->coff > sp->coff + sp->cols)
553			continue;
554		if (tsp->coff < sp->coff) {
555			if (tsp->coff + tsp->cols >= sp->coff)
556				break;
557			continue;
558		}
559		if (tsp->coff + tsp->cols > sp->coff + sp->cols)
560			break;
561#ifdef DEBUG
562		if (tlen < tsp->cols)
563			abort();
564#endif
565		tlen -= tsp->cols + first;
566		first = 1;
567		*lp++ = tsp;
568	}
569	if (tlen == 0) {
570		*lp = NULL;
571		*jdirp = HORIZ_PRECEDE;
572		return (0);
573	}
574
575	/* Check following horizontal. */
576	first = 0;
577	lp = listp;
578	tlen = sp->cols;
579	TAILQ_FOREACH(tsp, &wp->scrq, q) {
580		if (sp == tsp)
581			continue;
582		/* Test if precedes the screen horizontally. */
583		if (tsp->roff != sp->roff + sp->rows)
584			continue;
585		/*
586		 * Test if a subset on the horizontal axis.  If overlaps the
587		 * beginning or end, we can't join on this axis at all.
588		 */
589		if (tsp->coff > sp->coff + sp->cols)
590			continue;
591		if (tsp->coff < sp->coff) {
592			if (tsp->coff + tsp->cols >= sp->coff)
593				break;
594			continue;
595		}
596		if (tsp->coff + tsp->cols > sp->coff + sp->cols)
597			break;
598#ifdef DEBUG
599		if (tlen < tsp->cols)
600			abort();
601#endif
602		tlen -= tsp->cols + first;
603		first = 1;
604		*lp++ = tsp;
605	}
606	if (tlen == 0) {
607		*lp = NULL;
608		*jdirp = HORIZ_FOLLOW;
609		return (0);
610	}
611	return (1);
612}
613
614/*
615 * vs_fg --
616 *	Background the current screen, and foreground a new one.
617 *
618 * PUBLIC: int vs_fg __P((SCR *, SCR **, CHAR_T *, int));
619 */
620int
621vs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen)
622{
623	GS *gp;
624	WIN *wp;
625	SCR *nsp;
626	const char *np;
627	size_t nlen;
628
629	gp = sp->gp;
630	wp = sp->wp;
631
632	if (name)
633	    INT2CHAR(sp, name, STRLEN(name) + 1, np, nlen);
634	else
635	    np = NULL;
636	if (newscreen)
637		/* Get the specified background screen. */
638		nsp = vs_getbg(sp, np);
639	else
640		/* Swap screens. */
641		if (vs_swap(sp, &nsp, np))
642			return (1);
643
644	if ((*nspp = nsp) == NULL) {
645		msgq_wstr(sp, M_ERR, name,
646		    name == NULL ?
647		    "223|There are no background screens" :
648		    "224|There's no background screen editing a file named %s");
649		return (1);
650	}
651
652	if (newscreen) {
653		/* Remove the new screen from the background queue. */
654		TAILQ_REMOVE(&gp->hq, nsp, q);
655
656		/* Split the screen; if we fail, hook the screen back in. */
657		if (vs_split(sp, nsp, 0)) {
658			TAILQ_INSERT_TAIL(&gp->hq, nsp, q);
659			return (1);
660		}
661	} else {
662		/* Move the old screen to the background queue. */
663		TAILQ_REMOVE(&wp->scrq, sp, q);
664		TAILQ_INSERT_TAIL(&gp->hq, sp, q);
665	}
666	return (0);
667}
668
669/*
670 * vs_bg --
671 *	Background the screen, and switch to the next one.
672 *
673 * PUBLIC: int vs_bg __P((SCR *));
674 */
675int
676vs_bg(SCR *sp)
677{
678	GS *gp;
679	WIN *wp;
680	SCR *nsp;
681
682	gp = sp->gp;
683	wp = sp->wp;
684
685	/* Try and join with another screen. */
686	if (vs_discard(sp, &nsp))
687		return (1);
688	if (nsp == NULL) {
689		msgq(sp, M_ERR,
690		    "225|You may not background your only displayed screen");
691		return (1);
692	}
693
694	/* Move the old screen to the background queue. */
695	TAILQ_REMOVE(&wp->scrq, sp, q);
696	TAILQ_INSERT_TAIL(&gp->hq, sp, q);
697
698	/* Toss the screen map. */
699	free(_HMAP(sp));
700	_HMAP(sp) = NULL;
701
702	/* Switch screens. */
703	sp->nextdisp = nsp;
704	F_SET(sp, SC_SSWITCH);
705
706	return (0);
707}
708
709/*
710 * vs_swap --
711 *	Swap the current screen with a backgrounded one.
712 *
713 * PUBLIC: int vs_swap __P((SCR *, SCR **, const char *));
714 */
715int
716vs_swap(SCR *sp, SCR **nspp, const char *name)
717{
718	GS *gp;
719	WIN *wp;
720	SCR *nsp, *list[2];
721
722	gp = sp->gp;
723	wp = sp->wp;
724
725	/* Get the specified background screen. */
726	if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
727		return (0);
728
729	/*
730	 * Save the old screen's cursor information.
731	 *
732	 * XXX
733	 * If called after file_end(), and the underlying file was a tmp
734	 * file, it may have gone away.
735	 */
736	if (sp->frp != NULL) {
737		sp->frp->lno = sp->lno;
738		sp->frp->cno = sp->cno;
739		F_SET(sp->frp, FR_CURSORSET);
740	}
741
742	/* Switch screens. */
743	sp->nextdisp = nsp;
744	F_SET(sp, SC_SSWITCH);
745
746	/* Initialize terminal information. */
747	VIP(nsp)->srows = VIP(sp)->srows;
748
749	/* Initialize screen information. */
750	nsp->cols = sp->cols;
751	nsp->rows = sp->rows;	/* XXX: Only place in vi that sets rows. */
752	nsp->roff = sp->roff;
753
754	/*
755	 * Small screens: see vs_refresh.c, section 6a.
756	 *
757	 * The new screens may have different screen options sizes than the
758	 * old one, so use them.  Make sure that text counts aren't larger
759	 * than the new screen sizes.
760	 */
761	if (IS_SMALL(nsp)) {
762		nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
763		if (nsp->t_rows > sp->t_maxrows)
764			nsp->t_rows = nsp->t_maxrows;
765		if (nsp->t_minrows > sp->t_maxrows)
766			nsp->t_minrows = nsp->t_maxrows;
767	} else
768		nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
769
770	/* Reset the length of the default scroll. */
771	nsp->defscroll = nsp->t_maxrows / 2;
772
773	/* Allocate a new screen map. */
774	CALLOC_RET(nsp, _HMAP(nsp), SMAP *, SIZE_HMAP(nsp), sizeof(SMAP));
775	_TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
776
777	/* Fill the map. */
778	nsp->wp = sp->wp;
779	if (vs_sm_fill(nsp, nsp->lno, P_FILL))
780		return (1);
781
782	/*
783	 * The new screen replaces the old screen in the parent/child list.
784	 * We insert the new screen after the old one.  If we're exiting,
785	 * the exit will delete the old one, if we're foregrounding, the fg
786	 * code will move the old one to the background queue.
787	 */
788	TAILQ_REMOVE(&gp->hq, nsp, q);
789	TAILQ_INSERT_AFTER(&wp->scrq, sp, nsp, q);
790
791	/*
792	 * Don't change the screen's cursor information other than to
793	 * note that the cursor is wrong.
794	 */
795	F_SET(VIP(nsp), VIP_CUR_INVALID);
796
797	/* Draw the new screen from scratch, and add a status line. */
798	F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
799
800	list[0] = nsp; list[1] = NULL;
801	(void)gp->scr_discard(sp, list);
802
803	return (0);
804}
805
806/*
807 * vs_resize --
808 *	Change the absolute size of the current screen.
809 *
810 * PUBLIC: int vs_resize __P((SCR *, long, adj_t));
811 */
812int
813vs_resize(SCR *sp, long int count, adj_t adj)
814{
815	GS *gp;
816	SCR *g, *s, *prev, *next, *list[3] = {NULL, NULL, NULL};
817	size_t g_off, s_off;
818
819	gp = sp->gp;
820
821	/*
822	 * Figure out which screens will grow, which will shrink, and
823	 * make sure it's possible.
824	 */
825	if (count == 0)
826		return (0);
827	if (adj == A_SET) {
828		if (sp->t_maxrows == (size_t)count)
829			return (0);
830		if (sp->t_maxrows > (size_t)count) {
831			adj = A_DECREASE;
832			count = sp->t_maxrows - count;
833		} else {
834			adj = A_INCREASE;
835			count = count - sp->t_maxrows;
836		}
837	}
838
839	/* Find first overlapping screen */
840	for (next = TAILQ_NEXT(sp, q);
841	     next != NULL &&
842	     (next->coff >= sp->coff + sp->cols ||
843	      next->coff + next->cols <= sp->coff);
844	     next = TAILQ_NEXT(next, q))
845		continue;
846	/* See if we can use it */
847	if (next != NULL &&
848	    (sp->coff != next->coff || sp->cols != next->cols))
849		next = NULL;
850	for (prev = TAILQ_PREV(sp, _scrh, q);
851	     prev != NULL &&
852	     (prev->coff >= sp->coff + sp->cols ||
853	      prev->coff + prev->cols <= sp->coff);
854	     prev = TAILQ_PREV(prev, _scrh, q))
855		continue;
856	if (prev != NULL &&
857	    (sp->coff != prev->coff || sp->cols != prev->cols))
858		prev = NULL;
859
860	g_off = s_off = 0;
861	if (adj == A_DECREASE) {
862		if (count < 0)
863			count = -count;
864		s = sp;
865		if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count)
866			goto toosmall;
867		if ((g = prev) == NULL) {
868			if ((g = next) == NULL)
869				goto toobig;
870			g_off = -count;
871		} else
872			s_off = count;
873	} else {
874		g = sp;
875		if ((s = next) != NULL &&
876		    s->t_maxrows >= MINIMUM_SCREEN_ROWS + (size_t)count)
877				s_off = count;
878		else
879			s = NULL;
880		if (s == NULL) {
881			if ((s = prev) == NULL) {
882toobig:				msgq(sp, M_BERR, adj == A_DECREASE ?
883				    "227|The screen cannot shrink" :
884				    "228|The screen cannot grow");
885				return (1);
886			}
887			if (s->t_maxrows < MINIMUM_SCREEN_ROWS + (size_t)count) {
888toosmall:			msgq(sp, M_BERR,
889				    "226|The screen can only shrink to %d rows",
890				    MINIMUM_SCREEN_ROWS);
891				return (1);
892			}
893			g_off = -count;
894		}
895	}
896
897	/*
898	 * Fix up the screens; we could optimize the reformatting of the
899	 * screen, but this isn't likely to be a common enough operation
900	 * to make it worthwhile.
901	 */
902	s->rows += -count;
903	s->roff += s_off;
904	g->rows += count;
905	g->roff += g_off;
906
907	g->t_rows += count;
908	if (g->t_minrows == g->t_maxrows)
909		g->t_minrows += count;
910	g->t_maxrows += count;
911	_TMAP(g) += count;
912	F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
913
914	s->t_rows -= count;
915	s->t_maxrows -= count;
916	if (s->t_minrows > s->t_maxrows)
917		s->t_minrows = s->t_maxrows;
918	_TMAP(s) -= count;
919	F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
920
921	/* XXXX */
922	list[0] = g; list[1] = s;
923	gp->scr_discard(0, list);
924
925	return (0);
926}
927
928/*
929 * vs_getbg --
930 *	Get the specified background screen, or, if name is NULL, the first
931 *	background screen.
932 */
933static SCR *
934vs_getbg(SCR *sp, const char *name)
935{
936	GS *gp;
937	SCR *nsp;
938	char *p;
939
940	gp = sp->gp;
941
942	/* If name is NULL, return the first background screen on the list. */
943	if (name == NULL) {
944		return TAILQ_FIRST(&gp->hq);
945	}
946
947	/* Search for a full match. */
948	TAILQ_FOREACH(nsp, &gp->hq, q)
949		if (!strcmp(nsp->frp->name, name))
950			break;
951	if (nsp != NULL)
952		return (nsp);
953
954	/* Search for a last-component match. */
955	TAILQ_FOREACH(nsp, &gp->hq, q) {
956		if ((p = strrchr(nsp->frp->name, '/')) == NULL)
957			p = nsp->frp->name;
958		else
959			++p;
960		if (!strcmp(p, name))
961			break;
962	}
963	return nsp;
964}
965