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